OpenLayers Dynamic POI

From OpenStreetMap Wiki
Jump to navigation Jump to search
Dynamic Points of Interest

OpenLayers Dynamic POI layer "how to"

Basic approach how to make POIs with Openlayers can be found at Openlayers POI layer example. It is easy to do, but is usable only for relatively static and small POI set.

I have developed a more complex dynamic POI layer. Why dynamic?

  • on demand POI loading, like tiles
  • preprocessed, categorized POIs stored in database

Database schema

  • POI_categories - main POI categories
  • POI_subcategories - POI subcategories, each subcategory belongs to one category
  • POIs - each POI belongs at least to category but usually also to subcategory

POI_categories

 
  CREATE TABLE IF NOT EXISTS `POI_categories` (
  `id` int(11) NOT NULL,
  `name` varchar(32) character set utf8 collate utf8_unicode_ci NOT NULL,
  `icon` varchar(128) character set utf8 collate utf8_unicode_ci NOT NULL,
  `iconw` int(11) default NULL,
  `iconh` int(11) default NULL,
  `iconx` int(11) default NULL,
  `icony` int(11) default NULL,
  `list_icon` varchar(128) collate utf8_unicode_ci NOT NULL default '',
  `zoom_level` int(11) NOT NULL default '14',
  `description` varchar(256) character set utf8 collate utf8_unicode_ci NOT NULL,
  `display_order` int(11) NOT NULL,
  `key1` varchar(128) character set utf8 collate utf8_unicode_ci default NULL,
  `value1` varchar(128) character set utf8 collate utf8_unicode_ci default NULL,
  `key2` varchar(128) character set utf8 collate utf8_unicode_ci default NULL,
  `value2` varchar(128) character set utf8 collate utf8_unicode_ci default NULL,
  `param0` varchar(128) collate utf8_unicode_ci default NULL,
  `param1` varchar(128) collate utf8_unicode_ci default NULL,
  `param2` varchar(128) collate utf8_unicode_ci default NULL,
  `param3` varchar(128) collate utf8_unicode_ci default NULL,
  PRIMARY KEY  (`id`),
  KEY `zoom_level` (`zoom_level`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

POI_subcategories

 CREATE TABLE `POI_subcategories` (
  `id` int(11) NOT NULL,
  `category_id` int(11) NOT NULL,
  `subcategory_map` int(11) default NULL,
  `zoom_level` int(11) default NULL,
  `name` varchar(32) collate utf8_unicode_ci NOT NULL,
  `icon` varchar(128) collate utf8_unicode_ci NOT NULL,
  `iconw` int(11) default NULL,
  `iconh` int(11) default NULL,
  `iconx` int(11) default NULL,
  `icony` int(11) default NULL,
  `list_icon` varchar(128) collate utf8_unicode_ci NOT NULL default '',
  `description` varchar(256) collate utf8_unicode_ci NOT NULL,
  `display_order` int(11) NOT NULL,
  `key1` varchar(128) collate utf8_unicode_ci default NULL,
  `value1` varchar(128) collate utf8_unicode_ci default NULL,
  `key2` varchar(128) collate utf8_unicode_ci default NULL,
  `value2` varchar(128) collate utf8_unicode_ci default NULL,
  `param0` varchar(128) collate utf8_unicode_ci default NULL,
  `param1` varchar(128) collate utf8_unicode_ci default NULL,
  `param2` varchar(128) collate utf8_unicode_ci default NULL,
  `param3` varchar(128) collate utf8_unicode_ci default NULL,
  PRIMARY KEY  (`id`),
  KEY `zoom_level` (`zoom_level`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

POIs

 CREATE TABLE `POI` (
  `osm_id` int(11) NOT NULL,
  `osm_type` tinyint(4) NOT NULL,
  `category_id` int(11) NOT NULL,
  `subcategory_id` int(11) default NULL,
  `lat` float NOT NULL,
  `lon` float NOT NULL,
  `lat2` float default NULL,
  `lon2` float default NULL,
  `name` varchar(128) collate utf8_unicode_ci default NULL,
  `param0` varchar(128) collate utf8_unicode_ci default NULL,
  `param1` varchar(128) collate utf8_unicode_ci default NULL,
  `param2` varchar(128) collate utf8_unicode_ci default NULL,
  `param3` varchar(128) collate utf8_unicode_ci default NULL,
  KEY `osm_type_id` (`osm_id`,`osm_type`),
  KEY `category_id` (`category_id`),
  KEY `subcategory_id` (`subcategory_id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Client-side code

In addition to Openlayers.js also

  • Marker Grid (derived from Openlayers.Layer.Grid, code composed from Openlayers.Layer.TMS, Openlayers.Layer.Marker),
  • Marker Tile (derived from OpenLayers.Tile code composed from Openlayers.Layer.Text, Openlayers.Tile.WFS) and
  • Bounds (small helper code with reverse mercator projection)

Server-side code

Marker definition (as plain text file) for particular tiles are loaded with AJAX. Request URL is formed from zoom level, bbox of particular tile and list of features to fetch. List of features consists of semicolon separated pairs of category_id:subcategory_id.

Request URL: http://sandbox.freemap.sk/dynamic_poi/features/?z=12&l=19.07222250718225&t=48.63287483132393&r=19.160113113145684&b=48.5747561300883&f=2:3;1:1;1:2;2:4;5:17;5:18;3:8;3:9;3:10

Server response:

lat	lon	icon	iconSize	iconOffset	title	description	popupSize
48.6142	19.1495	features/icons/rail_station.png	38,38	0,-38	Sliač	[Železnica - železnice]
200,80 48.5884 19.1252 features/icons/fuel.png 38,38 0,-38 Slovnaft [Čerpacie stanice - čerpacie stanice]
200,80 48.5782 19.103 features/icons/fuel.png 38,38 0,-38 Slovnaft [Čerpacie stanice - čerpacie stanice]
200,80

Source for request handling code is here.

Example

Working minimal example

Warning! If you want to run this example from different site, you have to modify POI requesting code to use some http proxy script because of security limitations in crossdomain XHttpRequests.

POI preprocessing

I wrote my POI preprocessor prototype for Freemap Slovakia in VB6 :P using non-scalable iterative approach. Use of Xapi is recommended for scalable solution.

Another possible source is "POIs from OSM data" done by Christoph Eckert.

--Dido 17:09, 7 November 2007 (UTC)

Alternative get_poi_url javascript function

This get_poi_url function does not use the 'bound.js' function but calculates the boundries with getLatLonFromPixel and the getSize function.

This function gets called for every tile loaded, but requests the markers for the entire screen each time. This means you keep adding lots and lots of the same marker for each point and make lots of requests - most uncool. --H2g2bob 23:28, 9 March 2010 (UTC)
 function get_poi_url() {
    // custom get_poi_url() function that uses the the
    // getLatLonFromPixel function to determine the map
    // boudries and calculates the longitude and latitude
    // using the map projection transform functions of
    // Openlayers.

    // the Zoom Level selected
    var zoom = this.map.getZoom();

    // the top left corner
    var tlLonLat = this.map.getLonLatFromPixel(new OpenLayers.Pixel(1,1)).
          transform(this.map.getProjectionObject(),this.map.displayProjection);

    // the bottom right corner
    var mapsize = this.map.getSize();
    var brLonLat = this.map.getLonLatFromPixel(new OpenLayers.Pixel(mapsize.w - 1, mapsize.h - 1)).
          transform(this.map.getProjectionObject(),this.map.displayProjection);

    return url    + "&zoom=" + zoom
          + "&tllon=" + tlLonLat.lon
          + "&tllat=" + tlLonLat.lat
          + "&brlon=" + brLonLat.lon
          + "&brlat=" + brLonLat.lat
          + "&search="    + zoom + ";"
                + tlLonLat.lon + ";"
                + brLonLat.lat + ";"
                + brLonLat.lon + ";"
                + tlLonLat.lat;
 }

See also