Information : Transparency : Democracy


OpenStreetMaps

Prof. Dr. Nik Klever

Professor for New Media and Internet-Technology

Dean of Students - Faculty of Computer Science

Contents

  • Introduction
  • Geographic Maps
  • Coordinate Systems
  • Map Tiles
  • Satellite Navigation System
  • Satellite Data Formats
  • Google Maps
  • OpenStreetMap
  • Google Maps API
  • JavaScript Map Libraries
  • Map Services
  • Appendix: GeoLocation Webserver

Introduction

In seafaring since the middle ages and in aviation since the First World War Maps are obviously the basis to reach a target.

If you know where you are, you can 
be where you want -
but if you do not know where you are,
you have to see where you will stay!

Maps are the basis of our modern - digital oriented - life

Information : Transparency : Democracy and OpenStreetMaps

What's the relationship between the overall topic

Information : Transparency : Democracy

of this SummerSchool to OpenStreetMaps ?

  • Maps in general are Information
  • OpenStreetMap has a transparent and democratic concept to develop and produce maps

Geographic Maps

Topographic Maps

A topographic map is a detailed and accurate graphic representation of cultural and natural features on the ground.

Cartography

Cartography is the study and making of maps. Fundamental Tasks are

  • Set the map's agenda and select traits of the object to be mapped. This is the concern of map editing. Traits may be physical, such as roads or land masses, or may be abstract, such as toponyms or political boundaries.
  • Represent the terrain of the mapped object on flat media. This is the concern of map projections.
  • Eliminate characteristics of the mapped object that are not relevant to the map's purpose. This is the concern of generalization.
  • Reduce the complexity of the characteristics that will be mapped. This is also the concern of generalization.
  • Orchestrate the elements of the map to best convey its message to its audience. This is the concern of map design.

Map Producer - National Mapping Agencies

Map producers are historical a task for national or regional authorities.

In Bavaria the State Office for Digitalisation, Broadband and Surveying (Landesamt für Digitalisierung, Breitband und Vermessung) produces the topographical maps of Bavaria.

An Onlineversion is available on the BayernAtlas web page.

The ukrainian authority for producing maps is named Holovne Upravlinnja Heodezii Kartografi ta Kardastruv (HUHKK).

Coordinate Systems

Spherical Coordinate System

Location-dependent data on the earth are based on a geographic coordinate system, which can be approximated by a spherical coordinate system with a latitude $\varphi$ and a longitude $\lambda$:

Geographic_coordinates_sphere.svg

Mercator Projection (Lorke)

1. Step

Dividing the 3D-sphere like an orange peel into a 2D-plane

mercator.jpg

2. Step

Stretching in east-west to fill the gap between the trim lines

mercator2.jpg

3. Step

but the shapes of the circles in east-west differ from north-south

mercator3.jpg

4. Step

Mercator's idea: stretching also in north-south

mercator4.jpg

Result

MercNormSph.png

Geodetic Datum

Geodetic datums are coordinate systems and used in geodesy, navigation, and surveying by cartographers and satellite navigation systems to translate positions indicated on maps (paper or digital) to their real position on Earth. Each starts with an ellipsoid (stretched sphere), and then defines latitude, longitude and altitude coordinates.

World Geodetic System (WGS)

The World Geodetic System is developed by the US Department of Defense and comprises

  • a standard coordinate system for the Earth,
  • a standard spheroidal reference surface (the datum or reference ellipsoid) for raw altitude data, and
  • a gravitational equipotential surface (the geoid) that defines the nominal sea level.

WGS 84 is the basis of the Global Positioning System.

Web Maps

Web Mercator

Web Mercator is a variant of the mercator projection and the basis of all major web-based maps providers:

Web Map Service (WMS)

A Web Map Service (WMS) is a standard protocol for serving georeferenced map images over the Internet that are generated by a map server using data from a GIS database. The specification was developed and first published by the Open Geospatial Consortium in 1999.

WMS Requests

