diff options
author | Dave Arter <davea@mysociety.org> | 2016-06-29 11:07:36 +0100 |
---|---|---|
committer | Dave Arter <davea@mysociety.org> | 2016-07-07 11:28:33 +0100 |
commit | 5b9670512542f54588cc89f2eefc943db26b9ee2 (patch) | |
tree | b929b12826b3a916e68003559a222bd62f4ac53f /perllib/FixMyStreet | |
parent | 6d3cbb5d58c0297959c541da50faaf39884ffe1c (diff) |
[Zurich] Use new LV95/CH1903+ geocoder and base map tiles
- Use new `getLocation95` method for geocoding addresses
- Replace Coordinates::CH1903 with Coordinates::CH1903Plus
- Refactor Map::Zurich to use WMTSBase instead of duplicating code
- Slightly refactor map templates to make using custom WMTS tiles easier
- Use correct sizes for marker pins on maps
Closes mysociety/FixMyStreet-Commercial#769.
Closes mysociety/FixMyStreet-Commercial#768.
Diffstat (limited to 'perllib/FixMyStreet')
-rw-r--r-- | perllib/FixMyStreet/DB/Result/Problem.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/Geocode/Zurich.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/Map/Bristol.pm | 8 | ||||
-rw-r--r-- | perllib/FixMyStreet/Map/WMTSBase.pm | 20 | ||||
-rw-r--r-- | perllib/FixMyStreet/Map/Zurich.pm | 296 |
5 files changed, 87 insertions, 245 deletions
diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index b58294604..a2167032a 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -668,7 +668,7 @@ sub local_coords { my $self = shift; my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($self->cobrand)->new; if ($cobrand->moniker eq 'zurich') { - my ($x, $y) = Geo::Coordinates::CH1903::from_latlon($self->latitude, $self->longitude); + my ($x, $y) = Geo::Coordinates::CH1903Plus::from_latlon($self->latitude, $self->longitude); return ( int($x+0.5), int($y+0.5) ); } elsif ($cobrand->country eq 'GB') { my $coordsyst = 'G'; diff --git a/perllib/FixMyStreet/Geocode/Zurich.pm b/perllib/FixMyStreet/Geocode/Zurich.pm index 50a7c355e..ca893e9d6 100644 --- a/perllib/FixMyStreet/Geocode/Zurich.pm +++ b/perllib/FixMyStreet/Geocode/Zurich.pm @@ -13,7 +13,7 @@ package FixMyStreet::Geocode::Zurich; use strict; use Digest::MD5 qw(md5_hex); use File::Path (); -use Geo::Coordinates::CH1903; +use Geo::Coordinates::CH1903Plus; use Storable; use Utils; @@ -46,7 +46,7 @@ sub setup_soap { ) ); $soap = SOAP::Lite->on_action( sub { $action . $_[1]; } )->proxy($url); - $method = SOAP::Data->name('getLocation')->attr({ xmlns => $attr }); + $method = SOAP::Data->name('getLocation95')->attr({ xmlns => $attr }); } # string STRING CONTEXT @@ -95,7 +95,7 @@ sub string { foreach (@$results) { ($latitude, $longitude) = map { Utils::truncate_coordinate($_) } - Geo::Coordinates::CH1903::to_latlon($_->{easting}, $_->{northing}); + Geo::Coordinates::CH1903Plus::to_latlon($_->{easting}, $_->{northing}); push (@$error, { address => $_->{text}, latitude => $latitude, diff --git a/perllib/FixMyStreet/Map/Bristol.pm b/perllib/FixMyStreet/Map/Bristol.pm index 7098ceb40..3b60d1acf 100644 --- a/perllib/FixMyStreet/Map/Bristol.pm +++ b/perllib/FixMyStreet/Map/Bristol.pm @@ -20,9 +20,9 @@ sub zoom_parameters { sub tile_parameters { my $self = shift; my $params = { - url => 'https://maps.bristol.gov.uk/arcgis/rest/services/base/2015_BCC_96dpi/MapServer/WMTS/tile', + 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_name => '2015_BCC_96dpi', layer_style => 'default', matrix_set => 'default028mm', suffix => '.png', # appended to tile URLs @@ -60,6 +60,10 @@ sub copyright { return '© BCC'; } +sub map_type { + return 'bristol'; +} + # Reproject a WGS84 lat/lon into BNG easting/northing sub reproject_from_latlon($$$) { my ($self, $lat, $lon) = @_; diff --git a/perllib/FixMyStreet/Map/WMTSBase.pm b/perllib/FixMyStreet/Map/WMTSBase.pm index 13b6d8091..e35ae13c9 100644 --- a/perllib/FixMyStreet/Map/WMTSBase.pm +++ b/perllib/FixMyStreet/Map/WMTSBase.pm @@ -42,7 +42,8 @@ sub zoom_parameters { # A hash of parameters used in calculations for map tiles sub tile_parameters { my $params = { - url => '', # URL of the map tiles, up to the /{z}/{x}/{y} part + urls => [ '' ], # URL of the map tiles, up to the /{z}/{x}/{y} part + layer_names => [ '' ], wmts_version => '1.0.0', layer_style => '', matrix_set => '', @@ -206,14 +207,14 @@ sub get_map_hash { numZoomLevels => $self->zoom_parameters->{default_zoom}, tile_size => $self->tile_parameters->{size}, tile_dpi => $self->tile_parameters->{dpi}, - tile_url => $self->tile_parameters->{url}, + tile_urls => encode_json $self->tile_parameters->{urls}, tile_suffix => $self->tile_parameters->{suffix}, - layer_name => $self->tile_parameters->{layer_name}, + 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 => $self->tile_parameters->{origin_x}, - origin_y => $self->tile_parameters->{origin_y}, + origin_x => force_float_format($self->tile_parameters->{origin_x}), + origin_y => force_float_format($self->tile_parameters->{origin_y}), scales => encode_json \@scales, }; } @@ -222,7 +223,7 @@ sub tile_base_url { my $self = shift; my $params = $self->tile_parameters; return sprintf '%s/%s/%s/%s/%s', - $params->{url}, $params->{wmts_version}, $params->{layer_name}, + $params->{urls}[0], $params->{wmts_version}, $params->{layer_names}[0], $params->{layer_style}, $params->{matrix_set}; } @@ -336,4 +337,11 @@ sub click_to_wgs84 { return ( $lat, $lon ); } +sub force_float_format { + my $in = shift; + return mySociety::Locale::in_gb_locale { + sprintf( '%f', $in ); + }; +} + 1; diff --git a/perllib/FixMyStreet/Map/Zurich.pm b/perllib/FixMyStreet/Map/Zurich.pm index 4c597c30b..3b97f947f 100644 --- a/perllib/FixMyStreet/Map/Zurich.pm +++ b/perllib/FixMyStreet/Map/Zurich.pm @@ -1,259 +1,89 @@ # FixMyStreet:Map::Zurich # Zurich have their own tileserver. -# -# Copyright (c) 2012 UK Citizens Online Democracy. All rights reserved. -# Email: steve@mysociety.org; WWW: http://www.mysociety.org/ package FixMyStreet::Map::Zurich; +use base 'FixMyStreet::Map::WMTSBase'; use strict; -use Geo::Coordinates::CH1903; -use Math::Trig; -use Utils; - -use constant ZOOM_LEVELS => 9; -use constant DEFAULT_ZOOM => 5; -use constant MIN_ZOOM_LEVEL => 0; -use constant ID_OFFSET => 2; -use constant TILE_SIZE => 512; - -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->base_tile_url(); - 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.jpg', - $tile_url, $z, $row, $col; - 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 - ]; -} - -sub base_tile_url { - # use the new 512px maps as used by Javascript - return '//www.gis.stadt-zuerich.ch/maps/rest/services/tiled/LuftbildHybrid/MapServer/WMTS/tile/1.0.0/tiled_LuftbildHybrid/default/default028mm'; -} - -sub copyright { - return '© Stadt Zürich'; -} - -# 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; - - $params{zoom} = do { - my $zoom = defined $c->get_param('zoom') - ? $c->get_param('zoom') + 0 - : $c->stash->{page} eq 'report' - ? DEFAULT_ZOOM+1 - : DEFAULT_ZOOM; - $zoom = ZOOM_LEVELS - 1 if $zoom >= ZOOM_LEVELS; - $zoom = 0 if $zoom < 0; - $zoom; +use Geo::Coordinates::CH1903Plus; + +sub zoom_parameters { + my $self = shift; + my $params = { + zoom_levels => scalar $self->scales, + default_zoom => 5, + min_zoom_level => 0, + id_offset => 0, }; - - $c->stash->{map} = $self->get_map_hash( %params ); - - if ($params{print_report}) { - $params{zoom}++ unless $params{zoom} >= 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 - } + return $params; } -sub get_map_hash { - my ($self, %params) = @_; - - @params{'x_centre_tile', 'y_centre_tile', 'matrix_id'} - = 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}) - = latlon_to_px($pin->{latitude}, $pin->{longitude}, - @params{'x_left_tile', 'y_top_tile', 'zoom'}); - $pin; - } @{ $params{pins} } - ]; - - return { - %params, - type => 'zurich', - map_type => 'OpenLayers.Layer.WMTS', - tiles => $self->map_tiles( %params ), - copyright => $self->copyright(), - zoom => $params{zoom},, - zoomOffset => MIN_ZOOM_LEVEL, - numZoomLevels => ZOOM_LEVELS, - tile_size => TILE_SIZE, +sub tile_parameters { + my $self = shift; + my $params = { + urls => [ + 'https://www.gis.stadt-zuerich.ch/maps/rest/services/tiled95/LuftbildHybrid/MapServer/WMTS/tile', + 'https://www.gis.stadt-zuerich.ch/maps/rest/services/tiled95/Stadtplan3D/MapServer/WMTS/tile' + ], + layer_names => [ 'LuftbildHybrid', 'Stadtplan3D' ], + wmts_version => '1.0.0', + layer_style => 'default', + matrix_set => 'default028mm', + suffix => '', # appended to tile URLs + size => 512, # pixels + dpi => 96, + inches_per_unit => 39.3701, # BNG uses metres + projection => 'EPSG:2056', + # The original tile origin values from the getCapabilities call are + # -27386400.0/31814500.0, but this results in the map tile being offset + # slightly. These corrected values were figured out manually by + # trial and error... + origin_x => -27386322.5, + origin_y => 31814423.0, }; + return $params; } -# Given a lat/lon, convert it to Zurch tile co-ordinates (precise). -sub latlon_to_tile($$$) { - my ($lat, $lon, $zoom) = @_; - - my ($x, $y) = Geo::Coordinates::CH1903::from_latlon($lat, $lon); - - my $matrix_id = $zoom + ID_OFFSET; +sub scales { + my $self = shift; my @scales = ( - '250000', '125000', - '64000', '32000', - '16000', '8000', - '4000', '2000', - '1000', '500', - '250' + # The two highest zoom levels are pretty much useless so they're disabled. + # '256000', # resolution 67.73346880027094 + # '128000', # resolution 33.86673440013547 + '64000', # resolution 16.933367200067735 + '32000', # resolution 8.466683600033868 + '16000', # resolution 4.233341800016934 + '8000', # resolution 2.116670900008467 + '4000', # resolution 1.0583354500042335 + '2000', # resolution 0.5291677250021167 + '1000', # resolution 0.26458386250105836 + '500', # resolution 0.13229193125052918 + '250', # resolution 0.06614596562526459 ); - my $tileOrigin = { lat => 30814423, lon => -29386322 }; - my $res = $scales[$matrix_id] / (39.3701 * 96); - # OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH - - my $fx = ( $x - $tileOrigin->{lon} ) / ($res * TILE_SIZE); - my $fy = ( $tileOrigin->{lat} - $y ) / ($res * TILE_SIZE); - - return ( $fx, $fy, $matrix_id ); + return @scales; } -# 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). -# -# Takes parameter for rows/cols. For even sizes (2x2, 4x4 etc.) will -# do adjustment, but simply returns actual for odd sizes. -# -sub latlon_to_tile_with_adjust { - my ($lat, $lon, $zoom, $rows, $cols) = @_; - my ($x_tile, $y_tile, $matrix_id) - = my @ret - = latlon_to_tile($lat, $lon, $zoom); - - # Try and have point near centre of map, passing through if odd - unless ($cols % 2) { - if ($x_tile - int($x_tile) > 0.5) { - $x_tile += 1; - } - } - unless ($rows % 2) { - if ($y_tile - int($y_tile) > 0.5) { - $y_tile += 1; - } - } - - return ( int($x_tile), int($y_tile), $matrix_id ); -} - -sub tile_to_latlon { - my ($fx, $fy, $zoom) = @_; - - my $matrix_id = $zoom + ID_OFFSET; - my @scales = ( '250000', '125000', '64000', '32000', '16000', '8000', '4000', '2000', '1000', '500', '250' ); - my $tileOrigin = { lat => 30814423, lon => -29386322 }; - my $res = $scales[$matrix_id] / (39.3701 * 96); # OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH - - my $x = $fx * $res * TILE_SIZE + $tileOrigin->{lon}; - my $y = $tileOrigin->{lat} - $fy * $res * TILE_SIZE; - - my ($lat, $lon) = Geo::Coordinates::CH1903::to_latlon($x, $y); - - return ( $lat, $lon ); +sub copyright { + return '© Stadt Zürich'; } -# 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); - 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); +sub map_type { + return 'zurich'; } -# Convert tile co-ordinates to pixel co-ordinates from top left of map -# C is centre tile reference of displayed map -sub tile_to_px { - my ($p, $c) = @_; - $p = TILE_SIZE * ($p - $c); - $p = int($p + .5 * ($p <=> 0)); - return $p; -} -sub click_to_tile { - my ($pin_tile, $pin) = @_; - $pin -= TILE_SIZE while $pin > TILE_SIZE; - $pin += TILE_SIZE while $pin < 0; - return $pin_tile + $pin / TILE_SIZE; +# Reproject a WGS84 lat/lon into Swiss easting/northing +sub reproject_from_latlon($$$) { + my ($self, $lat, $lon) = @_; + my ($x, $y) = Geo::Coordinates::CH1903Plus::from_latlon($lat, $lon); + return ($x, $y); } -# 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 $tile_x = click_to_tile($pin_tile_x, $pin_x); - my $tile_y = click_to_tile($pin_tile_y, $pin_y); - my $zoom = (defined $c->get_param('zoom') ? $c->get_param('zoom') : DEFAULT_ZOOM); - my ($lat, $lon) = tile_to_latlon($tile_x, $tile_y, $zoom); - return ( $lat, $lon ); +# Reproject a Swiss easting/northing into WGS84 lat/lon +sub reproject_to_latlon($$$) { + my ($self, $x, $y) = @_; + my ($lat, $lon) = Geo::Coordinates::CH1903Plus::to_latlon($x, $y); + return ($lat, $lon); } 1; |