Overlay API

From OpenStreetMap Wiki
Jump to: navigation, search

Overlay API is a programming interface for vectors and pointers on slippy maps. This extension can be implemented as overlay for Map API.

khtml.maplib is one implementation of this API.

Relations to other standards

We use:

Overview

The simple overlay API uses as internal data hierachy the GeoJSON Object structure. Für Styling standard CSS for SVG is used. If Canvas is used as vector backend, the CSS will be translated to Canvas commands.

All things handeld by this API are called "Feature". Many Features can be organized in a "FeatureCollection". FeatureCollections can be added to FeatureCollections allow grouping Features in a tree structur. A FeatureCollection is a Feature.

For appending Features to FeatureCollections DOM Core 2.0 syntax is used. appendChild, removeChild, replaceChild,.. These commands must behave in exactly the same way as normal DOM Core functions do in HTM and SVG.

Initializing

The map object has per default a "featureCollection". That's where you can append new Features

Example

LineString.png

...
var feature=map.featureCollection.appendChild({type:"LineString",coordinates:[[0,0],[51,0],[48.2,16.5]]})
feature.style.stroke="red";
feature.style.strokeWidth=7;

Methods of FeatureCollection

  • appendChild
If a Feature does not have an owner, the feature will be appended to the Feature.
If it already has a parent node, the feature will be remove from the origin position.
appendChild will init the feature and render it to the screen.
  • removeChild:
After removing a feature from a featureCollection, it is not visible anymore on the screen.
It does not delete the feature.
  • replaceChild:
Like removeChild and insert it at the position where the removed feature was.
  • insertBefore:
Similar to appendChild. For details see DOM Core
  • geojson
Serialize to geojson format, LatLng and Bounds are translated back to numeric representation.

Methods of Feature and FeatureCollection

  • render,
Every Feature has a render method. If the render method of a FeatureCollection is called, the
render method of the child nodes is also called.
  • repair,
Recalculate the bbox,

Hierarchy

FeatureCollection may contain Features and FeatureCollections. There is CSS inheritance.

Styling

//style based
feature.style.fill="red";
feature.style.opacity=0.4;
feature.style.fillOpacity=0.8;
feature.style.strokeWidth=4;
feature.style.stroke="black";

//-- or --

//css based
<style type="text/css">
.myCSSRule{
fill:black;
stroke:black;
stroke-opacity:0.5;
opacity:0.7;
}
</stlye>

...
//in the javascript
feature.className.baseVal="myCSSRule";

Styling can be done before or after feature is appended. If you wunder about syntax, "baseVal" is proper SVG styling.

Programming

All Coordinates are replaced by LatLng and Bounds Objects. The coordinates can be changed dynamicly as the CSS Properties.

Creating Features

A feature can be a Point, Polygon, LineString, Multipolygon, MultiLineString, LinearRing and FeatureCollection.

var gemetry1= {"type": "Point", "coordinates": [102.0, 0.5]}