WMS specifies a number of different request types, two of which are required by any WMS server:

  • GetCapabilities - returns parameters about the WMS (such as map image format and WMS version compatibility) and the available layers (map bounding box, coordinate reference systems, URI of the data and whether the layer is mostly opaque or not)
  • GetMap - returns a map image. Parameters include: width and height of the map, coordinate reference system, rendering style, image format

A WMS server usually serves the map in a bitmap format, e.g. PNG, GIF or JPEG. In addition, vector graphics can be included: such as points, lines, curves and text, expressed in SVG or WebCGM format.

Map Tiles

  • square bitmap graphics displayed in a grid arrangement to show a map
  • Map tiles are typically 256×256 pixel images. e.g.: http://b.tile.openstreetmap.org/9/271/177.png
  • Tiles are not always in these dimensions; for example there could be 64×64 pixel images for mobile use, however 256×256 pixel images are a de facto standard.

QuadTiles

QuadTiles are a geo-data storage/indexing strategy (hierarchical binning). The idea is to store a geo-database such that data for a specific location can be retrieved quickly, by dividing the data up by location, partitioning the world into tiles.

Level 1

Level1.jpg

Level 2

Level2.jpg

Level 3

Level3.jpg

Slippy Map Tilenames

The file naming conventions for the Slippy Map are

  • Tiles are 256 × 256 pixel PNG files
  • Each zoom level is a directory, each column is a subdirectory, and each tile in that column is a file
  • Filename(url) format is /zoom/x/y.png

The slippy map expects tiles to be served up at URLs following this scheme, so all tile server URLs look pretty similar:

OSM Standard Style:

http://[abc].tile.openstreetmap.org/zoom/x/y.png

Satellite Navigation System

A satellite navigation system is a system of satellites that provide autonomous geo-spatial positioning with global coverage. It allows receivers to determine their location (longitude, latitude, and altitude) and calculate time synchronisation using time signals transmitted by radio. A satellite navigation system with global coverage may be termed a global navigation satellite system (GNSS).

  • US American Global Positioning System
  • European Galileo System
  • Russian GLONASS

Global Positioning System

  • 24 Satellites circle around the earth during 12 hours in an orbit of 20.200 km
  • from every survey point at every time of day are at least 4-6 satellites visible, minimal 3 are necessary to locate a position
  • all satellites broadcasts ot the same two frequencies in the L-Band (1.57542 GHz (L1 signal) and 1.2276 GHz (L2 signal))
  • with a power of 50 W the following data are repeatedly transmitted:
    • Orbitaldata,
    • a periodically repeated sequence of numbers
    • the exact time based on a atomic clock in the satellite

Positioning

GPS_Spheres.svg

Process of Positioning

  • by determining the differencing runtime delay between the continuously repetitive satellite signal sequence and the ongoing local reference sequence in the receiver the runtime of the signal is determined
  • with the propagation velocity of light c = 300m / ms and the determined runtime t of the signal the distance R (R = c * t) to the satellite can be calculated
  • with the distances to at least 3 satellites the current position can be determined on the ground using the intersection of three straight lines in space
  • at least one additional satellite is required to synchronize the time in the GPS receiver to get correct runtime calculations

Pros:

  • determination of the current position (geographic coordinates), direction and speed
  • determination of the direction and distance (lead) to a previously stored waypoint (WP)
  • determination of the estimated time of arrival (ETA) at a waypoint
  • recording the distance traveled (tracks) and store waypoints (eg sections of the route table) and grouping of waypoints to routes
  • compass function, altimeter, and calculation of sunrise and sunset
  • nowadays embedded and included into every mobile phone

Cons:

  • nowadays essentially only useable outdoors use, as trees, houses, power lines, etc. can interfere with the UHF signal
  • permanent use requires relatively large energy needs, replacement batteries are always useful to carry with

Satellite Data Formats

NMEA - National Marine Electronics Association

NMEA 0183 or the newer NMEA 2000 standard is a combined electrical and data specification for communication between marine electronics devices and uses a simple ASCII, serial communications protocol that defines how data are transmitted in a "sentence" like the HTTP protocol. NMEA standards are a proprietary protocol not officially published on the internet, a good description is available on Dale dePriest's NMEA web page.

