(c) 2023 Hochschule Augsburg - Fakultät für Informatik - Prof.Dr.Nik Klever - Impressum
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
What's the relationship between the overall topic
Information : Transparency : Democracy
of this SummerSchool to OpenStreetMaps ?
A topographic map is a detailed and accurate graphic representation of cultural and natural features on the ground.
Cartography is the study and making of maps. Fundamental Tasks are
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).
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.
The World Geodetic System is developed by the US Department of Defense and comprises
WGS 84 is the basis of the Global Positioning System.
Web Mercator is a variant of the mercator projection and the basis of all major web-based maps providers:
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 specifies a number of different request types, two of which are required by any WMS server:
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.
The file naming conventions for the Slippy Map are
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:
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).
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 - 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 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 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)
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.
<!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>
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
}));
var marker = new google.maps.Marker({
position: myLatlng,
title:"University of Applied Sciences Augsburg",
map: map
});
Take your home address coordinates (and/or another address) to add one or more additional markers
var kmlLayer = new google.maps.KmlLayer('http://klever.hs-augsburg.de/static/Placemark.kml');
kmlLayer.setMap(map);
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.
<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>
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);
Previous Version OpenLayers 2
<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>
var markers = new OpenLayers.Layer.Markers( "Markers" );
map.addLayer(markers);
markers.addMarker(new OpenLayers.Marker(position));
<script type="text/javascript">
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.transform([10.90652,48.35807], 'EPSG:4326', 'EPSG:3857'),
zoom: 4
})
});
</script>
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.
Elements (also data primitives) are the basic components of OpenStreetMap's conceptual data model of the physical world. They consist of
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.
More software projects can be found on the OpenStreetMap Github
ID is a JavaScript Online Editor, which is directly available on the OpenStreetMap web page. The source code is available on Github
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.
#!/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'"
<?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>
#!/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)
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 specifies a number of different request types, two of which are required by any WMS server:
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
# -*- coding: utf-8 -*-
"""
@author: nik.klever@hs-augsburg.de
"""
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
# -*- coding: utf-8 -*-
"""
@author: nik.klever@hs-augsburg.de
"""
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()