var gemetry2= {
   "type": "LineString",
   "coordinates": [
          [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
   ]
}
var feature1=khtml.maplib.geometry.Feature(geometry1);
feature1.hierkommtdermarkersyntax; //todo
var feature2=khtml.maplib.geometry.Feature(geometry2);
feature2.style.stroke="black";

Addressing

At startup we have the map.featureCollection. To change the latitude of the second feature, the code looks like that. You need to know the geojson syntax - wich is very easy.

map.featureCollection.features[1].geometry.coordinates[0].lat(12.3443);

Examples

Loop of featureCollection

//To make a loop over all features do:
for(var i=0;i<map.featureCollection.features.length;i++){
   var feature=map.featureCollection.features[i];
}

Recurse all featurecollections

//display layer tree structur as html
function recurse(f){
        var div=document.createElement("div");
        div.style.marginLeft="20px";
        div.appendChild(document.createTextNode(f.geometry.type));
        if(f.features){
                for(var i=0;i <f.features.length;i++){
                        div.appendChild(recurse(f.features[i]));
                }
        }
        return div;
}

Polyline

        var polyline=khtml.maplib.geometry.Feature({type:"LineString",coordinates:[[48.2,16.2],[48.2,16.5],[48.3,16.5],[48.2,16.2]]}); //somewhere near vienna
        polyline.className="park"; //css styling
        map.featureCollection.appendChild(polyline);

coordinates as LatLng Object

        var p1=new mr.LatLng(48,1);
        var p3=new mr.LatLng(59.2,20);
        var p2=new mr.LatLng(48.2,10);
        var p4=new mr.LatLng(59,10);
        var p5=new mr.LatLng(48,1);
        var polyline=new khtml.maplib.geometry.Feature({type:"Polygon",coordinates:[p1,p2,p3,p4,p5]});

        polyline.style.stroke="red";
        polyline.style.strokeWidth=6;
        polyline.style.opacity=0.3
        map.featureCollection.appendChild(polyline);

Coordinates as array of LatLng Objects

        //polygon
         
        var line=new Array();
        line.push(new mr.LatLng(48,15));
        line.push(new mr.LatLng(48,16));
        line.push(new mr.LatLng(49,16));
        line.push(new mr.LatLng(49,15));
         
         
        
        style.stroke="black";
        style.fill="red";
      
        var l=mr.geometry.Feature({type:"Polygon",coordinats:line});
        l.style.stroke="black";
        l.style.fill="red";
         
         
        var ov=map.featureCollection.appendChild(l);

Load complete FeatureCollection

function load(url){
	var xmlhttp=new XMLHttpRequest();
	xmlhttp.open("GET",url,true);
	xmlhttp.onreadystatechange=function(){
		if(xmlhttp.readyState==4){
			var json=json_parse(xmlhttp.responseText);
			loadjson(json);
		}
	}
	xmlhttp.send(null);
}
 
function loadjson(json){
	var fc=new khtml.maplib.geometry.Feature(json);
	map.featureCollection.appendChild(fc);
	console.log(map.featureCollection.features[0].features[0].features[1]);
}

load("path/to/featurecollection");

Stringify

var geojson=map.featureCollection.geojson();
var geojsonString=JSON.stringify(geojson);

Moving Overlays

To animate features you can simple change the lat,lng values. Add/Remove Points. Call the render method to display your changes.


Extensions

There are some possible geometry extensions to GeoJSON.

Bézier Curves

Essentials:[1]

There are normal, quadratic and qubic bezier Curves in SVG for example.

//not finished
{"type":"bezier","coordinates":
[
[0,0][0,0][0,0]
]


Styling

/*
map css
*/


way[highway=secondary] { color:gray; width:6;opacity:1;z-index:1}
way[highway=secondary] { color:yellow; stroke-width:4;opacity:1;z-index:2}

way[oneway][highway] {
    color: #6c70d5;
    dashes: 0,2,17,42;
    linejoin: bevel;
    width: 1;
    z-index: 15;
}
way[oneway][highway]{
    color: #6c70d5;
    dashes: 0,12,5,44;
    linejoin: bevel;
    width: 3;
    z-index: 16;
}
way[oneway][highway]{
    color: #6c70d5;
    dashes: 0,12,3,46;
    linejoin: bevel;
    width: 5;
    z-index: 17;
}
way[oneway][highway]{
    color: #6c70d5;
    dashes: 0,12,1,48;
    linejoin: bevel;
    width: 7;
    z-index: 18;
}

Jsoncssexample.png

JSON Equivalent

[
	{
		"selector": {
			"attributes": {
				"highway": "secondary"
			},
			"type": "way",
			"orig": "way[highway=secondary]"
		},
		"rules": {
			"zIndex": "1",
			"stroke": "gray",
			"strokeWidth": "6",
			"opacity": "1"
		}
	},
	{
		"selector": {
			"attributes": {
				"highway": "secondary"
			},
			"type": "way",
			"orig": "way[highway=secondary]"
		},
		"rules": {
			"zIndex": "2",
			"stroke": "yellow",
			"stroke-width": "4",
			"opacity": "1"
		}
	},
	{
		"selector": {
			"attributes": {
				"oneway": "*",
				"highway": "*"
			},
			"type": "way",
			"orig": "way[oneway][highway]"
		},
		"rules": {
			"zIndex": "15",
			"stroke": "#6c70d5",
			"strokeDasharray": "0,2,17,42",
			"linejoin": "bevel",
			"strokeWidth": "1"
		}
	},
	{
		"selector": {
			"attributes": {
				"oneway": "*",
				"highway": "*"
			},
			"type": "way",
			"orig": "way[oneway][highway]"
		},
		"rules": {
			"zIndex": "16",
			"stroke": "#6c70d5",
			"strokeDasharray": "0,12,5,44",
			"linejoin": "bevel",
			"strokeWidth": "3"
		}
	},
	{
		"selector": {
			"attributes": {
				"oneway": "*",
				"highway": "*"
			},
			"type": "way",
			"orig": "way[oneway][highway]"
		},
		"rules": {
			"zIndex": "17",
			"stroke": "#6c70d5",
			"strokeDasharray": "0,12,3,46",
			"linejoin": "bevel",
			"strokeWidth": "5"
		}
	},
	{
		"selector": {
			"attributes": {
				"oneway": "*",
				"highway": "*"
			},
			"type": "way",
			"orig": "way[oneway][highway]"
		},
		"rules": {
			"zIndex": "18",
			"stroke": "#6c70d5",
			"strokeDasharray": "0,12,1,48",
			"linejoin": "bevel",
			"strokeWidth": "7"
		}
	}
]