GPX - GPS Exchange Format

GPX - the GPS Exchange Format is a light-weight XML data format for the interchange of GPS data (waypoints, routes, and tracks) between applications and Web services on the Internet. GPX is developed and maintained by Topografix as an open standard.

GPX Version 1.1 is published as official XML Schema. An overview is also given on the german wikipedia GPX web page.

KML - Keyhole Markup Language

KML - Keyhole Markup Language is an XML notation for expressing geographic annotation and visualization. It was originally developed by Keyhole, Inc.. Keyhole, Inc. was acquired by Google in 2004. KML became an international standard of the Open Geospatial Consortium in 2008.

Current KML Version 2.2 is published there as XML schema.

The Google developers web page provides a KML Tutorial.

Google Maps

Google Maps is a desktop and mobile web mapping service application and technology provided by Google, offering satellite imagery, street maps, and Street View perspectives, as well as functions such as a route planner for traveling by foot, car, bicycle (beta test), or with public transportation.

Google Maps for mobile is the world's most popular app for smartphones, with over 54% of global smartphone owners using it at least once during the month of August 2013 (see Business Insider)

OpenStreetMap

OpenStreetMao (OSM) is a collaborative project to create a free editable map of the world. Two major driving forces behind the establishment and growth of OSM have been restrictions on use or availability of map information across much of the world and the advent of inexpensive portable satellite navigation devices.

Google Maps API v3

  1. declare the application as HTML5 using the <!DOCTYPE html> declaration.
  2. include the Maps API JavaScript using a script tag.
  3. create a div element named e.g. "map-canvas" to hold the Map.
  4. create a JavaScript object literal to hold a number of map properties.
  5. create a JavaScript "map" object, passing it the div element and the map properties.
  6. use the event listener "onload" to load the map after the page has loaded.

Google Maps API Basic Example with Google Maps

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <style type="text/css">
      html { height: 100% }
      body { height: 100%; margin: 0; padding: 0 }
      #map_canvas { height: 100% }
    </style>
    <script type="text/javascript"
      src="http://maps.googleapis.com/maps/api/js?sensor=False">
    </script>
    <script type="text/javascript">
      var myLatlng = new google.maps.LatLng(48.35807, 10.90652);
      function initialize() {
        var mapOptions = {
          center: myLatlng,
          zoom: 12,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        var map = new google.maps.Map(document.getElementById("map_canvas"),
            mapOptions);
      }
    </script>
  <body onload="initialize()">
    <div id="map_canvas" style="width:100%; height:100%"></div>
  </body>
</html>

Google Maps with Google Maps API v3

Google Maps API Basic Example with OpenStreetMap

Adding a new ImageMapType with an own mapTypeId allows using Google Maps API with OpenStreetMap

        var mapOptions = {
          center: myLatlng,
          zoom: 12,
          mapTypeId: "OSM"
        };
        var map = new google.maps.Map(document.getElementById("map_canvas"),
            mapOptions);
        //Define OSM map type pointing at the OpenStreetMap tile server
        map.mapTypes.set("OSM", new google.maps.ImageMapType({
            getTileUrl: function(coord, zoom) {
                return "http://tile.openstreetmap.org/" + zoom + "/" + coord.x + "/" + coord.y + ".png";
            },
            tileSize: new google.maps.Size(256, 256),
            name: "OpenStreetMap",
            maxZoom: 18
        }));

OpenStreetMap with Google Maps API v3

Adding Markers to the Map

var marker = new google.maps.Marker({
    position: myLatlng,
    title:"University of Applied Sciences Augsburg",
    map: map
});

Task

Take your home address coordinates (and/or another address) to add one or more additional markers

Adding KML Support using Layers

var kmlLayer = new google.maps.KmlLayer('http://klever.hs-augsburg.de/static/Placemark.kml');
kmlLayer.setMap(map);

JavaScript Map Libraries

OpenStreetMap has no API by its own to use the map but there are several JavaScript Map libraries which can be used for. The best known are Leaflet.js and OpenLayers. Leaflet.js has a more compact and simple API, whereas OpenLayers has a more feature-rich API.

Leaflet.js

Leaflet Quick Start Guide

<div id="map" style="width: 1920px; height: 1080px"></div>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<script>

    var map = L.map('map').setView([48.35807, 10.90652], 15);

    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        maxZoom: 18,
        attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' +
            '<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ',
        id: 'examples.map-i875mjb7'
    }).addTo(map);

    L.marker([48.35807, 10.90652]).addTo(map)
        .bindPopup("<b>University of Applied Sciences Augsburg</b><br />Faculty of Computer Sciences").openPopup();

    L.circle([48.36590, 10.90515], 200, {
        color: 'red',
        fillColor: '#f03',
        fillOpacity: 0.5
    }).addTo(map).bindPopup("I am a circle round the City Galerie.");

