diff --git a/docs/openlayers.api.php b/docs/openlayers.api.php index 2bd64a9..097497f 100644 --- a/docs/openlayers.api.php +++ b/docs/openlayers.api.php @@ -266,7 +266,7 @@ function hook_openlayers_maps() { ), 'options' => array( 'displayProjection' => 'EPSG:4326', - 'maxExtent' => openlayers_get_extent('EPSG:4326'), + 'maxExtent' => openlayers_get_extent('EPSG', '4326'), ), 'behaviors' => array( 'openlayers_behavior_panzoombar' => array(), diff --git a/includes/content_types/openlayers_map.inc b/includes/content_types/openlayers_map.inc index 7aff6f0..3b916d2 100644 --- a/includes/content_types/openlayers_map.inc +++ b/includes/content_types/openlayers_map.inc @@ -24,7 +24,7 @@ function openlayers_openlayers_map_content_type_content_types($plugin) { $maps = openlayers_maps(); foreach ($maps as $map) { - $types[$map->name] = array ( + $types[$map->name] = array( 'map' => $map->name, 'title' => $map->title, 'description' => $map->description, diff --git a/includes/openlayers.layers.inc b/includes/openlayers.layers.inc index b468fdc..102f820 100644 --- a/includes/openlayers.layers.inc +++ b/includes/openlayers.layers.inc @@ -19,7 +19,7 @@ function _openlayers_openlayers_layers() { $layer->data = array( 'isBaseLayer' => TRUE, 'type' => 'satellite', - 'projection' => array('EPSG:900913'), + 'projection' => array('EPSG:3857'), 'layer_type' => 'openlayers_layer_type_google', ); $layers[$layer->name] = $layer; @@ -32,7 +32,7 @@ function _openlayers_openlayers_layers() { $layer->data = array( 'isBaseLayer' => TRUE, 'type' => 'hybrid', - 'projection' => array('EPSG:900913'), + 'projection' => array('EPSG:3857'), 'layer_type' => 'openlayers_layer_type_google', ); $layers[$layer->name] = $layer; @@ -45,7 +45,7 @@ function _openlayers_openlayers_layers() { $layer->data = array( 'isBaseLayer' => TRUE, 'type' => 'normal', - 'projection' => array('EPSG:900913'), + 'projection' => array('EPSG:3857'), 'layer_type' => 'openlayers_layer_type_google', ); $layers[$layer->name] = $layer; @@ -58,7 +58,7 @@ function _openlayers_openlayers_layers() { $layer->data = array( 'isBaseLayer' => TRUE, 'type' => 'physical', - 'projection' => array('EPSG:900913'), + 'projection' => array('EPSG:3857'), 'layer_type' => 'openlayers_layer_type_google', ); $layers[$layer->name] = $layer; @@ -70,14 +70,14 @@ function _openlayers_openlayers_layers() { $layer->description = 'MapQuest’s rendering of OpenStreetMap.'; $layer->data = array( 'isBaseLayer' => TRUE, - 'attribution' => t('©CCBYSA '. + 'attribution' => t('©CCBYSA ' . '© OpenStreetMap contributors', array( '@openstreetmap' => 'http://www.openstreetmap.org/copyright', '@ccbysa' => 'http://creativecommons.org/licenses/by-sa/2.0/', ) - ) . '. '. t('Tiles Courtesy of MapQuest.'), - 'projection' => array('EPSG:900913'), + ) . '. ' . t('Tiles Courtesy of MapQuest.'), + 'projection' => array('EPSG:3857', 'EPSG:900913'), 'layer_type' => 'openlayers_layer_type_xyz', 'url' => array( '//otile1' . $mapquest_host . '/tiles/1.0.0/osm/${z}/${x}/${y}.png', @@ -85,7 +85,7 @@ function _openlayers_openlayers_layers() { '//otile3' . $mapquest_host . '/tiles/1.0.0/osm/${z}/${x}/${y}.png', '//otile4' . $mapquest_host . '/tiles/1.0.0/osm/${z}/${x}/${y}.png', ), - 'wrapDateLine' => FALSE, + 'wrapDateLine' => TRUE, 'resolutions' => openlayers_get_resolutions('EPSG:900913', 0, 19) ); $layers[$layer->name] = $layer; @@ -98,8 +98,8 @@ function _openlayers_openlayers_layers() { $layer->data = array( 'isBaseLayer' => TRUE, 'attribution' => t('Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency.') - . ' '. t('Tiles Courtesy of MapQuest.'), - 'projection' => array('EPSG:900913'), + . ' ' . t('Tiles Courtesy of MapQuest.'), + 'projection' => array('EPSG:3857', 'EPSG:900913'), 'layer_type' => 'openlayers_layer_type_xyz', 'url' => array( '//oatile1' . $mapquest_host . '/tiles/1.0.0/sat/${z}/${x}/${y}.png', @@ -121,7 +121,7 @@ function _openlayers_openlayers_layers() { 'name' => 'Bing Road', 'isBaseLayer' => TRUE, 'type' => 'Road', - 'projection' => array('EPSG:900913'), + 'projection' => array('EPSG:3857'), 'layer_type' => 'openlayers_layer_type_bing', ); $layers[$layer->name] = $layer; @@ -136,7 +136,7 @@ function _openlayers_openlayers_layers() { 'name' => 'Bing Aerial', 'isBaseLayer' => TRUE, 'type' => 'Aerial', - 'projection' => array('EPSG:900913'), + 'projection' => array('EPSG:3857'), 'layer_type' => 'openlayers_layer_type_bing', ); $layers[$layer->name] = $layer; @@ -151,7 +151,7 @@ function _openlayers_openlayers_layers() { 'name' => 'Bing Hybrid', 'isBaseLayer' => TRUE, 'type' => 'AerialWithLabels', - 'projection' => array('EPSG:900913'), + 'projection' => array('EPSG:3857'), 'layer_type' => 'openlayers_layer_type_bing', ); $layers[$layer->name] = $layer; @@ -164,14 +164,14 @@ function _openlayers_openlayers_layers() { $layer->description = 'The main OpenStreetMap map'; $layer->data = array( 'isBaseLayer' => TRUE, - 'attribution' => t('©CCBYSA '. + 'attribution' => t('©CCBYSA ' . '© OpenStreetMap contributors', array( '@openstreetmap' => 'http://www.openstreetmap.org/copyright', '@ccbysa' => 'http://creativecommons.org/licenses/by-sa/2.0/', ) ), - 'projection' => array('EPSG:900913'), + 'projection' => array('EPSG:3857'), 'layer_type' => 'openlayers_layer_type_xyz', 'url' => '//tile.openstreetmap.org/${z}/${x}/${y}.png', 'wrapDateLine' => TRUE, @@ -186,14 +186,14 @@ function _openlayers_openlayers_layers() { $layer->description = 'OpenStreetMap with highlighted bike lanes'; $layer->data = array( 'isBaseLayer' => TRUE, - 'attribution' => t('©CCBYSA '. + 'attribution' => t('©CCBYSA ' . '© OpenStreetMap contributors', array( '@openstreetmap' => 'http://www.openstreetmap.org/copyright', '@ccbysa' => 'http://creativecommons.org/licenses/by-sa/2.0/', ) ), - 'projection' => array('EPSG:900913'), + 'projection' => array('EPSG:3857'), 'layer_type' => 'openlayers_layer_type_xyz', 'url' => '//a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png', 'wrapDateLine' => TRUE, @@ -209,7 +209,7 @@ function _openlayers_openlayers_layers() { WSG84 for use on non spherical-mercator maps.'; $layer->data = array( 'isBaseLayer' => FALSE, - 'attribution' => t('©CCBYSA '. + 'attribution' => t('©CCBYSA ' . '© OpenStreetMap contributors', array( '@openstreetmap' => 'http://www.openstreetmap.org/copyright', @@ -221,9 +221,7 @@ function _openlayers_openlayers_layers() { 'url' => 'http://oam.hypercube.telascience.org/tiles', 'params' => array( 'isBaseLayer' => FALSE, - 'layers' => array( - 'osm-4326-hybrid' - ), + 'layers' => 'osm-4326-hybrid', ), 'options' => array( 'buffer' => 1, @@ -246,7 +244,9 @@ function _openlayers_openlayers_layers() { 'extractTracks' => FALSE, 'extractAttributes' => TRUE, ), - 'projection' => array('EPSG:4326'), + 'projection' => array( + 'EPSG:4326', + ), 'isBaseLayer' => 0, 'layer_type' => 'openlayers_layer_type_kml', 'layer_handler' => 'kml', @@ -270,7 +270,7 @@ function _openlayers_openlayers_layers() { ), 'options' => array( 'layers' => array('basic'), - 'maxExtent' => openlayers_get_extent('EPSG:4326'), + 'maxExtent' => openlayers_get_extent('EPSG', '4326'), ), ); $layers[$layer->name] = $layer; @@ -339,7 +339,7 @@ function _openlayers_openlayers_layers() { 'isBaseLayer' => TRUE, 'type' => 'png', 'resolutions' => array_intersect_key($resolutions, array_flip(range($item['minzoom'], $item['maxzoom']))), - 'projection' => array('EPSG:900913'), + 'projection' => array('EPSG:3857'), ); $layers[$key] = $openlayers_layers; } diff --git a/includes/openlayers.maps.inc b/includes/openlayers.maps.inc index 841ad6c..0c0f927 100644 --- a/includes/openlayers.maps.inc +++ b/includes/openlayers.maps.inc @@ -25,7 +25,7 @@ function _openlayers_openlayers_maps() { $default->title = t('Default Map'); $default->description = t('This is the default map that will be the basis for all maps, unless you have changed the !settings. This is also a good example of a basic map.', array('!settings' => l(t('OpenLayers main settings'), 'admin/structure/openlayers'))); $default->data = array( - 'projection' => 'EPSG:900913', + 'projection' => 'EPSG:3857', 'width' => 'auto', 'height' => '400px', 'default_layer' => 'mapquest_osm', @@ -38,7 +38,7 @@ function _openlayers_openlayers_maps() { 'image_path' => drupal_get_path('module', 'openlayers') . '/themes/default_dark/img/', 'css_path' => drupal_get_path('module', 'openlayers') . '/themes/default_dark/style.css', 'displayProjection' => 'EPSG:4326', - 'maxExtent' => openlayers_get_extent('EPSG:4326'), + 'maxExtent' => openlayers_get_extent('EPSG', '4326'), 'behaviors' => array( 'openlayers_behavior_panzoombar' => array(), 'openlayers_behavior_layerswitcher' => array(), @@ -107,7 +107,7 @@ function _openlayers_openlayers_maps() { 'google_normal' => 'google_normal', 'google_physical' => 'google_physical', ), - 'projection' => 'EPSG:900913', + 'projection' => 'EPSG:3857', 'displayProjection' => 'EPSG:4326', 'styles' => array( 'default' => 'default', @@ -183,7 +183,7 @@ function _openlayers_openlayers_maps() { 'layer_activated' => array( 'openlayers_geojson_picture_this' => 'openlayers_geojson_picture_this', ), - 'projection' => 'EPSG:900913', + 'projection' => 'EPSG:3857', 'displayProjection' => 'EPSG:4326', 'styles' => array( 'default' => 'default', @@ -261,7 +261,7 @@ function _openlayers_openlayers_maps() { 'layer_activated' => array( 'openlayers_kml_example' => 'openlayers_kml_example', ), - 'projection' => 'EPSG:900913', + 'projection' => 'EPSG:3857', 'displayProjection' => 'EPSG:4326', 'styles' => array( 'default' => 'default', @@ -321,7 +321,7 @@ function _openlayers_openlayers_maps() { 'bing_hybrid' => 'bing_hybrid', 'bing_aerial' => 'bing_aerial', ), - 'projection' => 'EPSG:900913', + 'projection' => 'EPSG:3857', 'displayProjection' => 'EPSG:4326', 'styles' => array( 'default' => 'default', @@ -379,7 +379,7 @@ function _openlayers_openlayers_maps() { 'osm_mapnik' => 'osm_mapnik', 'osm_cycle' => 'osm_cycle', ), - 'projection' => 'EPSG:900913', + 'projection' => 'EPSG:3857', 'displayProjection' => 'EPSG:4326', 'styles' => array( 'default' => 'default', diff --git a/includes/openlayers.projections.inc b/includes/openlayers.projections.inc new file mode 100644 index 0000000..4e89fd4 --- /dev/null +++ b/includes/openlayers.projections.inc @@ -0,0 +1,237 @@ + + */ +function _openlayers_openlayers_projections() { + $projections = array(); + + // CH1903+ / LV95 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:2056'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '2056'; + $openlayers_projections->definition = '+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 +x_0=2600000 +y_0=1200000 +ellps=bessel +towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '2485869.5728'; + $openlayers_projections->projectedextentbottom = '1076443.1884'; + $openlayers_projections->projectedextentright = '2837076.5648'; + $openlayers_projections->projectedextenttop = '1299941.7864'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // Pulkovo 1942(83) / 3-degree Gauß-Krüger zone 4 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:2398'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '2398'; + $openlayers_projections->definition = '+proj=tmerc +lat_0=0 +lon_0=12 +k=1 +x_0=4500000 +y_0=0 +ellps=krass +towgs84=24,-123,-94,0.02,-0.25,-0.13,1.1 +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '4390150.9946'; + $openlayers_projections->projectedextentbottom = '5419736.8531'; + $openlayers_projections->projectedextentright = '4610103.4021'; + $openlayers_projections->projectedextenttop = '6067242.1812'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // Pulkovo 1942(83) / 3-degree Gauß-Krüger zone 5 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:2399'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '2399'; + $openlayers_projections->definition = '+proj=tmerc +lat_0=0 +lon_0=15 +k=1 +x_0=5500000 +y_0=0 +ellps=krass +towgs84=24,-123,-94,0.02,-0.25,-0.13,1.1 +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '5384982.9245'; + $openlayers_projections->projectedextentbottom = '5152896.3049'; + $openlayers_projections->projectedextentright = '5615270.6353'; + $openlayers_projections->projectedextenttop = '6053880.2778'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // Former EPSG:900913 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:3857'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '3857'; + $openlayers_projections->definition = '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs'; + $openlayers_projections->projectedextentleft = '-20037508.34'; + $openlayers_projections->projectedextentbottom = '-20037508.34'; + $openlayers_projections->projectedextentright = '20037508.34'; + $openlayers_projections->projectedextenttop = '20037508.34'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // ETRS89 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:4258'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '4258'; + $openlayers_projections->definition = '+proj=longlat +ellps=GRS80 +no_defs'; + $openlayers_projections->projectedextentleft = '-10.6700'; + $openlayers_projections->projectedextentbottom = '34.5000'; + $openlayers_projections->projectedextentright = '31.5500'; + $openlayers_projections->projectedextenttop = '71.0500'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:4326'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '4326'; + $openlayers_projections->definition = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'; + $openlayers_projections->projectedextentleft = '-180'; + $openlayers_projections->projectedextentbottom = '-90'; + $openlayers_projections->projectedextentright = '180'; + $openlayers_projections->projectedextenttop = '90'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // CH1903 / LV03 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:21781'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '21781'; + // Warning: Conversions without grid shift file yield errors of up to 2.5m + $openlayers_projections->definition = '+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 +x_0=600000 +y_0=200000 +ellps=bessel +towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '485869.5728'; + $openlayers_projections->projectedextentbottom = '76443.1884'; + $openlayers_projections->projectedextentright = '837076.5648'; + $openlayers_projections->projectedextenttop = '299941.7864'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // ETRS89 / UTM zone 32N + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:25832'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '25832'; + $openlayers_projections->definition = '+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '265948.8191'; + $openlayers_projections->projectedextentbottom = '6421521.2254'; + $openlayers_projections->projectedextentright = '677786.3629'; + $openlayers_projections->projectedextenttop = '7288831.7014'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // ETRS89 / UTM zone 33N + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:25833'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '25833'; + $openlayers_projections->definition = '+proj=utm +zone=33 +ellps=GRS80 +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '227879.8880'; + $openlayers_projections->projectedextentbottom = '3932632.6543'; + $openlayers_projections->projectedextentright = '1044484.3835'; + $openlayers_projections->projectedextenttop = '8893131.0281'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // OSGB 1936 / British National Grid + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:27700'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '27700'; + $openlayers_projections->definition = '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '1393.0196'; + $openlayers_projections->projectedextentbottom = '13494.9764'; + $openlayers_projections->projectedextentright = '671196.3657'; + $openlayers_projections->projectedextenttop = '1230275.0454'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // DHDN / 3-degree Gauß-Krüger zone 2 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:31466'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '31466'; + $openlayers_projections->definition = '+proj=tmerc +lat_0=0 +lon_0=6 +k=1 +x_0=2500000 +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '2490547.1867'; + $openlayers_projections->projectedextentbottom = '5440321.7879'; + $openlayers_projections->projectedextentright = '2609576.6008'; + $openlayers_projections->projectedextenttop = '5958700.0208'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // DHDN / 3-degree Gauß-Krüger zone 3 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:31467'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '31467'; + $openlayers_projections->definition = '+proj=tmerc +lat_0=0 +lon_0=9 +k=1 +x_0=3500000 +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '3386564.9400'; + $openlayers_projections->projectedextentbottom = '5237917.9109'; + $openlayers_projections->projectedextentright = '3613579.2251'; + $openlayers_projections->projectedextenttop = '6104500.7393'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // DHDN / 3-degree Gauß-Krüger zone 4 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:31468'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '31468'; + $openlayers_projections->definition = '+proj=tmerc +lat_0=0 +lon_0=12 +k=1 +x_0=4500000 +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '4386596.4101'; + $openlayers_projections->projectedextentbottom = '5237914.5325'; + $openlayers_projections->projectedextentright = '4613610.5843'; + $openlayers_projections->projectedextenttop = '6104496.9694'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // DHDN / 3-degree Gauß-Krüger zone 5 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:31469'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '31469'; + $openlayers_projections->definition = '+proj=tmerc +lat_0=0 +lon_0=15 +k=1 +x_0=5500000 +y_0=0 +ellps=bessel +datum=potsdam +units=m +no_defs'; + $openlayers_projections->projectedextentleft = '5386627.6162'; + $openlayers_projections->projectedextentbottom = '5237909.9506'; + $openlayers_projections->projectedextentright = '5613641.6401'; + $openlayers_projections->projectedextenttop = '6104491.8566'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + // TODO Remove before publishing as replaced by EPSG:3857 + $openlayers_projections = new stdClass(); + $openlayers_projections->disabled = FALSE; /* Edit this to true to make a default openlayers_projections disabled initially */ + $openlayers_projections->api_version = 1; + $openlayers_projections->identifier = 'EPSG:900913'; + $openlayers_projections->authority = 'EPSG'; + $openlayers_projections->code = '900913'; + $openlayers_projections->definition = '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs'; + $openlayers_projections->projectedextentleft = '-20037508.34'; + $openlayers_projections->projectedextentbottom = '-20037508.34'; + $openlayers_projections->projectedextentright = '20037508.34'; + $openlayers_projections->projectedextenttop = '20037508.34'; + $openlayers_projections->data = ''; + $projections[$openlayers_projections->identifier] = $openlayers_projections; + + return $projections; +} \ No newline at end of file diff --git a/includes/openlayers.render.inc b/includes/openlayers.render.inc index 0b92c51..6218fae 100644 --- a/includes/openlayers.render.inc +++ b/includes/openlayers.render.inc @@ -20,7 +20,7 @@ function _openlayers_layers_process($layers = array(), &$map = array()) { $layer_data = array(); // Load Layers and assign weights - foreach ($layers as $key => $layer){ + foreach ($layers as $key => $layer) { if ($layer_object = openlayers_layer_load($layer)) { $layers[$key] = $layer_object; if (!empty($map['layer_weight'][$key])) { diff --git a/js/openlayers.js b/js/openlayers.js index a837556..34c4906 100644 --- a/js/openlayers.js +++ b/js/openlayers.js @@ -40,6 +40,10 @@ Drupal.behaviors.openlayers = { if (Drupal.settings.openlayers.maps[map_id] && Drupal.settings.openlayers.maps[map_id].stop_render != true) { var map = Drupal.settings.openlayers.maps[map_id]; + if(map.default_layer===null){ + console.error("Map configuration is invalid as it lacks a base layer."); + } + // Use try..catch for error handling. try { // Set OpenLayers language based on document language, @@ -51,15 +55,8 @@ Drupal.behaviors.openlayers = { options.projection = new OpenLayers.Projection(map.projection); options.displayProjection = new OpenLayers.Projection(map.displayProjection); - // TODO: work around this scary code - if (map.projection === 'EPSG:900913') { - options.maxExtent = new OpenLayers.Bounds( - -20037508.34, -20037508.34, 20037508.34, 20037508.34); - options.units = "m"; - } - if (map.projection === 'EPSG:4326') { - options.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90); - } + // Restrict map to its projection extent (data outwith cannot be represented) + options.maxExtent = new OpenLayers.Bounds(map.maxExtent[0], map.maxExtent[1], map.maxExtent[2], map.maxExtent[3]); options.maxResolution = 'auto'; // 1.40625; options.controls = []; @@ -79,11 +76,16 @@ Drupal.behaviors.openlayers = { } // Initialize openlayers map - var openlayers = new OpenLayers.Map(options); + var openlayers = new OpenLayers.Map(map_id, options); // Run the layer addition first Drupal.openlayers.addLayers(map, openlayers); + // Ensure redraw as maps stays blank until first zoom otherwise (observed with EPSG:2056) + openlayers.moveTo(openlayers.getCenter(), openlayers.getZoom(), { + forceZoomChange: true + }); + // Attach data to map DOM object $(this).data('openlayers', {'map': map, 'openlayers': openlayers}); @@ -175,7 +177,7 @@ Drupal.openlayers = { // Ensure that the layer handler is available if (options.layer_handler !== undefined && Drupal.openlayers.layer[options.layer_handler] !== undefined) { - var layer = Drupal.openlayers.layer[options.layer_handler](map.layers[name].title, map, options); + layer = Drupal.openlayers.layer[options.layer_handler](map.layers[name].title, map, options); layer.visibility = !!(!map.layer_activated || map.layer_activated[name]); @@ -197,8 +199,20 @@ Drupal.openlayers = { // Set the restricted extent if wanted. // Prevents the map from being panned outside of a specfic bounding box. if (typeof map.center.restrict !== 'undefined' && map.center.restrict.restrictextent) { - openlayers.restrictedExtent = OpenLayers.Bounds.fromString( - map.center.restrict.restrictedExtent); + var desiredRestriction = OpenLayers.Bounds.fromString( + map.center.restrict.restrictedExtent + ).transform(new OpenLayers.Projection('EPSG:3857'), openlayers.projection); + + if(desiredRestriction.left>=openlayers.maxExtent.left && desiredRestriction.right<=openlayers.maxExtent.right + && desiredRestriction.top<=openlayers.maxExtent.top && desiredRestriction.bottom>=openlayers.maxExtent.bottom){ + + openlayers.restrictedExtent = desiredRestriction; + } else { + // Given the map to set the restricted extent is not dependent on the map projection + // it does allow to set an extent outwith the valid bound of the map projection. As a + // result no valid data could be requested and thus the wrong extent needs to be ignored. + console.error("Restricted bounds ignored as not within projection bounds."); + } } // Zoom & center diff --git a/modules/openlayers_ui/includes/openlayers_ui.maps.inc b/modules/openlayers_ui/includes/openlayers_ui.maps.inc index fe7ef55..72736e1 100644 --- a/modules/openlayers_ui/includes/openlayers_ui.maps.inc +++ b/modules/openlayers_ui/includes/openlayers_ui.maps.inc @@ -93,24 +93,24 @@ function openlayers_ui_maps_form_process($values = array()) { break; case 'projections': - $processed['projection'] = $values['projections']['easy_projection']; - $processed['displayProjection'] = $values['projections']['displayProjection']; + $processed['projection'] = $values['layerstyles']['projections']['easy_projection']; + $processed['displayProjection'] = $values['layerstyles']['projections']['displayProjection']; break; case 'layers': // Put the default layer in the right place. // TODO: finish port here - $processed['default_layer'] = $values['layers']['default_layer']; - $baselayers = array_filter($values['layers']['baselayers']); - $overlays = (isset($values['layers']['overlays'])) ? - array_filter($values['layers']['overlays']) : array(); + $processed['default_layer'] = $values['layerstyles']['layers']['default_layer']; + $baselayers = array_filter($values['layerstyles']['layers']['baselayers']); + $overlays = (isset($values['layerstyles']['layers']['overlays'])) ? + array_filter($values['layerstyles']['layers']['overlays']) : array(); // Sort overlay layers by weight, in case browser didn't // send them already sorted - if (isset($values['layer_weight'])) { + if (isset($values['layerstyles']['layer_weight'])) { $overlays = array(); - asort($values['layer_weight']); - foreach (array_keys($values['layer_weight']) as $layer_key) { - if ($values['layers']['overlays'][$layer_key]) { + asort($values['layerstyles']['layer_weight']); + foreach (array_keys($values['layerstyles']['layer_weight']) as $layer_key) { + if ($values['layerstyles']['layers']['overlays'][$layer_key]) { $overlays[$layer_key] = $layer_key; } } @@ -121,6 +121,16 @@ function openlayers_ui_maps_form_process($values = array()) { $processed['layers'] = array_merge($baselayers, $overlays); break; + case 'layer_weight': + case 'layer_styles': + case 'layer_styles_select': + case 'layer_activated': + case 'layer_switcher': + case 'projections': + case 'styles': + $processed[$key] = isset($values['layerstyles'][$key]) ? $values['layerstyles'][$key] : array(); + break; + default: // $processed[$key] = is_array($values[$key]) ? // array_filter($values[$key]) : $values[$key]; @@ -230,6 +240,9 @@ function openlayers_ui_maps_form_center_map($defaults = array()) { ); drupal_add_js($pass_values, 'setting'); + $projection = openlayers_get_projection('EPSG', '3857'); + openlayers_add_js_projection_definition($projection); + // centerpoint & zoom of this map are overridden // by the mapformvalues behavior on page load. // @@ -237,8 +250,8 @@ function openlayers_ui_maps_form_center_map($defaults = array()) { // behavior for getting extent. $centermap_def = array( 'id' => 'openlayers-center-helpmap', - 'projection' => 'EPSG:900913', - 'displayProjection' => 'EPSG:900913', + 'projection' => $projection->identifier, + 'displayProjection' => $projection->identifier, 'default_layer' => 'mapquest_osm', 'width' => '600px', 'height' => '400px', diff --git a/modules/openlayers_ui/includes/openlayers_ui.theme.inc b/modules/openlayers_ui/includes/openlayers_ui.theme.inc index 496c871..4cd0475 100644 --- a/modules/openlayers_ui/includes/openlayers_ui.theme.inc +++ b/modules/openlayers_ui/includes/openlayers_ui.theme.inc @@ -24,7 +24,7 @@ function theme_openlayers_ui_form_layer_description($args) { * Theme function for form description of projection */ function theme_openlayers_ui_form_projection_description($args) { - $output = $args['projection_title']; + $output = $args['projection']->getLocalizedMessage(); if (count($args['available_layers'])) { $names = implode(', ', $args['available_layers']); $output .= " - $names"; @@ -34,7 +34,9 @@ function theme_openlayers_ui_form_projection_description($args) { function theme_openlayers_ui_maps_form_layers($args) { $form = $args['elements']; - $output = ''; + + $output = drupal_render($form['projections']) + . drupal_render($form['help_data_overlays']); // Base layers $rows = array(); @@ -47,7 +49,7 @@ function theme_openlayers_ui_maps_form_layers($args) { drupal_render($form['layers']['baselayers'][$key]), drupal_render($form['layers']['default_layer'][$key])); } - $output .= theme('table', array( + $output_layer_list = theme('table', array( 'header' => array(t('Base layers'), t('Enabled'), t('Default')), 'rows' => $rows) ); @@ -55,7 +57,7 @@ function theme_openlayers_ui_maps_form_layers($args) { // Overlay layers $rows = array(); foreach (element_children($form['layers']['overlaylabels']) as $key) { - unset($form['layers']['overlays'][$key]['#title']); + unset($form['overlays'][$key]['#title']); unset($form['layer_activated'][$key]['#title']); unset($form['layer_switcher'][$key]['#title']); @@ -72,7 +74,7 @@ function theme_openlayers_ui_maps_form_layers($args) { 'class' => array('draggable') ); } - $output .= theme('table', + $output_layer_list .= theme('table', array( 'header' => array( t('Overlay layers'), t('Weight'), t('Enabled'), @@ -82,10 +84,12 @@ function theme_openlayers_ui_maps_form_layers($args) { array('id' => 'overlay-layer-table') ) ); + $form['layers']['layerlist']['#markup'] = $output_layer_list; + $output .= drupal_render($form['layers']); drupal_add_tabledrag('overlay-layer-table', 'order', 'sibling', 'layer-weight'); - return $output . drupal_render_children($form); + return $form['#prefix'] . $output . drupal_render_children($form) . $form['#suffix']; } /** diff --git a/modules/openlayers_ui/js/openlayers_ui.maps.js b/modules/openlayers_ui/js/openlayers_ui.maps.js index 2966078..893f57e 100644 --- a/modules/openlayers_ui/js/openlayers_ui.maps.js +++ b/modules/openlayers_ui/js/openlayers_ui.maps.js @@ -69,87 +69,7 @@ Drupal.behaviors.openlayers_ui = { }); }); - - // Run once on load. - Drupal.openlayers_ui.updateMapCenter(); - } -}; - -/** - * Register form center value updating events. - */ -Drupal.behaviors.openlayers_ui_center = { - 'attach': function(context, settings) { - var data = $(context).data('openlayers'); - if (data) { - data.openlayers.events.register('moveend', data.map, function() { - Drupal.openlayers_ui.updateCenterFormValues() - }); - data.openlayers.events.register('zoomend', data.map, function() { - Drupal.openlayers_ui.updateCenterFormValues() - }); - } } }; -/** - * Helper functions. - */ -Drupal.openlayers_ui = { - - /** - * Update the center of the helpmap using the values from the form - * - * Take the center lat, lon and zoom values from the form and update - * the helper map. - */ - 'updateMapCenter': function() { - var data = $('#openlayers-center-helpmap').data('openlayers'); - if (data) { - var projection = $('#edit-projections-projection').val(); - var zoom = $('#edit-center-zoom').val(); - var lonlat = $('#edit-center-initial-centerpoint').val(); - if (typeof lonlat == Array) { - // Create new center - var center = new OpenLayers.LonLat( - parseFloat(lonlat[0]), - parseFloat(lonlat[1])); - // Transform for projection - center.transform( - new OpenLayers.Projection(projection), - new OpenLayers.Projection('EPSG:4326')); - // Set center of map. - data.openlayers.setCenter(center, zoom); - } - } - }, - - /** - * Event callback for updating center form field values when map - * is dragged or zoomed. - */ - 'updateCenterFormValues': function() { - var data = $('#openlayers-center-helpmap').data('openlayers'); - if (data) { - var helpmap = data.openlayers; - var projection = $('#edit-projections-projection').val(); - var zoom = helpmap.getZoom(); - var center = helpmap.getCenter(); - - // Transform center - center.transform( - new OpenLayers.Projection('EPSG:4326'), - new OpenLayers.Projection(projection)); - - // Get new lat and lon - var lat = center.lat; - var lon = center.lon; - - // Set new values - $('#edit-center-zoom').val(zoom); - $('#edit-center-lat').val(lat); - $('#edit-center-lon').val(lon); - } - } -} })(jQuery); diff --git a/modules/openlayers_ui/openlayers_ui.module b/modules/openlayers_ui/openlayers_ui.module index 4745722..cff5c39 100644 --- a/modules/openlayers_ui/openlayers_ui.module +++ b/modules/openlayers_ui/openlayers_ui.module @@ -56,39 +56,41 @@ function openlayers_ui_init() { function openlayers_ui_help($path, $arg) { switch ($path) { case 'admin/help#openlayers_ui': - return '

'. t('Provides a user interface to manage OpenLayers maps.') .'

'; + return '

' . t('Provides a user interface to manage OpenLayers maps.') . '

'; case 'admin/structure/openlayers': - return '

'. t('Configure where Drupal finds + return '

' . t('Configure where Drupal finds the OpenLayers javascript library. Using a local or compacted version of OpenLayers can reduce filesize, but remember to set the Image Path and CSS Path options in every map, because OpenLayers is not able to find these resources automatically. See the included documentation in /doc for more details.', - array('@olb' => 'http://trac.openlayers.org/wiki/Profiles')) .'

'; + array('@olb' => 'http://trac.openlayers.org/wiki/Profiles')) . '

'; case 'admin/structure/openlayers/styles': - return '

'. t('OpenLayer styles are stored versions of + return '

' . t('OpenLayer styles are stored versions of javascript StyleMap objects, but are much easier to configure through an interface. Styles will typically define the icon, fill color, border, and radius of points or polygons, so they are used on overlays more often than base layers, which are not affected by vector - styles.', array("@ols" => 'http://trac.openlayers.org/wiki/Styles')) .'

'; + styles.', array("@ols" => 'http://trac.openlayers.org/wiki/Styles')) . '

'; case 'admin/structure/openlayers/styles/add': - return '

'. t('The name, title, and description you give a style is for Drupal\'s + return '

' . t('The name, title, and description you give a style is for Drupal\'s interal storage. The rest of the properties are documented on openlayers.org') . '

'; case 'admin/structure/openlayers/maps': - return '

'. t('Maps are combinations of the layers, styles, and behaviors + return '

' . t('Maps are combinations of the layers, styles, and behaviors which constitute maps. They also contain basic configuration like the width and centerpoint of the map - everything except for a mechanism - to put a map on a page. For that, use the Views OpenLayers Map style.') .'

'; + to put a map on a page. For that, use the Views OpenLayers Map style.') . '

'; case 'admin/structure/openlayers/layers': - return '

'. t('Layers are stored versions of javascript OpenLayers layer + return '

' . t('Layers are stored versions of javascript OpenLayers layer objects. They point at a data source, like OpenStreetMap, Google Maps, - or Drupal itself, and sometimes define the zoom levels for which they apply.') .'

'; + or Drupal itself, and sometimes define the zoom levels for which they apply.') . '

'; + case 'admin/structure/openlayers/projections': + return t('

Projections describe how the earth is flattened to a map image. They define coordinate systems for data retrieval.

'); case 'admin/structure/openlayers/layers/settings': - return '

'. t('Commercial layer providers like Google will require API keys + return '

' . t('Commercial layer providers like Google will require API keys to ensure that users are\'t abusing the service and to enforce limits. Not all - of these keys are required, of course, only those that are used on a site.') .'

'; + of these keys are required, of course, only those that are used on a site.') . '

'; } } @@ -164,7 +166,7 @@ function openlayers_ui_theme($existing, $type, $theme, $path) { ), 'openlayers_ui_form_projection_description' => array( 'variables' => array( - 'projection_title' => '', + 'projection' => '', 'available_layers' => array(), 'layers' => array(), ), @@ -187,7 +189,7 @@ function openlayers_ui_theme($existing, $type, $theme, $path) { 'style' => NULL, ), 'template' => 'openlayers-ui-styles-preview', - 'path' => drupal_get_path('module', 'openlayers_ui') . '/themes', + 'path' => drupal_get_path('module', 'openlayers_ui') . '/themes', ), ); } @@ -201,10 +203,8 @@ function openlayers_ui_theme($existing, $type, $theme, $path) { function openlayers_ui_get_projection_options() { $options = array(); foreach (openlayers_layers_load() as $key => $layer) { - if ($layer->data['projection']) { - foreach ($layer->data['projection'] as $projection) { - $options[$projection] = $projection; - } + foreach ($layer->getProjections() as $projection) { + $options[$projection->identifier] = $projection; } } return $options; @@ -212,6 +212,8 @@ function openlayers_ui_get_projection_options() { /** * Get layer options. + * @param String $type + * @param openlayers_projection $projection */ function openlayers_ui_get_layer_options($type = 'baselayer', $projection = NULL) { @@ -225,8 +227,10 @@ function openlayers_ui_get_layer_options($type = 'baselayer', // Go through layers to theme output. foreach ($layers as $key => $layer) { - - if (!is_object($layer)) continue; + if (!is_object($layer)) { + // Failure was already logged in openlayers_get_layer_object + continue; + } // not filtering by projection // this layer has an appropriate projection @@ -234,9 +238,12 @@ function openlayers_ui_get_layer_options($type = 'baselayer', $variables = array(); if ( !isset($projection) || - in_array($projection, $layer->data['projection']) || + in_array($projection, $layer->getProjections()) || !empty($layer->data['vector']) ) { + if (!array_key_exists('isBaseLayer', $layer->data)) { + throw new Exception("Layer definition $key lacks isBaseLayer attribute"); + } if ($layer->data['isBaseLayer'] == TRUE && $type == 'baselayer') { $variables = array( diff --git a/modules/openlayers_ui/plugins/export_ui/openlayers_layers_ui.class.php b/modules/openlayers_ui/plugins/export_ui/openlayers_layers_ui.class.php index 6a1df67..e9a85ce 100644 --- a/modules/openlayers_ui/plugins/export_ui/openlayers_layers_ui.class.php +++ b/modules/openlayers_ui/plugins/export_ui/openlayers_layers_ui.class.php @@ -51,7 +51,8 @@ class openlayers_layers_ui extends ctools_export_ui { $parent = get_parent_class($layer_type_object); $parent_object = new $parent; $layer_options_form = $layer_type_object->options_form() + $parent_object->options_form($layer); - } + } + // TODO First case can never be true without crashing earlier if ($layer_type_object == FALSE || empty($layer_options_form)) { continue; } diff --git a/modules/openlayers_ui/plugins/export_ui/openlayers_maps_ui.class.php b/modules/openlayers_ui/plugins/export_ui/openlayers_maps_ui.class.php index b999e43..ee407ae 100644 --- a/modules/openlayers_ui/plugins/export_ui/openlayers_maps_ui.class.php +++ b/modules/openlayers_ui/plugins/export_ui/openlayers_maps_ui.class.php @@ -33,6 +33,8 @@ class openlayers_maps_ui extends ctools_export_ui { else { $defaults = $map->data; } + $defaults['projection'] = openlayers_get_projection_by_identifier($defaults['projection']); + $defaults['displayProjection'] = openlayers_get_projection_by_identifier($defaults['displayProjection']); // Map preview. It's a little in the way, so we allow the user // to turn it on or off. Show on preview, otherwise look at setting. @@ -264,12 +266,14 @@ class openlayers_maps_ui extends ctools_export_ui { // Layers & styles $form['layerstyles'] = array( + '#type' => 'fieldset', '#title' => t('Layers & Styles'), '#description' => t('Layer settings. The Layer options will change based on the projection chosen.'), '#theme' => 'openlayers_ui_maps_form_layers', - '#tree' => FALSE, - '#type' => 'fieldset', - '#group' => 'ui' + '#tree' => TRUE, + '#group' => 'ui', + '#prefix' => '', + '#suffix' => '' ); // Projection options @@ -279,19 +283,78 @@ class openlayers_maps_ui extends ctools_export_ui { openlayers_ui_get_layer_options('baselayer', $projection), openlayers_ui_get_layer_options('overlay', $projection)); - $projections[$projection] = theme( + $projections[$projection->identifier] = theme( 'openlayers_ui_form_projection_description', array( - 'projection_title' => $projection, - 'available_layers' => array_keys($projection_layers) + 'projection' => $projection, + 'available_layers' => $projection_layers ) ); } + $form['layerstyles']['projections'] = array( + '#type' => 'fieldset', + '#title' => t('Projections'), + '#description' => t('

WARNING: Projections are not well supported in + this module. If you need to handle non spherical mercator projections + you may be better using the API directly.

+

Select the !link_proj for your map. The list next + to each projection is the layers that support this projection.

', + array('!link_proj' => l(t('geographical projection'), + 'http://en.wikipedia.org/wiki/Map_projection')) + ), + '#tree' => TRUE, + ); + $form['layerstyles']['projections']['easy_projection'] = array( + '#type' => 'radios', + '#title' => t('Map Projection'), + '#description' => t('The !link_proj of this map: all layers will either be requested or reprojected to this format.', array( + '!link_proj' => l( + t('geographical projection'), + 'http://en.wikipedia.org/wiki/Map_projection'), + )), + '#default_value' => $defaults['projection']->identifier, + '#options' => $projections, + '#attributes' => array('class' => array('openlayers-form-easy-projection')), + '#ajax' => array( + 'callback' => 'openlayers_map_layerlist', + 'wrapper' => 'edit-layerstyles', + 'method' => 'replace', + 'event' => 'change' + ) + ); + $displayProjectionOptions = array(); + foreach(openlayers_ui_get_projection_options() as $projectionIdentifier => $projection){ + $displayProjectionOptions[$projectionIdentifier] = $projection->getLocalizedMessage(); + } + $form['layerstyles']['projections']['displayProjection'] = array( + '#type' => 'select', + '#title' => t('Display Projection'), + '#description' => t('All interaction with the map - drawing, panning, + centering, and more - occurs in the display projection. The vast majority + of maps use 4326 (latitude/longitude) for this value.'), + '#default_value' => !empty($defaults['displayProjection']) ? + $defaults['displayProjection']->identifier : openlayers_get_projection('EPSG', '4326')->identifier, + '#options' => $displayProjectionOptions, + ); + + $form['layerstyles']['layers'] = array( + '#title' => t('Layers & Styles'), + '#description' => t('Layer settings. The Layer options will change based on the projection chosen.'), + '#type' => 'fieldset', + ); + // Construct data for theme_openlayers_ui_maps_form_layers $form['layerstyles']['layers']['#tree'] = TRUE; $form['layerstyles']['layers']['baselabels'] = array(); - $base_options = openlayers_ui_get_layer_options('baselayer', $defaults['projection']); + if(isset($form_state['values']['layerstyles']['projections']['easy_projection'])){ + // Projection was choosen by editing the form + $easy_projection = openlayers_get_projection_by_identifier($form_state['values']['layerstyles']['projections']['easy_projection']); + } else { + // Form was not yet edited, use default + $easy_projection = $defaults['projection']; + } + $base_options = openlayers_ui_get_layer_options('baselayer', $easy_projection); foreach ($base_options as $id => $description) { $form['layerstyles']['layers']['baselabels'][$id] = array('#markup' => $description); @@ -313,7 +376,7 @@ class openlayers_maps_ui extends ctools_export_ui { ); // Overlay layers - $overlay_options = openlayers_ui_get_layer_options('overlay', $defaults['projection']); + $overlay_options = openlayers_ui_get_layer_options('overlay', $easy_projection); $form['layerstyles']['layers']['overlaylabels'] = array(); if (!empty($overlay_options)) { @@ -434,59 +497,6 @@ class openlayers_maps_ui extends ctools_export_ui { $defaults['styles']['temporary'] : NULL, ); - // TODO: This projection stuff sucks. See - // http://drupal.org/node/1331410 - // - // Grab default from submitted form values on AHAH rebuild. - // Start AHAH Wrapper - /* - $form['layerstyles']['ahah-start'] = array('#value' => '
'); - if (isset($form_state['values']['projections'])) { - if ($form_state['values']['projections']['easy_projection'] == 'other') { - $defaults['projection'] = $form_state['values']['projections']['projection']; - } - else { - $defaults['projection'] = $form_state['values']['projections']['easy_projection']; - } - } - */ - - // Just kind of hide the projection stuff for now. - $form['layerstyles']['projections'] = array( - '#type' => 'fieldset', - '#title' => t('Projections'), - '#description' => t('

WARNING: Projections are not well supported in - this module. If you need to handle non spherical mercator projections - you may be better using the API directly.

-

Select the EPSG code of the !link_proj for your map. The list next - to each projection is the layers that support this projection.

', - array('!link_proj' => l(t('geographical projection'), - 'http://en.wikipedia.org/wiki/Map_projection')) - ), - '#tree' => TRUE, - ); - $form['layerstyles']['projections']['easy_projection'] = array( - '#type' => 'radios', - '#title' => t('Map Projection'), - '#description' => t('The !link_proj of this map: all layers will either be requested or reprojected to this format.', array( - '!link_proj' => l( - t('geographical projection'), - 'http://en.wikipedia.org/wiki/Map_projection'), - )), - '#default_value' => isset($defaults['projection']) ? $defaults['projection'] : 'EPSG:900913', - '#options' => $projections, - '#attributes' => array('class' => array('openlayers-form-easy-projection')), - ); - $form['layerstyles']['projections']['displayProjection'] = array( - '#type' => 'textfield', - '#title' => t('Display Projection'), - '#description' => t('All interaction with the map - drawing, panning, - centering, and more - occurs in the display projection. The vast majority - of maps use 4326 (latitude/longitude) for this value.'), - '#default_value' => !empty($defaults['displayProjection']) ? - $defaults['displayProjection'] : 'EPSG:4326' - ); - // Behaviors $form['behaviors'] = array( '#title' => t('Behaviors'), diff --git a/modules/openlayers_ui/plugins/export_ui/openlayers_projections.inc b/modules/openlayers_ui/plugins/export_ui/openlayers_projections.inc new file mode 100644 index 0000000..4166c07 --- /dev/null +++ b/modules/openlayers_ui/plugins/export_ui/openlayers_projections.inc @@ -0,0 +1,47 @@ + 'openlayers_projections', + 'access' => 'administer openlayers', + 'menu' => array( + 'menu prefix' => 'admin/structure/openlayers', + 'menu item' => 'projections', + // TODO Translation? + 'menu title' => 'Projections', + // TODO Translation? + 'menu description' => 'Administer OpenLayers projections.', + ), + + 'handler' => array( + 'class' => 'openlayers_projections_ui', + ), + + 'export' => array( + 'key' => 'identifier' + ), + + // TODO What is this good for? + 'title singular' => t('projections'), + 'title plural' => t('projections'), + 'title singular proper' => t('OpenLayers projection'), + 'title plural proper' => t('OpenLayers projections'), + + 'strings' => array( + 'confirmation' => array( + 'add' => array( + 'success' => t('Projection saved.'), + ), + 'delete' => array( + 'success' => t('Projection was deleted.'), + ) + ) + ), + ); +} diff --git a/modules/openlayers_ui/plugins/export_ui/openlayers_projections_ui.class.php b/modules/openlayers_ui/plugins/export_ui/openlayers_projections_ui.class.php new file mode 100644 index 0000000..e9a1e1e --- /dev/null +++ b/modules/openlayers_ui/plugins/export_ui/openlayers_projections_ui.class.php @@ -0,0 +1,88 @@ + 'projectionauthority', + '#type' => 'textfield', + '#weight' => 1, + '#title' => t('Projection Authority'), + '#default_value' => $projectionData->authority, + '#description' => t('Authority defining the projection'), + '#required' => TRUE + ); + $form['info']['code'] = array( + '#id' => 'projectioncode', + '#type' => 'textfield', + '#weight' => 2, + '#title' => t('Projection Code'), + '#default_value' => $projectionData->code, + '#description' => t('Identifier (unique within authority)'), + '#required' => TRUE + ); + $form['info']['definition'] = array( + '#id' => 'projectiondefinition', + '#type' => 'textarea', + '#weight' => 3, + '#title' => t('proj4 Definition'), + '#default_value' => $projectionData->definition, + '#description' => t('Description that can be used to handle rough conversions between coordinate systems. A correct definition is essential for coordinate displays or scale bars to work properly.'), + '#required' => TRUE + ); + $form['info']['projectedextentleft'] = array( + '#id' => 'projectionprojectedextentleft', + '#type' => 'textfield', + '#weight' => 4, + '#title' => t('Left Extent Boundary'), + '#default_value' => $projectionData->projectedextentleft, + '#description' => t("Leftmost value in this projection's coordinates"), + '#required' => TRUE + ); + $form['info']['projectedextentbottom'] = array( + '#id' => 'projectionprojectedextentbottom', + '#type' => 'textfield', + '#weight' => 5, + '#title' => t('Bottom Extent Boundary'), + '#default_value' => $projectionData->projectedextentbottom, + '#description' => t("Bottommost value in this projection's coordinates"), + '#required' => TRUE + ); + $form['info']['projectedextentright'] = array( + '#id' => 'projectionprojectedextentright', + '#type' => 'textfield', + '#weight' => 6, + '#title' => t('Right Extent Boundary'), + '#default_value' => $projectionData->projectedextentright, + '#description' => t("Rightmost value in this projection's coordinates"), + '#required' => TRUE + ); + $form['info']['projectedextenttop'] = array( + '#id' => 'projectionprojectedextenttop', + '#type' => 'textfield', + '#weight' => 7, + '#title' => t('Top Extent Boundary'), + '#default_value' => $projectionData->projectedextenttop, + '#description' => t("Topmost value in this projection's coordinates"), + '#required' => TRUE + ); + } + + function edit_form_submit(&$form, &$form_state) { + // Fill identifier with data from required fields. No empty string must result as ctools can't handle it. + $form_state['values']['identifier'] = $form_state['values']['authority'].':'.$form_state['values']['code']; + + parent::edit_form_submit($form, $form_state); + } + + function hook_menu(&$items) { + parent::hook_menu($items); + $items['admin/structure/openlayers/projections']['type'] = MENU_LOCAL_TASK; + } +} \ No newline at end of file diff --git a/modules/openlayers_views/plugins/layer_types/openlayers_views_vector.inc b/modules/openlayers_views/plugins/layer_types/openlayers_views_vector.inc index dbe3f6b..73dd64e 100644 --- a/modules/openlayers_views/plugins/layer_types/openlayers_views_vector.inc +++ b/modules/openlayers_views/plugins/layer_types/openlayers_views_vector.inc @@ -47,10 +47,10 @@ class openlayers_layer_type_openlayers_views_vector $views = views_get_all_views(); $options = array('' => ''); - foreach($views as $view) { + foreach ($views as $view) { foreach ($view->display as $display => $data) { if ($data->display_plugin == 'openlayers') { - $options[$view->name . '::' . $display] = $view->human_name . ': '.$display; + $options[$view->name . '::' . $display] = $view->human_name . ': ' . $display; } } } @@ -76,8 +76,9 @@ class openlayers_layer_type_openlayers_views_vector $form_state['data']['views']['view'] = $view; $form_state['data']['views']['display'] = $display; unset($form_state['data']['views']['composite']); - } else { - $field = $form_state['data']['layer_type'].'][views][composite'; + } + else { + $field = $form_state['data']['layer_type'] . '][views][composite'; form_set_error($field, t('A View must be set.')); } } diff --git a/modules/openlayers_views/views/openlayers_views_style_data.inc b/modules/openlayers_views/views/openlayers_views_style_data.inc index 1a59762..69068a5 100644 --- a/modules/openlayers_views/views/openlayers_views_style_data.inc +++ b/modules/openlayers_views/views/openlayers_views_style_data.inc @@ -36,7 +36,7 @@ class openlayers_views_style_data extends views_plugin_style { if (!empty($data)) { $keys = array_keys($data); foreach ($data[$keys[0]]['attributes'] as $key => $value) { - $output .= '${'.$key."}\n"; + $output .= '${' . $key . "}\n"; } } $output .= "\n------------\n"; @@ -242,7 +242,7 @@ class openlayers_views_style_data extends views_plugin_style { '#description' => t('Choose the field which will appear as a style on ' . 'the layer.'), '#required' => FALSE, - '#options' => array('' => '',) + $fields, + '#options' => array('' => '', ) + $fields, '#default_value' => $this->options['data_source']['style_field'], ); @@ -461,10 +461,10 @@ class openlayers_views_style_data extends views_plugin_style { $formatted_features[] = theme('openlayers_views_group_display_item', array( 'name' => isset($handlers[$ds['name_field']]) ? - $subfeature['attributes'][$ds['name_field'] . '_rendered'] : false, + $subfeature['attributes'][$ds['name_field'] . '_rendered'] : FALSE, 'description' => (in_array($ds['description_field'], $exceptions) || isset($handlers[$ds['description_field']])) ? - $subfeature['attributes'][$ds['description_field'] . '_rendered'] : false + $subfeature['attributes'][$ds['description_field'] . '_rendered'] : FALSE ) ); diff --git a/openlayers.info b/openlayers.info index e416caa..6e55952 100644 --- a/openlayers.info +++ b/openlayers.info @@ -5,6 +5,8 @@ package = "OpenLayers" php = 5.2 dependencies[] = ctools dependencies[] = libraries +dependencies[] = proj4js +dependencies[] = file ; Core files files[] = openlayers.module diff --git a/openlayers.install b/openlayers.install index 55ad148..9a2ca18 100644 --- a/openlayers.install +++ b/openlayers.install @@ -121,6 +121,75 @@ function openlayers_schema() { ), 'primary key' => array('name'), ); + // Projection table (ctools extras) + // TODO: Translations missing? + $schema['openlayers_projections'] = array( + 'description' => 'Storage for user defined OpenLayers projections.', + 'export' => array( + 'key' => 'identifier', + 'key name' => 'Identifier', + 'primary key' => 'identifier', + 'identifier' => 'openlayers_projections', + 'default hook' => 'openlayers_projections', + 'api' => array( + 'owner' => 'openlayers', + 'api' => 'openlayers_projections', + 'minimum_version' => 1, + 'current_version' => 1, + ), + ), + 'fields' => array( + 'identifier' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Opaque identifier. Guaranteed to be unique but does not have any other meaning.' + ), + 'authority' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => 'Projection authority.', + ), + 'code' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => 'Projection code.', + ), + 'definition' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => 'Projection definition (proj4 format).', + ), + 'projectedextentleft' => array( + 'type' => 'numeric', + 'not null' => TRUE, + 'description' => "Leftmost value in this projection's coordinates", + ), + 'projectedextentbottom' => array( + 'type' => 'numeric', + 'not null' => TRUE, + 'description' => "Bottommost value in this projection's coordinates", + ), + 'projectedextentright' => array( + 'type' => 'numeric', + 'not null' => TRUE, + 'description' => "Rightmost value in this projection's coordinates", + ), + 'projectedextenttop' => array( + 'type' => 'numeric', + 'not null' => TRUE, + 'description' => "Topmost value in this projection's coordinates", + ), + // TODO Hopefully never used + 'data' => array( + 'type' => 'text', + 'not null' => FALSE, + 'description' => 'Projection data serialized.', + 'serialize' => TRUE, + ), + ), + 'primary key' => array('identifier'), + ); // Styles table (ctools extras) $schema['openlayers_styles'] = array( 'description' => 'Storage for user defined OpenLayers styles.', @@ -301,7 +370,7 @@ function openlayers_update_7202() { } /** - * Renamme the 'baselayer' in 'isBaseLayer' in layers. + * Rename the 'baselayer' in 'isBaseLayer' in layers. */ function openlayers_update_7204() { foreach (openlayers_layers_load() as $key => $layer) { @@ -323,3 +392,109 @@ function openlayers_update_7205() { db_query('UPDATE {openlayers_styles} SET name = LOWER(name);'); db_query('UPDATE {openlayers_maps} SET name = LOWER(name);'); } + +/** + * Migrate configured projections so that other projections as those of EPSG can be used, too. + */ +function openlayers_update_7206() { + // Create projection table + $openlayers_projections_schema = array( + 'description' => 'Storage for user defined OpenLayers projections.', + 'export' => array( + 'key' => 'identifier', + 'key name' => 'Identifier', + 'primary key' => 'identifier', + 'identifier' => 'openlayers_projections', + 'default hook' => 'openlayers_projections', + 'api' => array( + 'owner' => 'openlayers', + 'api' => 'openlayers_projections', + 'minimum_version' => 1, + 'current_version' => 1, + ), + ), + 'fields' => array( + 'identifier' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Opaque identifier. Guaranteed to be unique but does not have any other meaning.' + ), + 'authority' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => 'Projection authority.', + ), + 'code' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => 'Projection code.', + ), + 'definition' => array( + 'type' => 'text', + 'not null' => TRUE, + 'description' => 'Projection definition (proj4 format).', + ), + 'projectedextentleft' => array( + 'type' => 'numeric', + 'not null' => TRUE, + 'description' => "Leftmost value in this projection's coordinates", + ), + 'projectedextentbottom' => array( + 'type' => 'numeric', + 'not null' => TRUE, + 'description' => "Bottommost value in this projection's coordinates", + ), + 'projectedextentright' => array( + 'type' => 'numeric', + 'not null' => TRUE, + 'description' => "Rightmost value in this projection's coordinates", + ), + 'projectedextenttop' => array( + 'type' => 'numeric', + 'not null' => TRUE, + 'description' => "Topmost value in this projection's coordinates", + ), + // TODO Hopefully never used + 'data' => array( + 'type' => 'text', + 'not null' => FALSE, + 'description' => 'Projection data serialized.', + 'serialize' => TRUE, + ), + ), + 'primary key' => array('identifier'), + ); + db_create_table('openlayers_projections', $openlayers_projections_schema); + + // Migrate stored layers + foreach (openlayers_layers_load() as $key => $layer) { + $projections = array(); + + // Takeover srs key that no longer exists and migrate it to a projection + if ($layer instanceof openlayers_layer_type_wms && array_key_exists('srs', $layer->data['options']) && !in_array($layer->data['options']['srs'], $layer->data['projection'])) { + $projections[] = $layer->data['options']['srs']; + } + unset($layer->data['options']['srs']); + + foreach ($layer->data['projection'] as $projection) { + if (is_string($projection)) { + $projections[] = $projection; + } + else { + throw new DrupalUpdateException("Unsupported projection type encountered in layer " . $key); + } + } + $layer->data['projection'] = $projections; + ctools_export_crud_save('openlayers_layers', $layer); + } + + // The code for the projection changed but its semantics stayed. + // Only update the display projection as servers might be in use that don't yet support the new code. + foreach (openlayers_maps() as $key => $map) { + if ($map->data['displayProjection']==='EPSG:900913') { + $map->data['displayProjection'] = 'EPSG:3857'; + } + ctools_export_crud_save('openlayers_maps', $map); + } +} \ No newline at end of file diff --git a/openlayers.module b/openlayers.module index 5af409d..757c5c6 100644 --- a/openlayers.module +++ b/openlayers.module @@ -108,7 +108,8 @@ function openlayers_include() { $variant = variable_get('openlayers_source_internal_variant', NULL); if ($variant == 'original') $variant = NULL; libraries_load('openlayers', $variant); - } else { + } + else { // Use a static variable to prevent running URL check code repeatedly. static $once; if (!isset($once)) { @@ -119,7 +120,7 @@ function openlayers_include() { // Correctly handle URLs beginning with a double backslash, see RFC 1808 Section 4 if (substr($path, 0, 2) == '//') { $http_protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; - $path = $http_protocol .':'. $path; + $path = $http_protocol . ':' . $path; } // Check for full URL and include it manually if external. @@ -211,6 +212,8 @@ function openlayers_build_map($map = array()) { function openlayers_render_map_data($map = array()) { // Run map through build process $map = openlayers_build_map($map); + $projection = openlayers_get_projection_by_identifier($map['projection']); + $map['maxExtent'] = $projection->getProjectedExtent(); $output = ''; // Given hide_empty_map flag, check if the map has any features @@ -223,15 +226,38 @@ function openlayers_render_map_data($map = array()) { } } if ($empty) { + // Abort early because there are no features to display on the map anyway return ''; } } // Return themed map if no errors found if (empty($map['errors'])) { + // In case the layer offers the same projection as the map, use this and do not provide + // projection definition to client. Otherwise rely on the client to reproject on the fly. + foreach ($map['layers'] as $layer_name => $layer) { + if(in_array($map['projection'], $map['layers'][$layer_name]['projection'])){ + $map['layers'][$layer_name]['projection'] = $map['projection']; + } else { + // Client is able to reproject any possible projection because their definitions need to be + // known to be able to set up a layer with a certain projection. Thus choice does not matter. + $layerProjectionIdentifier = $map['layers'][$layer_name]['projection'][0]; + $map['layers'][$layer_name]['projection'] = $layerProjectionIdentifier; + // Provide client with projection definition so that it can reproject + openlayers_add_js_projection_definition( + openlayers_get_projection_by_identifier($layerProjectionIdentifier) + ); + } + } + $js = array('openlayers' => array('maps' => array($map['id'] => $map))); drupal_add_js($js, 'setting'); + // Ensure projections in use are known to the client (loads Proj4js if required) + openlayers_add_js_projection_definition($projection); + $display_projection = openlayers_get_projection_by_identifier($map['displayProjection']); + openlayers_add_js_projection_definition($display_projection); + // Push map through theme function and return $output = theme('openlayers_map', array( 'map' => $map, @@ -241,6 +267,28 @@ function openlayers_render_map_data($map = array()) { } /** + * Load projection transformations in case OpenLayers does not support projections in use natively + * @param openlayers_projection $projection + */ +function openlayers_add_js_projection_definition(openlayers_projection $projection) { + $openlayers_natively_supported = array('EPSG:4326', 'EPSG:900913'); + // Only load Proj4js if projection not supported by OpenLayers anyway + if (!in_array($projection->identifier, $openlayers_natively_supported)) { + // TODO Ideally to be handled via proj4js_load_definition but its dependency handling does not work + drupal_add_js(drupal_get_path('module', 'proj4js')."/lib/proj4js/lib/proj4js-compressed.js", array( + 'type' => 'file', + 'group' => JS_LIBRARY, + 'weight' => 0 + )); + drupal_add_js('Proj4js.defs['. json_encode($projection->identifier).']='.json_encode($projection->getDefinition()).';', array( + 'type' => 'inline', + 'group' => JS_LIBRARY, + 'weight' => 1 + )); + } +} + +/** * Render a map by name * * Given a map name render it into a full map object. @@ -267,6 +315,9 @@ function openlayers_render_map($map = '') { $map_name = variable_get('openlayers_default_map', 'default'); } $map = openlayers_map_load($map_name); + if (!is_object($map)) { + throw new Exception("Failed to load map called " . $map_name); + } } return openlayers_render_map_data($map->data); @@ -278,7 +329,7 @@ function openlayers_render_map($map = '') { * @ingroup openlayers_api * @param $reset * Boolean whether to reset cache or not - * @return array + * @return openlayers_layer_type|FALSE * array of layer info */ function openlayers_get_layer_object($layer, $map = array()) { @@ -315,7 +366,7 @@ function openlayers_get_layer_object($layer, $map = array()) { * * @param $name * Layer name - * @return array + * @return openlayers_layer_type|FALSE * Layer export */ function openlayers_layer_load($name, $reset = FALSE) { @@ -387,7 +438,7 @@ function openlayers_layer_definition_check($definition) { foreach ($mandatory_fields as $field) { $missing = drupal_array_nested_key_exists($definition, $field); if (!$missing) { - drupal_set_message(t("There's a problem in the plugin definition of the layer type !type. The layer will be disabled.", array('!type' => $definition['name'])), 'warning'); + drupal_set_message(t("There's a problem in the plugin definition of the layer type !type. The layer will be disabled.", array('!type' => htmlspecialchars($definition['name']))), 'warning'); watchdog('openlayers', 'Layer !layer is unavailable because its plugin definition is incomplete.', array('!layer' => $definition['name']), @@ -422,7 +473,7 @@ function openlayers_behavior_definition_check($definition) { foreach ($mandatory_fields as $field) { $missing = drupal_array_nested_key_exists($definition, $field); if (!$missing) { - drupal_set_message(t("There's a problem in the definition of the behavior !behavior. The behavior will be disabled.", array('!behavior' => $definition['name'])), 'warning'); + drupal_set_message(t("There's a problem in the definition of the behavior !behavior. The behavior will be disabled.", array('!behavior' => htmlspecialchars($definition['name']))), 'warning'); watchdog('openlayers', 'Behavior !behavior is unavailable because its plugin definition is incomplete.', array('!behavior' => $definition['name']), @@ -693,7 +744,8 @@ function openlayers_map_load($name = '', $reset = FALSE) { if (empty($maps[$name])) { return FALSE; - } else { + } + else { $map = $maps[$name]; $map->data['map_name'] = $name; return clone $map; @@ -823,6 +875,136 @@ function openlayers_error_check_map($map, $log_errors = TRUE) { } /** + * Models a projection, a description of a coordinate system. + */ +class openlayers_projection { + /** + * @var String Opaque primary key (should not be exposed but ctools API doesn't allow for a more rigid structure) + */ + public $identifier; + + /** + * @var String proj4 definition for on-the-fly reprojections of vector data. + */ + private $definition; + + /** + * @var number Leftmost boundary where coordinate system is valid + */ + private $projectedextentleft; + /** + * @var number Bottommost boundary where coordinate system is valid + */ + private $projectedextentbottom; + /** + * @var number Rightmost boundary where coordinate system is valid + */ + private $projectedextentright; + /** + * @var number Topmost boundary where coordinate system is valid + */ + private $projectedextenttop; + + public function __construct($identifier, $definition, $projectedextentleft, $projectedextentbottom, $projectedextentright, $projectedextenttop) { + $this->identifier = $identifier; + $this->definition = $definition; + $this->projectedextentleft = $projectedextentleft; + $this->projectedextentbottom = $projectedextentbottom; + $this->projectedextentright = $projectedextentright; + $this->projectedextenttop = $projectedextenttop; + } + + /** + * @return string Textual representation for the user. + */ + public function getLocalizedMessage() { + return $this->identifier; + } + + /** + * @return array Boundaries of projection in projected coordinates + */ + public function getProjectedExtent() { + return array($this->projectedextentleft, $this->projectedextentbottom, $this->projectedextentright, $this->projectedextenttop); + } + + /** + * @return String Proj4 style definition + */ + public function getDefinition() { + return $this->definition; + } +} + +/** + * @param stdClass $record Object as returned by ctools_export_crud_load for type openlayers_projections + * @return openlayers_projection + */ +function openlayers_projection_from_record($record) { + return new openlayers_projection($record->identifier, $record->definition, + $record->projectedextentleft, $record->projectedextentbottom, $record->projectedextentright, $record->projectedextenttop); +} + +/** + * @param String $authority Organization who defined the code + * @param String $code Projection identifier + * @return openlayers_projection + */ +function openlayers_get_projection($authority, $code) { + static $projections; + if (!isset($projections)) { + $projections = array(); + } + $identifier = $authority . ':' . $code; + if (!array_key_exists($identifier, $projections)) { + $projections[$identifier] = openlayers_get_projection_by_identifier($identifier); + } + return $projections[$identifier]; +} + +/** + * @param String $identifier Opaque identifier + * @return openlayers_projection + */ +function openlayers_get_projection_by_identifier($identifier) { + $record = ctools_export_crud_load('openlayers_projections', $identifier); + if ($record===NULL) { + throw new Exception("Projection $identifier requested but not supported"); + } + return openlayers_projection_from_record($record); +} + +/** + * @return array + */ +function openlayers_get_all_projections() { + $projections = array(); + + foreach (ctools_export_crud_load_all('openlayers_projections') as $record) { + $projections[] = openlayers_projection_from_record($record); + } + + return $projections; +} + +/** + * Rerenders form part “Layers & Styles” when map projections changes + * @param array $form + * @param array $form_state + * @return array Form part to rerender + */ +function openlayers_map_layerlist(&$form, &$form_state) { + module_load_include('inc', 'openlayers_ui', '/include/openlayers_ui.theme'); + + // Remove group property as Drupal otherwise returns NULL instead of the rendered form part + unset($form['layerstyles']['#group']); + // Force a diffent, fixed identifier so that the form part can reliably replaced + $form['layerstyles']['#id']= 'edit-layerstyles'; + // Rerender the form part + return $form['layerstyles']; +} + +/** * Get extent given projection * * Returns standard world-max-extents for common projections. @@ -831,19 +1013,16 @@ function openlayers_error_check_map($map, $log_errors = TRUE) { * * @ingroup openlayers_api * + * @param $authority + * String Organization code, such as EPSG. * @param $projection - * String of the projection value. Currently - * supports 900913, 4326. + * String of the projection value, such as 4326. * @return * Array of maxExtent in OpenLayers toArray() format. */ -function openlayers_get_extent($projection) { - switch ($projection) { - case 'EPSG:900913': - return array(-20037508, -20037508, 20037508, 20037508); - case 'EPSG:4326': - return array(-180, -90, 180, 90); - } +function openlayers_get_extent($authority, $projection) { + // Use projected extend from openlayers_projection instead but keep this function for now as it's marked as API + return openlayers_get_projection($authority, $projection)->getProjectedExtent(); } /** @@ -857,7 +1036,7 @@ function openlayers_get_extent($projection) { * @ingroup openlayers_api * * @param $projection - * String specifying which projection this should take, like 900913. + * String specifying which projection this should take, like EPSG:900913. * @param $zoom_start * Integer of first zoom level, default 0. * @param $zoom_end @@ -866,6 +1045,7 @@ function openlayers_get_extent($projection) { * Array of resolutions. */ function openlayers_get_resolutions($projection, $zoom_start = 0, $zoom_end = FALSE) { + // TODO This is conceptually flawed and should not exist. Resolutions depend on the server in use in combination with the tile size. switch ($projection) { case 'EPSG:900913': // 16 zoom levels, taken from @@ -984,7 +1164,7 @@ class openlayers_layer_type { /** * Set configuration and store map. * - * @param $layer + * @param stdClass $layer * Configuration object with the options for the layer. * @param $map * Array with the current map. @@ -995,6 +1175,7 @@ class openlayers_layer_type { $this->{$k} = $layer->{$k}; } } + // Extend options with the defaults. $this->data += $this->options_init(); @@ -1002,6 +1183,21 @@ class openlayers_layer_type { } /** + * @return array + * List of all projections that are supported by the layer. + */ + public function getProjections() { + $projections = array(); + // TODO Ignore incomplete data until cause is fixed (projection for every layer set during migration) + if (isset($this->data['projection'])) { + foreach ($this->data['projection'] as $projection) { + $projections[] = openlayers_get_projection_by_identifier($projection); + } + } + return $projections; + } + + /** * Provides the default options for the layer. * * @return @@ -1011,9 +1207,10 @@ class openlayers_layer_type { return array( 'layer_type' => get_class($this), 'isBaseLayer' => TRUE, + // TODO: Remove hard-coded resolutions 'projection' => array('EPSG:900913'), 'serverResolutions' => openlayers_get_resolutions('EPSG:900913'), - 'maxExtent' => openlayers_get_extent('EPSG:900913'), + 'maxExtent' => openlayers_get_extent('EPSG', '900913'), 'resolutions' => openlayers_get_resolutions('EPSG:900913'), 'base_url' => NULL, 'transitionEffect' => 'resize', @@ -1028,18 +1225,19 @@ class openlayers_layer_type { * Array with form elements. */ function options_form($default = array()) { + $allProjectionOptions = array(); + foreach (openlayers_get_all_projections() as $projection) { + $allProjectionOptions[$projection->identifier] = $projection->getLocalizedMessage(); + } return array( 'projection' => array( '#type' => 'select', '#title' => t('Projection'), '#multiple' => TRUE, - '#options' => array( - 'EPSG:900913' => 'EPSG:900913', - 'EPSG:4326' => 'EPSG:4326' - ), + '#options' => $allProjectionOptions, '#default_value' => isset($default->data['projection']) ? $default->data['projection'] : - 'EPSG:900913' + openlayers_get_projection('EPSG', '3857')->identifier ), 'isBaseLayer' => array( '#type' => 'checkbox', @@ -1102,6 +1300,9 @@ function openlayers_ctools_plugin_api($module, $api) { case 'openlayers_layers': return array('version' => 1); + case 'openlayers_projections': + return array('version' => 1); + case 'openlayers_styles': return array('version' => 1); @@ -1127,6 +1328,15 @@ function openlayers_openlayers_layers() { } /** + * Gather list of default projections + * @return array + */ +function openlayers_openlayers_projections() { + module_load_include('inc', 'openlayers', 'includes/openlayers.projections'); + return _openlayers_openlayers_projections(); +} + +/** * Implements hook_openlayers_styles(). */ function openlayers_openlayers_styles() { diff --git a/plugins/behaviors/openlayers_behavior_boxselect.js b/plugins/behaviors/openlayers_behavior_boxselect.js index 38b44e0..4317f25 100644 --- a/plugins/behaviors/openlayers_behavior_boxselect.js +++ b/plugins/behaviors/openlayers_behavior_boxselect.js @@ -2,7 +2,7 @@ * @file * JS Implementation of OpenLayers behavior. */ - + // Not the best, but gets around scoping; var selections_layer; @@ -17,14 +17,14 @@ var selections_layer; function setRestrictedExtent(box) { var bounding_box = box.geometry.getBounds().toBBOX(); $('#edit-center-restrict-restrictedextent').val(bounding_box); - + // Check box if (!($('#restrictextent').attr('checked'))) { $('#restrictextent') .attr('checked', 'checked') .trigger('change'); } - + // Remove any other features. for (var i = 0; i < selections_layer.features.length; i++) { if (selections_layer.features[i] != box) { diff --git a/plugins/behaviors/openlayers_behavior_drawfeatures.inc b/plugins/behaviors/openlayers_behavior_drawfeatures.inc index 6fb1450..97e7ccf 100644 --- a/plugins/behaviors/openlayers_behavior_drawfeatures.inc +++ b/plugins/behaviors/openlayers_behavior_drawfeatures.inc @@ -70,7 +70,7 @@ class openlayers_behavior_drawfeatures extends openlayers_behavior { * Render. */ function render(&$map) { - drupal_add_css(drupal_get_path('module', 'openlayers') . + drupal_add_css(drupal_get_path('module', 'openlayers') . '/plugins/behaviors/openlayers_behavior_drawfeatures.css'); drupal_add_js(drupal_get_path('module', 'openlayers') . '/plugins/behaviors/openlayers_behavior_drawfeatures.js'); diff --git a/plugins/behaviors/openlayers_behavior_graticule.inc b/plugins/behaviors/openlayers_behavior_graticule.inc index 2273032..634433a 100644 --- a/plugins/behaviors/openlayers_behavior_graticule.inc +++ b/plugins/behaviors/openlayers_behavior_graticule.inc @@ -57,7 +57,7 @@ class openlayers_behavior_graticule extends openlayers_behavior { 'numPoints' => array( '#type' => 'textfield', '#title' => t('Number of points'), - '#description' => t('The number of points to use in each graticule line. Higher numbers result in a smoother curve for projected maps '), + '#description' => t('The number of points to use in each graticule line. Higher numbers result in a smoother curve for projected maps.'), '#default_value' => (isset($defaults['numPoints'])) ? $defaults['numPoints'] : 50, ), 'targetSize' => array( diff --git a/plugins/behaviors/openlayers_behavior_layerswitcher.inc b/plugins/behaviors/openlayers_behavior_layerswitcher.inc index fef0864..9bac055 100644 --- a/plugins/behaviors/openlayers_behavior_layerswitcher.inc +++ b/plugins/behaviors/openlayers_behavior_layerswitcher.inc @@ -93,7 +93,7 @@ class openlayers_behavior_layerswitcher extends openlayers_behavior { if ($this->options['sortBaseLayer'] == 'ASC' || $this->options['sortBaseLayer'] == 'DESC') { uasort($map['layers'], 'drupal_sort_title'); if ($this->options['sortBaseLayer'] == 'ASC') { - $map['layers'] = array_reverse ($map['layers'], TRUE); + $map['layers'] = array_reverse($map['layers'], TRUE); } } diff --git a/plugins/behaviors/openlayers_behavior_tooltip.js b/plugins/behaviors/openlayers_behavior_tooltip.js index 06259cc..3ee4d24 100644 --- a/plugins/behaviors/openlayers_behavior_tooltip.js +++ b/plugins/behaviors/openlayers_behavior_tooltip.js @@ -46,7 +46,7 @@ Drupal.openlayers.addBehavior('openlayers_behavior_tooltip', function (data, opt } } } - + // if only 1 layer exists, do not add as an array. Kind of a // hack, see https://drupal.org/node/1393460 if (layers.length == 1) { diff --git a/plugins/layer_types/openlayers_layer_type_kml.inc b/plugins/layer_types/openlayers_layer_type_kml.inc index d853dd2..557fb72 100644 --- a/plugins/layer_types/openlayers_layer_type_kml.inc +++ b/plugins/layer_types/openlayers_layer_type_kml.inc @@ -34,7 +34,7 @@ class openlayers_layer_type_kml extends openlayers_layer_type { 'projection' => array('EPSG:4326'), 'resolutions' => openlayers_get_resolutions('EPSG:4326'), 'serverResolutions' => openlayers_get_resolutions('EPSG:4326'), - 'maxExtent' => openlayers_get_extent('EPSG:4326'), + 'maxExtent' => openlayers_get_extent('EPSG', '4326'), 'isBaseLayer' => FALSE, 'vector' => TRUE, 'formatOptions' => array( @@ -42,7 +42,7 @@ class openlayers_layer_type_kml extends openlayers_layer_type { 'extractAttributes' => TRUE, 'extractTracks' => FALSE, ), - 'file' =>'', + 'file' => '', ) + parent::options_init(); } @@ -138,13 +138,15 @@ class openlayers_layer_type_kml extends openlayers_layer_type { $method = $form_state['data']['method']; if (empty($form_state['data'][$method])) { - form_set_error($form_state['data']['layer_type'].']['.$method, 'The field cannot be empty'); + form_set_error($form_state['data']['layer_type'] . '][' . $method, 'The field cannot be empty'); } if ($method == 'file') { if ($file = file_load($form_state['data']['file'])) { - } else { - form_set_error($form_state['data']['layer_type'].']['.$method, 'Cannot access the file.'); + // TODO Why nothing here? + } + else { + form_set_error($form_state['data']['layer_type'] . '][' . $method, 'Cannot access the file.'); } } diff --git a/plugins/layer_types/openlayers_layer_type_tms.inc b/plugins/layer_types/openlayers_layer_type_tms.inc index 7ed7059..f138a7c 100644 --- a/plugins/layer_types/openlayers_layer_type_tms.inc +++ b/plugins/layer_types/openlayers_layer_type_tms.inc @@ -32,7 +32,7 @@ class openlayers_layer_type_tms extends openlayers_layer_type { return array( 'layer_handler' => 'tms', 'type' => 'png', - 'wrapDateLine' => false, + 'wrapDateLine' => FALSE, ) + parent::options_init(); } diff --git a/plugins/layer_types/openlayers_layer_type_wms.inc b/plugins/layer_types/openlayers_layer_type_wms.inc index 8967fc4..2bf0b89 100644 --- a/plugins/layer_types/openlayers_layer_type_wms.inc +++ b/plugins/layer_types/openlayers_layer_type_wms.inc @@ -29,9 +29,16 @@ class openlayers_layer_type_wms extends openlayers_layer_type { * Provide initial values for options. */ function options_init() { - return array( + $options = array( 'layer_handler' => 'wms', ) + parent::options_init(); + + // Discard hard-codes resolution because the client can calculate them usually + unset($options['serverResolutions']); + unset($options['maxExtent']); + unset($options['resolutions']); + + return $options; } /** @@ -75,15 +82,6 @@ class openlayers_layer_type_wms extends openlayers_layer_type { ) ), 'options' => array( - 'srs' => array( - '#type' => 'select', - '#title' => t('Projection'), - '#options' => array( - 'EPSG:900913' => 'EPSG:900913', - 'EPSG:4326' => 'EPSG:4326'), - '#default_value' => isset($this->data['options']['srs']) ? - $this->data['options']['srs'] : 'EPSG:900913' - ), 'TRANSPARENT' => array( '#type' => 'checkbox', '#default_value' => isset($this->data['options']['TRANSPARENT']) ? @@ -113,11 +111,11 @@ class openlayers_layer_type_wms extends openlayers_layer_type { $this->data['options']['format'] : 'image/png' ), 'layers' => array( - '#type' => 'textarea', + '#type' => 'textfield', '#title' => t('Layers'), - '#default_value' => !empty($this->data['options']['layers']) ? - implode("\r\n", $this->data['options']['layers']) : '', - '#description' => t('Specifies which layers to show. One per line.') + '#default_value' => isset($this->data['options']['layers']) ? + $this->data['options']['layers'] : '', + '#description' => t('Specifies which layers to show. Multiple layers can be given by separating them with a comma.') ), 'styles' => array( '#type' => 'textfield', @@ -130,13 +128,6 @@ class openlayers_layer_type_wms extends openlayers_layer_type { } /** - * hook_validate() - */ - function options_form_validate($form, &$form_state) { - $form_state['data']['options']['layers'] = explode("\r\n", $form_state['data']['options']['layers']); - } - - /** * Render. */ function render(&$map) { diff --git a/plugins/layer_types/openlayers_layer_type_wms.js b/plugins/layer_types/openlayers_layer_type_wms.js index 86865b2..3185609 100644 --- a/plugins/layer_types/openlayers_layer_type_wms.js +++ b/plugins/layer_types/openlayers_layer_type_wms.js @@ -19,8 +19,24 @@ Drupal.openlayers.layer.wms = function(title, map, options) { } options.params.drupalID = options.drupalID; - var layer = new OpenLayers.Layer.WMS(title, - options.base_url, options.options, options.params); + + // Set isBaseLayer explicitly so that OpenLayers does not guess from transparency + options.params.isBaseLayer = Boolean(options.isBaseLayer); + + // Convert to representation that match with WMS specification + var optionsClone = jQuery.extend(true, {}, options.options); + if(optionsClone.hasOwnProperty("TRANSPARENT") && optionsClone.TRANSPARENT===0){ + optionsClone.TRANSPARENT = "FALSE"; + } + if(optionsClone.hasOwnProperty("TRANSPARENT") && optionsClone.TRANSPARENT===1){ + optionsClone.TRANSPARENT = "FALSE"; + } + + var paramsClone = jQuery.extend(true, {}, options.params); + // OpenLayers can calculate the resolutions usually if provided with the number of zoom levels and tile sizes + paramsClone.numZoomLevels=18; + + var layer = new OpenLayers.Layer.WMS(title, options.base_url, optionsClone, paramsClone); layer.styleMap = styleMap; return layer; }; diff --git a/tests/includes/openlayers_test.maps.inc b/tests/includes/openlayers_test.maps.inc index 39e316f..33b33be 100644 --- a/tests/includes/openlayers_test.maps.inc +++ b/tests/includes/openlayers_test.maps.inc @@ -32,7 +32,7 @@ function _openlayers_test_openlayers_maps() { ), 'options' => array( 'displayProjection' => 'EPSG:4326', - 'maxExtent' => openlayers_get_extent('EPSG:4326'), + 'maxExtent' => openlayers_get_extent('EPSG', '4326'), ), 'behaviors' => array( 'openlayers_behavior_panzoombar' => array(),