aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/Map
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/Map')
-rw-r--r--perllib/FixMyStreet/Map/BathNES.pm20
-rw-r--r--perllib/FixMyStreet/Map/Bristol.pm54
-rw-r--r--perllib/FixMyStreet/Map/Bromley.pm12
-rw-r--r--perllib/FixMyStreet/Map/Buckinghamshire.pm41
-rw-r--r--perllib/FixMyStreet/Map/CheshireEast.pm70
-rw-r--r--perllib/FixMyStreet/Map/GoogleOL.pm2
-rw-r--r--perllib/FixMyStreet/Map/HighwaysEngland.pm8
-rw-r--r--perllib/FixMyStreet/Map/Hounslow.pm63
-rw-r--r--perllib/FixMyStreet/Map/IsleOfWight.pm63
-rw-r--r--perllib/FixMyStreet/Map/Lincolnshire.pm21
-rw-r--r--perllib/FixMyStreet/Map/MasterMap.pm40
-rw-r--r--perllib/FixMyStreet/Map/Northamptonshire.pm85
-rw-r--r--perllib/FixMyStreet/Map/OSM.pm40
-rw-r--r--perllib/FixMyStreet/Map/UKCouncilWMTS.pm55
-rw-r--r--perllib/FixMyStreet/Map/WMSBase.pm151
-rw-r--r--perllib/FixMyStreet/Map/WMTSBase.pm195
-rw-r--r--perllib/FixMyStreet/Map/WMXBase.pm199
17 files changed, 819 insertions, 300 deletions
diff --git a/perllib/FixMyStreet/Map/BathNES.pm b/perllib/FixMyStreet/Map/BathNES.pm
deleted file mode 100644
index 45261a625..000000000
--- a/perllib/FixMyStreet/Map/BathNES.pm
+++ /dev/null
@@ -1,20 +0,0 @@
-# FixMyStreet:Map::BathNES
-# More JavaScript, for street assets
-
-package FixMyStreet::Map::BathNES;
-use base 'FixMyStreet::Map::OSM';
-
-use strict;
-
-sub map_javascript { [
- '/vendor/OpenLayers/OpenLayers.wfs.js',
- '/vendor/OpenLayers.Projection.OrdnanceSurvey.js',
- '/js/map-OpenLayers.js',
- '/js/map-OpenStreetMap.js',
- '/cobrands/fixmystreet-uk-councils/roadworks.js',
- '/cobrands/fixmystreet/assets.js',
- '/cobrands/bathnes/js.js',
- '/cobrands/bathnes/assets.js',
-] }
-
-1;
diff --git a/perllib/FixMyStreet/Map/Bristol.pm b/perllib/FixMyStreet/Map/Bristol.pm
index 99bdd26d7..70240a991 100644
--- a/perllib/FixMyStreet/Map/Bristol.pm
+++ b/perllib/FixMyStreet/Map/Bristol.pm
@@ -2,39 +2,15 @@
# Bristol use their own tiles on their cobrand
package FixMyStreet::Map::Bristol;
-use base 'FixMyStreet::Map::WMTSBase';
+use base 'FixMyStreet::Map::UKCouncilWMTS';
use strict;
-sub zoom_parameters {
- my $self = shift;
- my $params = {
- zoom_levels => scalar $self->scales,
- default_zoom => 5,
- min_zoom_level => 0,
- id_offset => 0,
- };
- return $params;
-}
+sub default_zoom { 5; }
-sub tile_parameters {
- my $self = shift;
- my $params = {
- urls => [ 'https://maps.bristol.gov.uk/arcgis/rest/services/base/2015_BCC_96dpi/MapServer/WMTS/tile' ],
- layer_names => [ '2015_BCC_96dpi' ],
- wmts_version => '1.0.0',
- layer_style => 'default',
- matrix_set => 'default028mm',
- suffix => '.png', # appended to tile URLs
- size => 256, # pixels
- dpi => 96,
- inches_per_unit => 39.3701, # BNG uses metres
- projection => 'EPSG:27700',
- origin_x => -5220400.0,
- origin_y => 4470200.0,
- };
- return $params;
-}
+sub urls { [ 'https://maps.bristol.gov.uk/arcgis/rest/services/base/2019_Q2_BCC_96dpi/MapServer/WMTS/tile' ] }
+
+sub layer_names { [ '2019_Q2_BCC_96dpi' ] }
sub scales {
my $self = shift;
@@ -56,29 +32,11 @@ sub copyright {
return '© BCC';
}
-sub map_template { 'bristol' }
-
sub map_javascript { [
- '/vendor/OpenLayers/OpenLayers.bristol.js',
+ '/vendor/OpenLayers/OpenLayers.wmts.js',
'/js/map-OpenLayers.js',
'/js/map-wmts-base.js',
'/js/map-wmts-bristol.js',
- '/cobrands/fixmystreet/assets.js',
- '/cobrands/bristol/assets.js',
] }
-# Reproject a WGS84 lat/lon into BNG easting/northing
-sub reproject_from_latlon($$$) {
- my ($self, $lat, $lon) = @_;
- my ($x, $y) = Utils::convert_latlon_to_en($lat, $lon);
- return ($x, $y);
-}
-
-# Reproject a BNG easting/northing into WGS84 lat/lon
-sub reproject_to_latlon($$$) {
- my ($self, $x, $y) = @_;
- my ($lat, $lon) = Utils::convert_en_to_latlon($x, $y);
- return ($lat, $lon);
-}
-
1;
diff --git a/perllib/FixMyStreet/Map/Bromley.pm b/perllib/FixMyStreet/Map/Bromley.pm
index cd50cc1d1..518382fc0 100644
--- a/perllib/FixMyStreet/Map/Bromley.pm
+++ b/perllib/FixMyStreet/Map/Bromley.pm
@@ -9,18 +9,8 @@ use base 'FixMyStreet::Map::FMS';
use strict;
-sub map_javascript { [
- '/vendor/OpenLayers/OpenLayers.wfs.js',
- '/js/map-OpenLayers.js',
- '/js/map-bing-ol.js',
- '/js/map-fms.js',
- '/cobrands/fixmystreet/assets.js',
- '/cobrands/bromley/map.js',
- '/cobrands/bromley/assets.js',
-] }
-
sub map_tile_base {
- '-', "https://%sfix.bromley.gov.uk/tilma/%d/%d/%d.png";
+ '-', "//%stilma.mysociety.org/bromley/%d/%d/%d.png";
}
1;
diff --git a/perllib/FixMyStreet/Map/Buckinghamshire.pm b/perllib/FixMyStreet/Map/Buckinghamshire.pm
index 10ee2a080..4c1b8e9a0 100644
--- a/perllib/FixMyStreet/Map/Buckinghamshire.pm
+++ b/perllib/FixMyStreet/Map/Buckinghamshire.pm
@@ -2,19 +2,44 @@
# More JavaScript, for street assets
package FixMyStreet::Map::Buckinghamshire;
-use base 'FixMyStreet::Map::OSM';
+use base 'FixMyStreet::Map::UKCouncilWMTS';
use strict;
+sub default_zoom { 8; }
+
+sub urls { [ 'https://maps.buckscc.gov.uk/arcgis/rest/services/Bucks_Basemapping/MapServer/WMTS/tile' ] }
+
+sub layer_names { [ 'Bucks_Basemapping' ] }
+
+sub scales {
+ my $self = shift;
+ my @scales = (
+ '1000000',
+ '500000',
+ '250000',
+ '125000',
+ '64000',
+ '32000',
+ '16000',
+ '8000',
+ '4000',
+ '2000',
+ '1000',
+ );
+ return @scales;
+
+}
+
+sub copyright {
+ return '© BCC';
+}
+
sub map_javascript { [
- '/vendor/OpenLayers/OpenLayers.wfs.js',
- '/vendor/OpenLayers.Projection.OrdnanceSurvey.js',
+ '/vendor/OpenLayers/OpenLayers.wmts.js',
'/js/map-OpenLayers.js',
- '/js/map-OpenStreetMap.js',
- '/cobrands/fixmystreet-uk-councils/roadworks.js',
- '/cobrands/fixmystreet/assets.js',
- '/cobrands/buckinghamshire/js.js',
- '/cobrands/buckinghamshire/assets.js',
+ '/js/map-wmts-base.js',
+ '/js/map-wmts-buckinghamshire.js',
] }
1;
diff --git a/perllib/FixMyStreet/Map/CheshireEast.pm b/perllib/FixMyStreet/Map/CheshireEast.pm
new file mode 100644
index 000000000..4e59f2593
--- /dev/null
+++ b/perllib/FixMyStreet/Map/CheshireEast.pm
@@ -0,0 +1,70 @@
+package FixMyStreet::Map::CheshireEast;
+use base 'FixMyStreet::Map::OSM';
+
+use strict;
+use Utils;
+
+use constant MIN_ZOOM_LEVEL => 7;
+
+sub map_javascript { [
+ '/vendor/OpenLayers/OpenLayers.wfs.js',
+ '/js/map-OpenLayers.js',
+ '/js/map-cheshireeast.js',
+] }
+
+sub tile_parameters { {
+ origin_x => -3276800,
+ origin_y => 3276800,
+} }
+
+sub resolutions { (
+ 1792.003584007169,
+ 896.0017920035843,
+ 448.0008960017922,
+ 224.0004480008961,
+ 112.000224000448,
+ 56.000112000224014,
+ 28.000056000111993,
+ 14.000028000056004,
+ 7.000014000028002,
+ 2.8000056000112004,
+ 1.4000028000056002,
+ 0.7000014000028001,
+ 0.35000070000140004,
+ 0.14000028000056003,
+) }
+
+my $url = 'https://maps-cache.cheshiresharedservices.gov.uk/maps/?wmts/CE_OS_AllBasemaps_COLOUR/oscce_grid/%d/%d/%d.jpeg&KEY=3a3f5c60eca1404ea114e6941c9d3895';
+
+sub map_tiles {
+ my ( $self, %params ) = @_;
+ my ( $x, $y, $z ) = ( $params{x_tile}, $params{y_tile}, $params{zoom_act} );
+ return [
+ sprintf($url, $z, $x-1, $y-1),
+ sprintf($url, $z, $x, $y-1),
+ sprintf($url, $z, $x-1, $y),
+ sprintf($url, $z, $x, $y),
+ ];
+}
+
+sub latlon_to_tile($$$$) {
+ my ($self, $lat, $lon, $zoom) = @_;
+ my ($x, $y) = eval { Utils::convert_latlon_to_en($lat, $lon) };
+ my $tile_params = $self->tile_parameters;
+ my $res = ($self->resolutions)[$zoom];
+ my $fx = ( $x - $tile_params->{origin_x} ) / ($res * 256);
+ my $fy = ( $tile_params->{origin_y} - $y ) / ($res * 256);
+ return ( $fx, $fy );
+}
+
+sub tile_to_latlon {
+ my ($self, $fx, $fy, $zoom) = @_;
+ my $tile_params = $self->tile_parameters;
+ my $res = ($self->resolutions)[$zoom];
+ my $x = $fx * $res * 256 + $tile_params->{origin_x};
+ my $y = $tile_params->{origin_y} - $fy * $res * 256;
+ my ($lat, $lon) = Utils::convert_en_to_latlon($x, $y);
+ return ( $lat, $lon );
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/GoogleOL.pm b/perllib/FixMyStreet/Map/GoogleOL.pm
index 44d0e77e7..7049b27d4 100644
--- a/perllib/FixMyStreet/Map/GoogleOL.pm
+++ b/perllib/FixMyStreet/Map/GoogleOL.pm
@@ -16,7 +16,7 @@ sub map_template { 'google-ol' }
sub map_javascript {
my $google_maps_url = "https://maps.googleapis.com/maps/api/js?v=3";
my $key = FixMyStreet->config('GOOGLE_MAPS_API_KEY');
- $google_maps_url .= "&key=$key" if $key;
+ $google_maps_url .= "&key=$key" if $key;
[
$google_maps_url,
'/vendor/OpenLayers/OpenLayers.google.js',
diff --git a/perllib/FixMyStreet/Map/HighwaysEngland.pm b/perllib/FixMyStreet/Map/HighwaysEngland.pm
new file mode 100644
index 000000000..de44240a4
--- /dev/null
+++ b/perllib/FixMyStreet/Map/HighwaysEngland.pm
@@ -0,0 +1,8 @@
+package FixMyStreet::Map::HighwaysEngland;
+use base 'FixMyStreet::Map::FMS';
+
+use strict;
+
+use constant MIN_ZOOM_LEVEL => 12;
+
+1;
diff --git a/perllib/FixMyStreet/Map/Hounslow.pm b/perllib/FixMyStreet/Map/Hounslow.pm
new file mode 100644
index 000000000..d66188a83
--- /dev/null
+++ b/perllib/FixMyStreet/Map/Hounslow.pm
@@ -0,0 +1,63 @@
+# FixMyStreet:Map::Hounslow
+# Hounslow use their own tiles on their cobrand
+
+package FixMyStreet::Map::Hounslow;
+use base 'FixMyStreet::Map::UKCouncilWMTS';
+
+use strict;
+
+sub default_zoom { 5; }
+
+sub urls { [ 'https://gis.ringway.co.uk/server/rest/services/Hosted/HounslowOSBasemap/MapServer/WMTS/tile' ] }
+
+sub layer_names { [ 'Hosted_HounslowOSBasemap' ] }
+
+sub scales {
+ my $self = shift;
+ my @scales = (
+ # The first 5 levels don't load and are really zoomed-out, so
+ # they're not included here.
+ # '600000',
+ # '500000',
+ # '400000',
+ # '300000',
+ # '200000',
+ '100000',
+ '75000',
+ '50000',
+ '25000',
+ '10000',
+ '8000',
+ '6000',
+ '4000',
+ '2000',
+ '1000',
+ '400',
+ );
+ return @scales;
+}
+
+sub zoom_parameters {
+ my $self = shift;
+ my $params = {
+ zoom_levels => scalar $self->scales,
+ default_zoom => $self->default_zoom,
+ min_zoom_level => 0,
+ id_offset => 5, # see note above about zoom layers we've skipped
+ };
+ return $params;
+}
+
+sub copyright {
+ return 'Contains Ordnance Survey data &copy; Crown copyright and database rights 2019 OS. Use of this data is subject to <a href="/about/mapterms">terms and conditions</a>.';
+}
+
+
+sub map_javascript { [
+ '/vendor/OpenLayers/OpenLayers.wmts.js',
+ '/js/map-OpenLayers.js',
+ '/js/map-wmts-base.js',
+ '/js/map-wmts-hounslow.js',
+] }
+
+1;
diff --git a/perllib/FixMyStreet/Map/IsleOfWight.pm b/perllib/FixMyStreet/Map/IsleOfWight.pm
new file mode 100644
index 000000000..2316e2939
--- /dev/null
+++ b/perllib/FixMyStreet/Map/IsleOfWight.pm
@@ -0,0 +1,63 @@
+# FixMyStreet:Map::IsleOfWight
+# IsleOfWight use their own tiles on their cobrand
+
+package FixMyStreet::Map::IsleOfWight;
+use base 'FixMyStreet::Map::UKCouncilWMTS';
+
+use strict;
+
+sub default_zoom { 7; }
+
+sub urls { [ 'https://gis.ringway.co.uk/server/rest/services/Hosted/IOW_OS/MapServer/WMTS/tile' ] }
+
+sub layer_names { [ 'Hosted_IOW_OS' ] }
+
+sub scales {
+ my $self = shift;
+ my @scales = (
+ # The first 5 levels don't load and are really zoomed-out, so
+ # they're not included here.
+ # '600000',
+ # '500000',
+ # '400000',
+ # '300000',
+ # '200000',
+ '100000',
+ '75000',
+ '50000',
+ '25000',
+ '10000',
+ '8000',
+ '6000',
+ '4000',
+ '2000',
+ '1000',
+ '400',
+ );
+ return @scales;
+}
+
+sub zoom_parameters {
+ my $self = shift;
+ my $params = {
+ zoom_levels => scalar $self->scales,
+ default_zoom => $self->default_zoom,
+ min_zoom_level => 0,
+ id_offset => 5, # see note above about zoom layers we've skipped
+ };
+ return $params;
+}
+
+sub copyright {
+ return 'Contains Ordnance Survey data &copy; Crown copyright and database rights 2019 OS 100019229. Use of this data is subject to <a href="/about/mapterms">terms and conditions</a>.';
+}
+
+
+sub map_javascript { [
+ '/vendor/OpenLayers/OpenLayers.wmts.js',
+ '/js/map-OpenLayers.js',
+ '/js/map-wmts-base.js',
+ '/js/map-wmts-isleofwight.js',
+] }
+
+1;
diff --git a/perllib/FixMyStreet/Map/Lincolnshire.pm b/perllib/FixMyStreet/Map/Lincolnshire.pm
deleted file mode 100644
index 7dbfe5d8e..000000000
--- a/perllib/FixMyStreet/Map/Lincolnshire.pm
+++ /dev/null
@@ -1,21 +0,0 @@
-# FixMyStreet:Map::Lincolnshire
-# More JavaScript, for street assets
-
-package FixMyStreet::Map::Lincolnshire;
-use base 'FixMyStreet::Map::FMS';
-
-use strict;
-
-sub map_javascript { [
- '/vendor/OpenLayers/OpenLayers.wfs.js',
- '/vendor/OpenLayers.Projection.OrdnanceSurvey.js',
- '/js/map-OpenLayers.js',
- '/js/map-bing-ol.js',
- '/js/map-fms.js',
- '/cobrands/fixmystreet-uk-councils/roadworks.js',
- '/cobrands/fixmystreet/assets.js',
- '/cobrands/lincolnshire/roadworks.js',
- '/cobrands/lincolnshire/assets.js',
-] }
-
-1;
diff --git a/perllib/FixMyStreet/Map/MasterMap.pm b/perllib/FixMyStreet/Map/MasterMap.pm
new file mode 100644
index 000000000..d66234bbf
--- /dev/null
+++ b/perllib/FixMyStreet/Map/MasterMap.pm
@@ -0,0 +1,40 @@
+# FixMyStreet:Map::MasterMap
+#
+# A combination of FMS OS maps and our own tiles
+
+package FixMyStreet::Map::MasterMap;
+use base 'FixMyStreet::Map::FMS';
+
+use strict;
+
+use constant ZOOM_LEVELS => 7;
+
+sub map_template { 'fms' }
+
+sub map_javascript { [
+ '/vendor/OpenLayers/OpenLayers.wfs.js',
+ '/js/map-OpenLayers.js',
+ '/js/map-bing-ol.js',
+ '/js/map-fms.js',
+ '/js/map-mastermap.js',
+] }
+
+sub map_tiles {
+ my ( $self, %params ) = @_;
+ my ( $x, $y, $z ) = ( $params{x_tile}, $params{y_tile}, $params{zoom_act} );
+ if ($z >= 17) {
+ my $layer = FixMyStreet->config('STAGING_SITE') ? 'mastermap-staging' : 'mastermap';
+ my $base = "//%stilma.mysociety.org/$layer/%d/%d/%d.png";
+ return [
+ sprintf($base, 'a.', $z, $x-1, $y-1),
+ sprintf($base, 'b.', $z, $x, $y-1),
+ sprintf($base, 'c.', $z, $x-1, $y),
+ sprintf($base, '', $z, $x, $y),
+ ];
+ } else {
+ return $self->SUPER::map_tiles(%params);
+ }
+}
+
+1;
+
diff --git a/perllib/FixMyStreet/Map/Northamptonshire.pm b/perllib/FixMyStreet/Map/Northamptonshire.pm
new file mode 100644
index 000000000..81f7e45eb
--- /dev/null
+++ b/perllib/FixMyStreet/Map/Northamptonshire.pm
@@ -0,0 +1,85 @@
+package FixMyStreet::Map::Northamptonshire;
+use base 'FixMyStreet::Map::WMSBase';
+
+use strict;
+
+sub default_zoom { 8; }
+
+sub urls { [ 'https://maps.northamptonshire.gov.uk/interactivemappingwms/getmap.ashx' ] }
+
+sub layer_names{ [ 'BaseMap' ] }
+
+sub copyright {
+ return '&copy; NCC';
+}
+
+sub scales {
+ my $self = shift;
+ my @scales = (
+ '300000',
+ '200000',
+ '100000',
+ '75000',
+ '50000',
+ '25000',
+ '10000',
+ '8000',
+ '6000',
+ '4000',
+ '2000',
+ '1000',
+ '400',
+ );
+ return @scales;
+}
+sub tile_parameters {
+ my $self = shift;
+ my $params = {
+ urls => $self->urls,
+ layer_names => $self->layer_names,
+ wms_version => '1.1.1',
+ layer_style => 'default',
+ format => 'image/png', # appended to tile URLs
+ size => 256, # pixels
+ dpi => 96,
+ inches_per_unit => 39.3701,
+ projection => 'EPSG:27700',
+ };
+ return $params;
+}
+
+sub zoom_parameters {
+ my $self = shift;
+ my $params = {
+ zoom_levels => scalar $self->scales,
+ default_zoom => 7,
+ min_zoom_level => 1,
+ id_offset => 0,
+ };
+ return $params;
+}
+
+# Reproject a WGS84 lat/lon into BNG easting/northing
+sub reproject_from_latlon($$$) {
+ my ($self, $lat, $lon) = @_;
+ # do not try to reproject if we have no co-ordindates as convert breaks
+ return (0.0, 0.0) if $lat == 0 && $lon == 0;
+ my ($x, $y) = Utils::convert_latlon_to_en($lat, $lon);
+ return ($x, $y);
+}
+
+# Reproject a BNG easting/northing into WGS84 lat/lon
+sub reproject_to_latlon($$$) {
+ my ($self, $x, $y) = @_;
+ my ($lat, $lon) = Utils::convert_en_to_latlon($x, $y);
+ return ($lat, $lon);
+}
+
+sub map_javascript { [
+ '/vendor/OpenLayers/OpenLayers.wms.js',
+ '/js/map-OpenLayers.js',
+ '/js/map-wms-base.js',
+ '/js/map-wms-northamptonshire.js',
+] }
+
+1;
diff --git a/perllib/FixMyStreet/Map/OSM.pm b/perllib/FixMyStreet/Map/OSM.pm
index a6cb6acea..ef465d7dc 100644
--- a/perllib/FixMyStreet/Map/OSM.pm
+++ b/perllib/FixMyStreet/Map/OSM.pm
@@ -87,10 +87,10 @@ sub generate_map_data {
$zoom = $numZoomLevels - 1 if $zoom >= $numZoomLevels;
$zoom = 0 if $zoom < 0;
$params{zoom_act} = $zoomOffset + $zoom;
- ($params{x_tile}, $params{y_tile}) = latlon_to_tile_with_adjust($params{latitude}, $params{longitude}, $params{zoom_act});
+ ($params{x_tile}, $params{y_tile}) = $self->latlon_to_tile_with_adjust($params{latitude}, $params{longitude}, $params{zoom_act});
foreach my $pin (@{$params{pins}}) {
- ($pin->{px}, $pin->{py}) = latlon_to_px($pin->{latitude}, $pin->{longitude}, $params{x_tile}, $params{y_tile}, $params{zoom_act});
+ ($pin->{px}, $pin->{py}) = $self->latlon_to_px($pin->{latitude}, $pin->{longitude}, $params{x_tile}, $params{y_tile}, $params{zoom_act});
}
return {
@@ -102,24 +102,24 @@ sub generate_map_data {
zoom => $zoom,
zoomOffset => $zoomOffset,
numZoomLevels => $numZoomLevels,
- compass => compass( $params{x_tile}, $params{y_tile}, $params{zoom_act} ),
+ compass => $self->compass( $params{x_tile}, $params{y_tile}, $params{zoom_act} ),
};
}
sub compass {
- my ( $x, $y, $z ) = @_;
+ my ( $self, $x, $y, $z ) = @_;
return {
- north => [ map { Utils::truncate_coordinate($_) } tile_to_latlon( $x, $y-1, $z ) ],
- south => [ map { Utils::truncate_coordinate($_) } tile_to_latlon( $x, $y+1, $z ) ],
- west => [ map { Utils::truncate_coordinate($_) } tile_to_latlon( $x-1, $y, $z ) ],
- east => [ map { Utils::truncate_coordinate($_) } tile_to_latlon( $x+1, $y, $z ) ],
- here => [ map { Utils::truncate_coordinate($_) } tile_to_latlon( $x, $y, $z ) ],
+ north => [ map { Utils::truncate_coordinate($_) } $self->tile_to_latlon( $x, $y-1, $z ) ],
+ south => [ map { Utils::truncate_coordinate($_) } $self->tile_to_latlon( $x, $y+1, $z ) ],
+ west => [ map { Utils::truncate_coordinate($_) } $self->tile_to_latlon( $x-1, $y, $z ) ],
+ east => [ map { Utils::truncate_coordinate($_) } $self->tile_to_latlon( $x+1, $y, $z ) ],
+ here => [ map { Utils::truncate_coordinate($_) } $self->tile_to_latlon( $x, $y, $z ) ],
};
}
# Given a lat/lon, convert it to OSM tile co-ordinates (precise).
-sub latlon_to_tile($$$) {
- my ($lat, $lon, $zoom) = @_;
+sub latlon_to_tile($$$$) {
+ my ($self, $lat, $lon, $zoom) = @_;
my $x_tile = ($lon + 180) / 360 * 2**$zoom;
my $y_tile = (1 - log(tan(deg2rad($lat)) + sec(deg2rad($lat))) / pi) / 2 * 2**$zoom;
return ( $x_tile, $y_tile );
@@ -127,9 +127,9 @@ sub latlon_to_tile($$$) {
# Given a lat/lon, convert it to OSM tile co-ordinates (nearest actual tile,
# adjusted so the point will be near the centre of a 2x2 tiled map).
-sub latlon_to_tile_with_adjust($$$) {
- my ($lat, $lon, $zoom) = @_;
- my ($x_tile, $y_tile) = latlon_to_tile($lat, $lon, $zoom);
+sub latlon_to_tile_with_adjust($$$$) {
+ my ($self, $lat, $lon, $zoom) = @_;
+ my ($x_tile, $y_tile) = $self->latlon_to_tile($lat, $lon, $zoom);
# Try and have point near centre of map
if ($x_tile - int($x_tile) > 0.5) {
@@ -143,7 +143,7 @@ sub latlon_to_tile_with_adjust($$$) {
}
sub tile_to_latlon {
- my ($x, $y, $zoom) = @_;
+ my ($self, $x, $y, $zoom) = @_;
my $n = 2 ** $zoom;
my $lon = $x / $n * 360 - 180;
my $lat = rad2deg(atan(sinh(pi * (1 - 2 * $y / $n))));
@@ -151,9 +151,9 @@ sub tile_to_latlon {
}
# Given a lat/lon, convert it to pixel co-ordinates from the top left of the map
-sub latlon_to_px($$$$$) {
- my ($lat, $lon, $x_tile, $y_tile, $zoom) = @_;
- my ($pin_x_tile, $pin_y_tile) = latlon_to_tile($lat, $lon, $zoom);
+sub latlon_to_px($$$$$$) {
+ my ($self, $lat, $lon, $x_tile, $y_tile, $zoom) = @_;
+ my ($pin_x_tile, $pin_y_tile) = $self->latlon_to_tile($lat, $lon, $zoom);
my $pin_x = tile_to_px($pin_x_tile, $x_tile);
my $pin_y = tile_to_px($pin_y_tile, $y_tile);
return ($pin_x, $pin_y);
@@ -182,8 +182,8 @@ sub click_to_wgs84 {
my ($self, $c, $pin_tile_x, $pin_x, $pin_tile_y, $pin_y) = @_;
my $tile_x = click_to_tile($pin_tile_x, $pin_x);
my $tile_y = click_to_tile($pin_tile_y, $pin_y);
- my $zoom = MIN_ZOOM_LEVEL + (defined $c->get_param('zoom') ? $c->get_param('zoom') : 3);
- my ($lat, $lon) = tile_to_latlon($tile_x, $tile_y, $zoom);
+ my $zoom = $self->MIN_ZOOM_LEVEL + (defined $c->get_param('zoom') ? $c->get_param('zoom') : 3);
+ my ($lat, $lon) = $self->tile_to_latlon($tile_x, $tile_y, $zoom);
return ( $lat, $lon );
}
diff --git a/perllib/FixMyStreet/Map/UKCouncilWMTS.pm b/perllib/FixMyStreet/Map/UKCouncilWMTS.pm
new file mode 100644
index 000000000..53b6859bf
--- /dev/null
+++ b/perllib/FixMyStreet/Map/UKCouncilWMTS.pm
@@ -0,0 +1,55 @@
+package FixMyStreet::Map::UKCouncilWMTS;
+use base 'FixMyStreet::Map::WMTSBase';
+
+use strict;
+
+sub zoom_parameters {
+ my $self = shift;
+ my $params = {
+ zoom_levels => scalar $self->scales,
+ default_zoom => $self->default_zoom,
+ min_zoom_level => 0,
+ id_offset => 0,
+ };
+ return $params;
+}
+
+sub tile_parameters {
+ my $self = shift;
+ my $params = {
+ urls => $self->urls,
+ layer_names => $self->layer_names,
+ wmts_version => '1.0.0',
+ layer_style => 'default',
+ matrix_set => 'default028mm',
+ suffix => '.png', # appended to tile URLs
+ size => 256, # pixels
+ dpi => 96,
+ inches_per_unit => 39.37, # BNG uses metres
+ projection => 'EPSG:27700',
+ origin_x => -5220400.0,
+ origin_y => 4470200.0,
+ };
+ return $params;
+}
+
+# Reproject a WGS84 lat/lon into BNG easting/northing
+sub reproject_from_latlon($$$) {
+ my ($self, $lat, $lon) = @_;
+ # do not try to reproject if we have no co-ordindates as convert breaks
+ return (0.0, 0.0) if $lat == 0 && $lon == 0;
+ my ($x, $y) = Utils::convert_latlon_to_en($lat, $lon);
+ return ($x, $y);
+}
+
+# Reproject a BNG easting/northing into WGS84 lat/lon
+sub reproject_to_latlon($$$) {
+ my ($self, $x, $y) = @_;
+ return (0,0) if $x<0 || $y<0;
+ my ($lat, $lon) = Utils::convert_en_to_latlon($x, $y);
+ return ($lat, $lon);
+}
+
+sub map_template { 'wmts' }
+
+1;
diff --git a/perllib/FixMyStreet/Map/WMSBase.pm b/perllib/FixMyStreet/Map/WMSBase.pm
new file mode 100644
index 000000000..ce8b6ab38
--- /dev/null
+++ b/perllib/FixMyStreet/Map/WMSBase.pm
@@ -0,0 +1,151 @@
+# FixMyStreet:Map::WMSBase
+# Makes it easier for cobrands to use their own WMS base map.
+# This cannot be used directly; you must subclass it and implement several
+# methods. See, e.g. FixMyStreet::Map::Northamptonshire.
+
+package FixMyStreet::Map::WMSBase;
+use parent FixMyStreet::Map::WMXBase;
+
+use strict;
+
+# A hash of parameters used in calculations for map tiles
+sub tile_parameters {
+ my $params = {
+ urls => [ '' ], # URL of the map tiles, up to the /{z}/{x}/{y} part
+ layer_names => [ '' ],
+ wms_version => '1.0.0',
+ size => 256, # pixels
+ dpi => 96,
+ inches_per_unit => 0, # See OpenLayers.INCHES_PER_UNIT for some options.
+ projection => 'EPSG:3857', # Passed through to OpenLayers.Projection
+ };
+ return $params;
+}
+
+# This is used to determine which template to render the map with
+sub map_template { 'wms' }
+
+sub get_res {
+ my ($self, $zoom) = @_;
+
+ my @scales = $self->scales;
+
+ my $res = $scales[$zoom] /
+ ($self->tile_parameters->{inches_per_unit} * $self->tile_parameters->{dpi});
+
+ return $res;
+}
+
+sub _get_tile_size {
+ my ($self, $params) = @_;
+
+ my $res = $self->get_res($params->{zoom});
+ return $res * $self->tile_parameters->{size};
+}
+
+sub _get_tile_params {
+ my ($self, $params, $left_col, $top_row, $z, $tile_url, $size) = @_;
+
+ my ($min_x, $min_y, $max_x, $max_y) = ($left_col, $top_row - $size, $left_col + $size, $top_row);
+
+ return ($tile_url, $min_x, $min_y, $max_x, $max_y);
+}
+
+sub _get_tile_src {
+ my ($self, $tile_url, $min_x, $min_y, $max_x, $max_y, $col, $row) = @_;
+
+ my $src = sprintf( '%s&bbox=%d,%d,%d,%d',
+ $tile_url, $min_x + $col, $min_y - $row, $max_x + $col, $max_y - $row);
+
+ return $src;
+}
+
+sub _get_tile_id {
+ my ($self, $tile_url, $min_x, $min_y, $max_x, $max_y, $col, $row) = @_;
+
+ return sprintf( '%d.%d', ($min_x + $col), ($min_y - $row) );
+}
+
+sub _get_row {
+ my ($self, $top_row, $row_offset, $size) = @_;
+ return $row_offset * $size;
+}
+
+sub _get_col {
+ my ($self, $left_col, $col_offset, $size) = @_;
+ return $col_offset * $size;
+}
+
+sub map_type { 'OpenLayers.Layer.WMS' }
+
+sub _map_hash_extras {
+ my $self = shift;
+
+ return {
+ wms_version => $self->tile_parameters->{wms_version},
+ format => $self->tile_parameters->{format},
+ };
+}
+
+sub tile_base_url {
+ my $self = shift;
+ my $params = $self->tile_parameters;
+ return sprintf '%s?version=%s&format=%s&size=%s&width=%s&height=%s&service=WMS&layers=%s&request=GetMap&srs=%s',
+ $params->{urls}[0], $params->{wms_version}, $params->{format}, $params->{size}, $params->{size},
+ $params->{size}, $params->{layer_names}[0], $params->{projection};
+}
+
+# Given a lat/lon, convert it to tile co-ordinates (nearest actual tile,
+# adjusted so the point will be near the centre of a 2x2 tiled map).
+sub latlon_to_tile_with_adjust {
+ my ($self, $lat, $lon, $zoom, $rows, $cols) = @_;
+ my ($x_tile, $y_tile)
+ = $self->reproject_from_latlon($lat, $lon, $zoom);
+
+ my $tile_params = $self->tile_parameters;
+ my $res = $self->get_res($zoom);
+
+ $x_tile = $x_tile - ($res * $tile_params->{size});
+ $y_tile = $y_tile + ($res * $tile_params->{size});
+
+ return ( int($x_tile), int($y_tile) );
+}
+
+sub tile_to_latlon {
+ my ($self, $fx, $fy, $zoom) = @_;
+ my ($lat, $lon) = $self->reproject_to_latlon($fx, $fy);
+
+ return ($lat, $lon);
+}
+
+# Given a lat/lon, convert it to pixel co-ordinates from the top left of the map
+sub latlon_to_px($$$$$$) {
+ my ($self, $lat, $lon, $x_tile, $y_tile, $zoom) = @_;
+ my ($pin_x_tile, $pin_y_tile) = $self->reproject_from_latlon($lat, $lon, $zoom);
+ my $res = $self->get_res($zoom);
+ my $pin_x = ( $pin_x_tile - $x_tile ) / $res;
+ my $pin_y = ( $y_tile - $pin_y_tile ) / $res;
+ return ($pin_x, $pin_y);
+}
+
+sub click_to_tile {
+ my ($self, $pin_tile, $pin, $zoom, $reverse) = @_;
+ my $tile_params = $self->tile_parameters;
+ my $size = $tile_params->{size};
+ my $res = $self->get_res($zoom);
+
+ return $reverse ? $pin_tile + ( ( $size - $pin ) * $res ) : $pin_tile + ( $pin * $res );
+}
+
+# Given some click co-ords (the tile they were on, and where in the
+# tile they were), convert to WGS84 and return.
+sub click_to_wgs84 {
+ my ($self, $c, $pin_tile_x, $pin_x, $pin_tile_y, $pin_y) = @_;
+ my $zoom = (defined $c->get_param('zoom') ? $c->get_param('zoom') : $self->zoom_parameters->{default_zoom});
+ my $tile_x = $self->click_to_tile($pin_tile_x, $pin_x, $zoom);
+ my $tile_y = $self->click_to_tile($pin_tile_y, $pin_y, $zoom, 1);
+ my ($lat, $lon) = $self->tile_to_latlon($tile_x, $tile_y, $zoom);
+ return ( $lat, $lon );
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/WMTSBase.pm b/perllib/FixMyStreet/Map/WMTSBase.pm
index 051f8f369..e482b3f37 100644
--- a/perllib/FixMyStreet/Map/WMTSBase.pm
+++ b/perllib/FixMyStreet/Map/WMTSBase.pm
@@ -4,40 +4,9 @@
# methods. See, e.g. FixMyStreet::Map::Zurich or FixMyStreet::Map::Bristol.
package FixMyStreet::Map::WMTSBase;
+use parent FixMyStreet::Map::WMXBase;
use strict;
-use Math::Trig;
-use Utils;
-use JSON::MaybeXS;
-
-sub scales {
- my $self = shift;
- my @scales = (
- # A list of scales corresponding to zoom levels, e.g.
- # '192000',
- # '96000',
- # '48000',
- # etc...
- );
- return @scales;
-}
-
-# The copyright string to display in the corner of the map.
-sub copyright {
- return '';
-}
-
-# A hash of parameters that control the zoom options for the map
-sub zoom_parameters {
- my $self = shift;
- my $params = {
- zoom_levels => scalar $self->scales,
- default_zoom => 0,
- min_zoom_level => 0,
- id_offset => 0,
- };
- return $params;
-}
# A hash of parameters used in calculations for map tiles
sub tile_parameters {
@@ -58,162 +27,46 @@ sub tile_parameters {
return $params;
}
-# This is used to determine which template to render the map with
-sub map_template { 'fms' }
+sub _get_tile_params {
+ my ($self, $params, $left_col, $top_row, $z, $tile_url) = @_;
-# Reproject a WGS84 lat/lon into an x/y coordinate in this map's CRS.
-# Subclasses will want to override this.
-sub reproject_from_latlon($$$) {
- my ($self, $lat, $lon) = @_;
- return (0.0, 0.0);
+ return ($tile_url, $z, $self->tile_parameters->{suffix});
}
-# Reproject a x/y coordinate from this map's CRS into WGS84 lat/lon
-# Subclasses will want to override this.
-sub reproject_to_latlon($$$) {
- my ($self, $x, $y) = @_;
- return (0.0, 0.0);
+sub _get_tile_src {
+ my ($self, $tile_url, $z, $suffix, $col, $row) = @_;
+
+ return sprintf( '%s/%d/%d/%d%s',
+ $tile_url, $z, $row, $col, $suffix);
}
+sub _get_tile_id {
+ my ($self, $tile_url, $x, $suffix, $col, $row) = @_;
-sub map_tiles {
- my ($self, %params) = @_;
- my ($left_col, $top_row, $z) = @params{'x_left_tile', 'y_top_tile', 'matrix_id'};
- my $tile_url = $self->tile_base_url;
- my $tile_suffix = $self->tile_parameters->{suffix};
- my $cols = $params{cols};
- my $rows = $params{rows};
-
- my @col_offsets = (0.. ($cols-1) );
- my @row_offsets = (0.. ($rows-1) );
-
- return [
- map {
- my $row_offset = $_;
- [
- map {
- my $col_offset = $_;
- my $row = $top_row + $row_offset;
- my $col = $left_col + $col_offset;
- my $src = sprintf '%s/%d/%d/%d%s',
- $tile_url, $z, $row, $col, $tile_suffix;
- my $dotted_id = sprintf '%d.%d', $col, $row;
-
- # return the data structure for the cell
- +{
- src => $src,
- row_offset => $row_offset,
- col_offset => $col_offset,
- dotted_id => $dotted_id,
- alt => "Map tile $dotted_id", # TODO "NW map tile"?
- }
- }
- @col_offsets
- ]
- }
- @row_offsets
- ];
+ return sprintf( '%d.%d', $col, $row);
}
-# display_map C PARAMS
-# PARAMS include:
-# latitude, longitude for the centre point of the map
-# CLICKABLE is set if the map is clickable
-# PINS is array of pins to show, location and colour
-sub display_map {
- my ($self, $c, %params) = @_;
-
- # Map centre may be overridden in the query string
- $params{latitude} = Utils::truncate_coordinate($c->get_param('lat') + 0)
- if defined $c->get_param('lat');
- $params{longitude} = Utils::truncate_coordinate($c->get_param('lon') + 0)
- if defined $c->get_param('lon');
-
- $params{rows} //= 2; # 2x2 square is default
- $params{cols} //= 2;
-
- my $zoom_params = $self->zoom_parameters;
-
- $params{zoom} = do {
- my $zoom = defined $c->get_param('zoom')
- ? $c->get_param('zoom') + 0
- : $c->stash->{page} eq 'report'
- ? $zoom_params->{default_zoom}+1
- : $zoom_params->{default_zoom};
- $zoom = $zoom_params->{zoom_levels} - 1
- if $zoom >= $zoom_params->{zoom_levels};
- $zoom = 0 if $zoom < 0;
- $zoom;
- };
+sub _get_row {
+ my ($self, $top_row, $row_offset, $size) = @_;
+ return $top_row + $row_offset;
+}
- $c->stash->{map} = $self->get_map_hash( %params );
-
- if ($params{print_report}) {
- $params{zoom}++ unless $params{zoom} >= $zoom_params->{zoom_levels};
- $c->stash->{print_report_map}
- = $self->get_map_hash(
- %params,
- img_type => 'img',
- cols => 4, rows => 4,
- );
- # NB: we can passthrough img_type as literal here, as only designed for print
-
- # NB we can do arbitrary size, including non-squares, however we'd have
- # to modify .square-map style with padding-bottom percentage calculated in
- # an inline style:
- # <zarino> in which case, the only change that'd be required is
- # removing { padding-bottom: 100% } from .square-map__outer, putting
- # the percentage into an inline style on the element itself, and then
- # probably renaming .square-map__* to .fixed-aspect-map__* or something
- # since it's no longer necessarily square
- }
+sub _get_col {
+ my ($self, $left_col, $col_offset, $size) = @_;
+ return $left_col + $col_offset;
}
-sub get_map_hash {
- my ($self, %params) = @_;
-
- @params{'x_centre_tile', 'y_centre_tile', 'matrix_id'}
- = $self->latlon_to_tile_with_adjust(
- @params{'latitude', 'longitude', 'zoom', 'rows', 'cols'});
-
- # centre_(row|col) is either in middle, or just to right.
- # e.g. if centre is the number in parens:
- # 1 (2) 3 => 2 - int( 3/2 ) = 1
- # 1 2 (3) 4 => 3 - int( 4/2 ) = 1
- $params{x_left_tile} = $params{x_centre_tile} - int($params{cols} / 2);
- $params{y_top_tile} = $params{y_centre_tile} - int($params{rows} / 2);
-
- $params{pins} = [
- map {
- my $pin = { %$_ }; # shallow clone
- ($pin->{px}, $pin->{py})
- = $self->latlon_to_px($pin->{latitude}, $pin->{longitude},
- @params{'x_left_tile', 'y_top_tile', 'zoom'});
- $pin;
- } @{ $params{pins} }
- ];
+sub map_type { 'OpenLayers.Layer.WMTS' }
+
+sub _map_hash_extras {
+ my $self = shift;
- my @scales = $self->scales;
return {
- %params,
- type => $self->map_template,
- map_type => 'OpenLayers.Layer.WMTS',
- tiles => $self->map_tiles( %params ),
- copyright => $self->copyright(),
- zoom => $params{zoom},
- zoomOffset => $self->zoom_parameters->{min_zoom_level},
- numZoomLevels => $self->zoom_parameters->{zoom_levels},
- tile_size => $self->tile_parameters->{size},
- tile_dpi => $self->tile_parameters->{dpi},
- tile_urls => encode_json( $self->tile_parameters->{urls} ),
- tile_suffix => $self->tile_parameters->{suffix},
- layer_names => encode_json( $self->tile_parameters->{layer_names} ),
layer_style => $self->tile_parameters->{layer_style},
matrix_set => $self->tile_parameters->{matrix_set},
- map_projection => $self->tile_parameters->{projection},
origin_x => force_float_format($self->tile_parameters->{origin_x}),
origin_y => force_float_format($self->tile_parameters->{origin_y}),
- scales => encode_json( \@scales ),
+ tile_suffix => $self->tile_parameters->{suffix},
};
}
diff --git a/perllib/FixMyStreet/Map/WMXBase.pm b/perllib/FixMyStreet/Map/WMXBase.pm
new file mode 100644
index 000000000..bc529817e
--- /dev/null
+++ b/perllib/FixMyStreet/Map/WMXBase.pm
@@ -0,0 +1,199 @@
+# FixMyStreet:Map::WMXBase
+# Common methods for WMS and WMTS maps
+
+package FixMyStreet::Map::WMXBase;
+
+use strict;
+use Math::Trig;
+use Utils;
+use JSON::MaybeXS;
+
+sub scales {
+ my $self = shift;
+ my @scales = (
+ # A list of scales corresponding to zoom levels, e.g.
+ # '192000',
+ # '96000',
+ # '48000',
+ # etc...
+ );
+ return @scales;
+}
+
+# The copyright string to display in the corner of the map.
+sub copyright {
+ return '';
+}
+
+# A hash of parameters that control the zoom options for the map
+sub zoom_parameters {
+ my $self = shift;
+ my $params = {
+ zoom_levels => scalar $self->scales,
+ default_zoom => 0,
+ min_zoom_level => 0,
+ id_offset => 0,
+ };
+ return $params;
+}
+
+# This is used to determine which template to render the map with
+sub map_template { 'fms' }
+
+# Reproject a WGS84 lat/lon into an x/y coordinate in this map's CRS.
+# Subclasses will want to override this.
+sub reproject_from_latlon($$$) {
+ my ($self, $lat, $lon) = @_;
+ return (0.0, 0.0);
+}
+
+# Reproject a x/y coordinate from this map's CRS into WGS84 lat/lon
+# Subclasses will want to override this.
+sub reproject_to_latlon($$$) {
+ my ($self, $x, $y) = @_;
+ return (0.0, 0.0);
+}
+
+sub _get_tile_size {
+ return shift->tile_parameters->{size};
+}
+
+sub map_tiles {
+ my ($self, %params) = @_;
+ my ($left_col, $top_row, $z) = @params{'x_left_tile', 'y_top_tile', 'matrix_id'};
+ my $tile_url = $self->tile_base_url;
+ my $cols = $params{cols};
+ my $rows = $params{rows};
+
+ my @col_offsets = (0.. ($cols-1) );
+ my @row_offsets = (0.. ($rows-1) );
+
+ my $size = $self->_get_tile_size(\%params);
+ my @params = $self->_get_tile_params(\%params, $left_col, $top_row, $z, $tile_url, $size);
+
+ return [
+ map {
+ my $row_offset = $_;
+ [
+ map {
+ my $col_offset = $_;
+ my $row = $self->_get_row($top_row, $row_offset, $size);
+ my $col = $self->_get_col($left_col, $col_offset, $size);
+ my $src = $self->_get_tile_src(@params, $col, $row);
+ my $dotted_id = $self->_get_tile_id(@params, $col, $row);
+
+ # return the data structure for the cell
+ +{
+ src => $src,
+ row_offset => $row_offset,
+ col_offset => $col_offset,
+ dotted_id => $dotted_id,
+ alt => "Map tile $dotted_id",
+ }
+ }
+ @col_offsets
+ ]
+ }
+ @row_offsets
+ ];
+}
+
+# display_map C PARAMS
+# PARAMS include:
+# latitude, longitude for the centre point of the map
+# CLICKABLE is set if the map is clickable
+# PINS is array of pins to show, location and colour
+sub display_map {
+ my ($self, $c, %params) = @_;
+
+ # Map centre may be overridden in the query string
+ $params{latitude} = Utils::truncate_coordinate($c->get_param('lat') + 0)
+ if defined $c->get_param('lat');
+ $params{longitude} = Utils::truncate_coordinate($c->get_param('lon') + 0)
+ if defined $c->get_param('lon');
+
+ $params{rows} //= 2; # 2x2 square is default
+ $params{cols} //= 2;
+
+ my $zoom_params = $self->zoom_parameters;
+
+ $params{zoom} = do {
+ my $zoom = defined $c->get_param('zoom')
+ ? $c->get_param('zoom') + 0
+ : $c->stash->{page} eq 'report'
+ ? $zoom_params->{default_zoom}+1
+ : $zoom_params->{default_zoom};
+ $zoom = $zoom_params->{zoom_levels} - 1
+ if $zoom >= $zoom_params->{zoom_levels};
+ $zoom = 0 if $zoom < 0;
+ $zoom;
+ };
+
+ $c->stash->{map} = $self->get_map_hash( %params );
+
+ if ($params{print_report}) {
+ $params{zoom}++ unless $params{zoom} >= $zoom_params->{zoom_levels};
+ $c->stash->{print_report_map}
+ = $self->get_map_hash(
+ %params,
+ img_type => 'img',
+ cols => 4, rows => 4,
+ );
+ }
+}
+
+sub _map_hash_extras { return {} }
+
+sub get_map_hash {
+ my ($self, %params) = @_;
+
+ @params{'x_centre_tile', 'y_centre_tile', 'matrix_id'}
+ = $self->latlon_to_tile_with_adjust(
+ @params{'latitude', 'longitude', 'zoom', 'rows', 'cols'});
+
+ $params{x_left_tile} = $params{x_centre_tile} - int($params{cols} / 2);
+ $params{y_top_tile} = $params{y_centre_tile} - int($params{rows} / 2);
+
+ $params{pins} = [
+ map {
+ my $pin = { %$_ }; # shallow clone
+ ($pin->{px}, $pin->{py})
+ = $self->latlon_to_px($pin->{latitude}, $pin->{longitude},
+ @params{'x_left_tile', 'y_top_tile', 'zoom'});
+ $pin;
+ } @{ $params{pins} }
+ ];
+
+ my @scales = $self->scales;
+ return {
+ %params,
+ type => $self->map_template,
+ map_type => $self->map_type,
+ tiles => $self->map_tiles( %params ),
+ copyright => $self->copyright(),
+ zoom => $params{zoom},
+ zoomOffset => $self->zoom_parameters->{min_zoom_level},
+ numZoomLevels => $self->zoom_parameters->{zoom_levels},
+ tile_size => $self->tile_parameters->{size},
+ tile_dpi => $self->tile_parameters->{dpi},
+ tile_urls => encode_json( $self->tile_parameters->{urls} ),
+ layer_names => encode_json( $self->tile_parameters->{layer_names} ),
+ map_projection => $self->tile_parameters->{projection},
+ scales => encode_json( \@scales ),
+ compass => $self->compass( $params{x_centre_tile}, $params{y_centre_tile}, $params{zoom} ),
+ %{ $self->_map_hash_extras },
+ };
+}
+
+sub compass {
+ my ( $self, $x, $y, $z ) = @_;
+ return {
+ north => [ map { Utils::truncate_coordinate($_) } $self->tile_to_latlon( $x, $y-1, $z ) ],
+ south => [ map { Utils::truncate_coordinate($_) } $self->tile_to_latlon( $x, $y+1, $z ) ],
+ west => [ map { Utils::truncate_coordinate($_) } $self->tile_to_latlon( $x-1, $y, $z ) ],
+ east => [ map { Utils::truncate_coordinate($_) } $self->tile_to_latlon( $x+1, $y, $z ) ],
+ here => [ map { Utils::truncate_coordinate($_) } $self->tile_to_latlon( $x, $y, $z ) ],
+ };
+}
+
+1;