</script>

Add Polygon and Popup on Click

    L.polygon([
        [48.34845, 10.91211],
        [48.34953, 10.91573],
        [48.34775, 10.91638],
        [48.34759, 10.91556],
        [48.34654, 10.91586],
        [48.34698, 10.91889],
        [48.34382, 10.91782],
        [48.34647, 10.91291]
    ]).addTo(map).bindPopup("I am a polygon around the Zoo.");

    var popup = L.popup();

    function onMapClick(e) {
        popup
            .setLatLng(e.latlng)
            .setContent("You clicked the map at " + e.latlng.toString())
            .openOn(map);
    }

    map.on('click', onMapClick);

Leaflet Tutorials

  • Leaflet Quick Start Guide
    • A simple step-by-step guide that will quickly get you started with Leaflet basics, including setting up a Leaflet map (with Mapbox tiles) on your page, working with markers, polylines and popups, and dealing with events.
  • Leaflet on Mobile
    • In this tutorial, you’ll learn how to create a fullscreen map tuned for mobile devices like iPhone, iPad or Android phones, and how to easily detect and use the current user location.
  • Markers with Custom Icons
    • In this pretty tutorial, you’ll learn how to easily define your own icons for use by the markers you put on the map.
  • Using GeoJSON with Leaflet
    • In this tutorial, you’ll learn how to create and interact with map vectors created from GeoJSON objects.
  • Interactive Choropleth Map
    • A case study of creating a colorful interactive cloropleth map of US States Population Density with GeoJSON and some custom controls. News websites will love this.
  • Layer Groups and Layers Control
    • A tutorial on how to manage groups of layers and use the layer switching control.

OpenLayers

<script src="OpenLayers.js"></script>
<script>
  function init() {
    map = new OpenLayers.Map("basicMap");
    var mapnik         = new OpenLayers.Layer.OSM();
    var fromProjection = new OpenLayers.Projection("EPSG:4326");   // Transform from WGS 1984
    var toProjection   = new OpenLayers.Projection("EPSG:900913"); // to Spherical Mercator Projection
    var position       = new OpenLayers.LonLat(10.90652,48.35807).transform( fromProjection, toProjection);
    var zoom           = 15; 

    map.addLayer(mapnik);
    map.setCenter(position, zoom );
  }
</script>

OpenLayers Marker Example

    var markers = new OpenLayers.Layer.Markers( "Markers" );
    map.addLayer(markers);
    markers.addMarker(new OpenLayers.Marker(position));

OpenLayer 3

<script type="text/javascript">
  var map = new ol.Map({
    target: 'map',
    layers: [
      new ol.layer.Tile({
        source: new ol.source.MapQuest({layer: 'sat'})
      })
    ],
    view: new ol.View({
      center: ol.proj.transform([10.90652,48.35807], 'EPSG:4326', 'EPSG:3857'),
      zoom: 4
    })
  });
</script>

Map Services

Tile Map Server

Pre-rendering Tiles for Offline Viewing (Schatz)

  1. Setting up the geospatial database
  2. Importing OpenStreetmap data
  3. Rendering tiles
    • Configuring renderd
    • Configuring mapnik
  4. Viewing pre-rendered tiles

Natural Earth Data

Natural Earth is a public domain map dataset available at 1:10m, 1:50m, and 1:110 million scales. Featuring tightly integrated vector and raster data, with Natural Earth you can make a variety of visually pleasing, well-crafted maps with cartography or GIS software.

  • Natural Earth Vector comes in ESRI shapefile format, the de facto standard for vector geodata. Character encoding is Windows-1252.
  • Natural Earth Raster comes in TIFF format with a TFW world file.
  • All Natural Earth data use the Geographic coordinate system (projection),
    • WGS84 datum +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs

OSM Data

Elements (also data primitives) are the basic components of OpenStreetMap's conceptual data model of the physical world. They consist of

  • nodes (defining points in space),
  • ways (defining linear features and area boundaries), and
  • relations (which are sometimes used to explain how other elements work together).

All of the above can have one of more associated tags (which describe the meaning of a particular element).

There is a OSM XML Format defined by a OSM XML Schema. For editing purposes a OSM API is available.

You can download partial OSM Data from geofabrik.de.

Editing OpenStreetMap

  • iD is the easy to use editor found per default on the "Edit" button of the OpenStreetMap homepage, and is ideal for quick editing contributions. It runs in your browser and downloads data automatically as you look around.
  • Potlatch 2 is an intermediate-level editor available under the "Edit" button drop-down ("▼"). Requires Flash.
  • JOSM is a standalone desktop application (cross-platform; requires Java) which allows fluid zooming, panning, anbd editing of a locally stored dataset, before uploading changes in a batch.

More software projects can be found on the OpenStreetMap Github

ID

ID is a JavaScript Online Editor, which is directly available on the OpenStreetMap web page. The source code is available on Github

Mapnik Tool

Mapnik is an open source toolkit for rendering maps. Among other things, it is used to render the five main Slippy Map layers on the OpenStreetMap website. It supports a variety of geospatial data formats and provides flexible styling options for designing many different kinds of maps.

Python Mapnik Wrapper

In [ ]:
#!/usr/bin/env python2

import mapnik
# Create a map
m = mapnik.Map(600,300) # create a map with a given width and height in pixels
# note: m.srs will default to '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
# the 'map.srs' is the target projection of the map and can be whatever you wish 
m.background = mapnik.Color('steelblue') # set background colour to 'steelblue'.  

# Create a style
s = mapnik.Style() # style object to hold rules
r = mapnik.Rule() # rule object to hold symbolizers

# to fill a polygon we create a PolygonSymbolizer
polygon_symbolizer = mapnik.PolygonSymbolizer(mapnik.Color('#f2eff9'))
r.symbols.append(polygon_symbolizer) # add the symbolizer to the rule object

# to add outlines to a polygon we create a LineSymbolizer
line_symbolizer = mapnik.LineSymbolizer(mapnik.Color('rgb(50%,50%,50%)'),0.1)
r.symbols.append(line_symbolizer) # add the symbolizer to the rule object
s.rules.append(r) # now add the rule to the style and we're done

# Add the style to the map
m.append_style('My Style',s) # Styles are given names only as they are applied to the map

# Create a datasource
ds = mapnik.Shapefile(file='ne_110m_admin_0_countries.shp')

# Create a layers
layer = mapnik.Layer('world') # new layer called 'world' (we could name it anything)
# note: layer.srs will default to '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'

layer.datasource = ds # attach the datasource to the layer
layer.styles.append('My Style') # attach the style to the layer

# Prepare the map for rendering
m.layers.append(layer)
m.zoom_all()
# Render the map to the file 
mapnik.render_to_file(m,'world.png', 'png')
print "rendered image to 'world.png'"

mapnik_style.xml

<?xml version="1.0" encoding="utf-8"?>
<Map background-color="#f2efe9" srs="+proj=latlong +datum=WGS84">
    <FontSet name="book-fonts">
        <Font face-name="DejaVu Sans Book" />
    </FontSet>
    <Style name="highways">
        <Rule>
            <Filter>[highway] <> ''</Filter>
            <LineSymbolizer stroke="#808080" stroke-width="2" stroke-linejoin="round" stroke-linecap="round"/> 
        </Rule>
        <Rule>
            <Filter>[highway] <> ''</Filter>
            <TextSymbolizer fontset-name="book-fonts"
                size="9" fill="#000" halo-radius="1" placement="line">[name]</TextSymbolizer>
        </Rule>
    </Style>
    <Layer name="highways" status="on" srs="+proj=latlong +datum=WGS84">
        <StyleName>highways</StyleName>
        <Datasource>
            <Parameter name="type">osm</Parameter>
            <Parameter name="file">schwaben-latest.osm</Parameter>
        </Datasource>
    </Layer>
</Map>
In [ ]:
#!/usr/bin/env python2
# render_osm_directly.py

from mapnik import *

mapfile = 'mapnik_style.xml'
map_output = 'mymap.png'

m = Map(4*1024,4*1024)
load_map(m, mapfile)
bbox=(Envelope( 10.0,47.5,11.1,48.1 ))

m.zoom_to_box(bbox)
print "Scale = " , m.scale()
render_to_file(m, map_output)

Web Map Service (WMS)

A Web Map Service (WMS) is a standard protocol for serving georeferenced map images over the Internet that are generated by a map server using data from a GIS database. The specification was developed and first published by the Open Geospatial Consortium in 1999.

WMS Requests

WMS specifies a number of different request types, two of which are required by any WMS server:

  • GetCapabilities - returns parameters about the WMS (such as map image format and WMS version compatibility) and the available layers (map bounding box, coordinate reference systems, URI of the data and whether the layer is mostly opaque or not)
  • GetMap - returns a map image. Parameters include: width and height of the map, coordinate reference system, rendering style, image format

A WMS server usually serves the map in a bitmap format, e.g. PNG, GIF or JPEG. In addition, vector graphics can be included: such as points, lines, curves and text, expressed in SVG or WebCGM format.

Examples for WMS Server

Appendix: Geolocation WebServer

Flask Webserver for Importing and Viewing CSV, GPX and KML Waypoints

GeoLocation.py

In [ ]:
# -*- coding: utf-8 -*-
"""
@author: [email protected]
"""

from xml.sax import parseString, handler, SAXException, SAXParseException
import math
import io


import math
class Location(object):
    """
    class Location to define a location on the earth surface and to calculate
    the distance of two locations
    """
    
    def __init__(self,kw):
        self.lat = kw['lat']
        self.lon = kw['lon']
        self.alt = kw['alt']

    def getKoord(self):
        return dict(lon=self.lon,lat=self.lat,alt=self.alt)

    def setKoord(self,lon,lat,alt):
        self.lon = lon
        self.lat = lat
        self.alt = alt   

    def distance(self,sndloc,type="sphere"):
        if type == "area": # calculate the distance on a 2D area
            return math.sqrt((self.lon - sndloc.lon)**2+(self.lat - sndloc.lat)**2)*100
        else: # calculate the distance on a 3D sphere
            RadiusEarth = 6371000 # medium radius of the earth in m
            lat1 = math.radians(self.lat)
            lat2 = math.radians(sndloc.lat)
            lon1 = math.radians(self.lon)
            lon2 = math.radians(sndloc.lon)
            return math.acos(math.sin(lat1) * math.sin(lat2) + \
                   math.cos(lat1) * math.cos(lat2) * math.cos(lon2-lon1)) * RadiusEarth


class GPXParser(handler.ContentHandler,Location):
    """
    Reading and parsing of GPX XML data via this SAX Content-Handler class
    """
    
    def __init__(self):
        self.waypoints = []
        self.ele = None
    
    def startElement(self, name, attr):
        if name == "trkpt":
            lat = float(attr.getValue("lat"))
            lon = float(attr.getValue("lon"))
            self.waypoints.append(Location(dict(lat=lat,lon=lon,alt=0)))
        elif name == "ele":
            self.ele = len(self.waypoints)
    
    def characters(self, ch):
        if self.ele:
            self.waypoints[self.ele-1].alt = float(ch)
    
    def endElement(self, name):
        if name == "ele":
            self.ele = None

class KMLParser(handler.ContentHandler,Location):
    """
    Reading and parsing of KML XML data via this SAX Content-Handler class
    """
    
    def __init__(self):
        self.waypoints = []
        self.coordinates = False
    
    def startElement(self, name, attr):
        if name == "coordinates":
            self.coordinates = True
    
    def characters(self, ch):
        if self.coordinates:
            for line in ch.splitlines():
                if line.strip():
                    lon, lat, alt = line.split(',')
                    self.waypoints.append(Location(dict(lat=float(lat),lon=float(lon),alt=float(alt))))
    
    def endElement(self, name):
        if name == "coordinates":
            self.coordinates = False

            
class WayPoints(Location):
    """
    class WayPoints to read the waypoints (either as stream, as list or as a file), 
    to parse them (either as CSV, GPX or KML) and create a Google Static Map or a Google Map Object
    """
    
    def __init__(self,filename_or_stream_or_allLines):
        # Test for the type of the parameter filename_or_stream_or_allLines 
        if isinstance(filename_or_stream_or_allLines, io.BytesIO):
            self.lines = filename_or_stream_or_allLines.read().split('\n')[:-1]
        elif isinstance(filename_or_stream_or_allLines, list):
            self.lines = filename_or_stream_or_allLines
        elif isinstance(filename_or_stream_or_allLines, str):
            f = open(filename_or_stream_or_allLines,'r')
            self.lines = f.read().split('\n')[:-1]
            f.close()
        else:
            raise IOError("wrong type for data input")
    
    def parseCSV(self):
        self.waypoints = []
        columndefinition = self.lines[0].split(';')
        for line in self.lines[1:]:
            columns = line.split(';')
            waypoint = Location(dict((columndefinition[x], float(columns[x])) for x in range(len(columns))))
            self.waypoints.append(waypoint)

    def parseGPX(self):
        gpxhandler = GPXParser()
        parseString('\n'.join(self.lines),gpxhandler)
        self.waypoints = gpxhandler.waypoints        
        
    def parseKML(self):
        kmlhandler = KMLParser()
        parseString('\n'.join(self.lines),kmlhandler)
        self.waypoints = kmlhandler.waypoints

    def writeGoogleStaticMapsUrl(self,radius,lat,lon,alt=0,size='640x640'):
        center = Location(dict(lat=lat,lon=lon,alt=alt))
        radius = radius
        url = "http://maps.google.com/maps/api/staticmap?size={}&maptype=satellite&sensor=false".format(size)
        url += "&markers=color:green|{:.6f},{:.6f}".format(center.lat,center.lon)
        url += "&markers=color:blue"
        for waypoint in self.waypoints:
            if waypoint.distance(center) > radius:
                url += "|{:.6f},{:.6f}".format(waypoint.lat,waypoint.lon)
        url += "&markers=color:red"
        for waypoint in self.waypoints:
            if waypoint.distance(center) < radius:
                url += "|{:.6f},{:.6f}".format(waypoint.lat,waypoint.lon)
        return url

    def createGoogleMapsObject(self,radius,lat,lon,alt=0):
        from flask.ext.googlemaps import Map
        center = Location(dict(lat=lat,lon=lon,alt=alt))
        markers = { 'http://maps.google.com/mapfiles/ms/icons/green-dot.png':[(center.lat,center.lon)],
                    'http://maps.google.com/mapfiles/ms/icons/red-dot.png':[(waypoint.lat,waypoint.lon) for waypoint in self.waypoints if waypoint.distance(center) < radius],
                    'http://maps.google.com/mapfiles/ms/icons/blue-dot.png':[(waypoint.lat,waypoint.lon) for waypoint in self.waypoints if waypoint.distance(center) > radius]}
        googlemap = Map(identifier="view-side",style="width:560px;height:560px;margin:0;",zoom=15,maptype="SATELLITE",lat=lat,lng=lon,markers=markers)
        return googlemap

    def createOSMObject(self,radius,lat,lon,alt=0):
        center = Location(dict(lat=lat,lon=lon,alt=alt))
        return dict(radius=radius,center=center,waypoints=self.waypoints)

if __name__ == "__main__":
    """
    Only for testing purposes
    """
    import sys
    filename= sys.argv[1]
    instance = WayPoints(filename)
    firstLine = instance.lines[0]
    if firstLine.startswith('<?xml'):
        if '<gpx' in firstLine.lower() or (len(instance.lines) > 1 and '<gpx' in instance.lines[1].lower()):
            instance.parseGPX()
        elif '<kml' in firstLine.lower() or (len(instance.lines) > 1 and '<kml' in instance.lines[1].lower()):
            instance.parseKML()
        else:
            raise "Wrong XML Format for Data Input - currently only GPX or KML allowed"
    elif 'lon' in firstLine and 'lat' in firstLine:
        instance.parseCSV()
    else:
        raise "Wrong Format for Data Input - currently only GPX, KML or CSV allowed"

    mlon = float(sys.argv[2])
    mlat = float(sys.argv[3])
    radius = float(sys.argv[4])
    
    if sys.argv[5] == 'googlestaticmap':
        url=instance.writeGoogleStaticMapsUrl(radius,mlat,mlon,size='560x560')
        print url
    elif sys.argv[5] == 'googlemap':
        googlemap=instance.createGoogleMapsObject(radius,mlat,mlon)
        print googlemap
    elif sys.argv[5] == 'osm':
        osm=instance.createOSMObject(radius,mlat,mlon)
        print osm

FlaskServerGeoLocation.py

In [ ]:
# -*- coding: utf-8 -*-
"""
@author: [email protected]
"""

from flask import Flask, render_template, request
import os
import logging
from GeoLocation import WayPoints, GPXParser, KMLParser
from flask.ext.googlemaps import GoogleMaps

# Flask Application GeoLocation
app = Flask("GeoLocationWebserver")
GoogleMaps(app)
app.debug = True
if not app.debug:
    from logging import StreamHandler
    err_handler = StreamHandler()
    app.logger.addHandler(err_handler)

app.logger.setLevel(logging.INFO)

# Rootpage
@app.route('/')
def root():
    return render_template("index.html")

# Call of the application with /geolocation 
@app.route('/geolocation', methods=['GET', 'POST'])
def geolocation():
    if not request.form:
        return render_template("geolocation.html", form=True)

    filestream = request.files['geodatei'].stream

    try:
        app.instance = WayPoints(filestream)
    except IOError:
        return render_template("error.html", message="Wrong Type for Data Input")
    firstLine = app.instance.lines[0]
    if firstLine.startswith('<?xml'):
        if '<gpx' in firstLine.lower() or (len(app.instance.lines) > 1 and '<gpx' in app.instance.lines[1].lower()):
            app.instance.parseGPX()
        elif '<kml' in firstLine.lower() or (len(app.instance.lines) > 1 and '<kml' in app.instance.lines[1].lower()):
            app.instance.parseKML()
        else:
            return render_template("error.html", message="Wrong XML Format for Data Input - currently only GPX or KML allowed")
    elif 'lon' in firstLine and 'lat' in firstLine:
        app.instance.parseCSV()
    else:
        return render_template("error.html", message="Wrong Format for Data Input - currently only GPX or CSV allowed")

    mlon = float(request.form['mlon'])
    mlat = float(request.form['mlat'])
    radius = float(request.form['radius'])

    url = None
    googlemap = None
    osm = None
        
    if request.form['output'] == 'googlestaticmap':
        url=app.instance.writeGoogleStaticMapsUrl(radius,mlat,mlon,size='560x560')
    elif request.form['output'] == 'googlemap':
        googlemap=app.instance.createGoogleMapsObject(radius,mlat,mlon)
    elif request.form['output'] == 'openstreetmap':
        osm=app.instance.createOSMObject(radius,mlat,mlon)
    else:
        return render_template("error.html", message="Wrong Format for Data Output")
    return render_template("geolocation.html", url=url, googlemap=googlemap, osm=osm)

if __name__ == '__main__':
    app.run()