aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--perllib/FixMyStreet/Map/Zurich.pm175
-rw-r--r--perllib/Geo/Coordinates/CH1903.pm115
2 files changed, 272 insertions, 18 deletions
diff --git a/perllib/FixMyStreet/Map/Zurich.pm b/perllib/FixMyStreet/Map/Zurich.pm
index 3d343c6ac..d2f7a35af 100644
--- a/perllib/FixMyStreet/Map/Zurich.pm
+++ b/perllib/FixMyStreet/Map/Zurich.pm
@@ -7,32 +7,171 @@
# Email: steve@mysociety.org; WWW: http://www.mysociety.org/
package FixMyStreet::Map::Zurich;
-use base 'FixMyStreet::Map::OSM';
use strict;
+use Geo::Coordinates::CH1903;
+use Math::Trig;
+use Utils;
-sub map_type {
- return 'OpenLayers.Layer.WMTS';
-}
-
-sub map_template {
- return 'zurich';
-}
-
-sub base_tile_url {
- return 'http://www.wmts.stadt-zuerich.ch/Luftbild/MapServer/WMTS/tile/1.0.0/Luftbild/default/default028mm';
-}
+use constant ZOOM_LEVELS => 10;
+use constant DEFAULT_ZOOM => 7;
+use constant MIN_ZOOM_LEVEL => 0;
sub map_tiles {
my ( $self, %params ) = @_;
- my ( $x, $y, $z ) = ( $params{x_tile}, $params{y_tile}, $params{zoom_act} );
+ my ( $col, $row, $z ) = ( $params{x_tile}, $params{y_tile}, $params{matrix_id} );
my $tile_url = $self->base_tile_url();
return [
- "$tile_url/$z/" . ($x - 1) . "/" . ($y - 1) . ".jpg",
- "$tile_url/$z/$x/" . ($y - 1) . ".jpg",
- "$tile_url/$z/" . ($x - 1) . "/$y.jpg",
- "$tile_url/$z/$x/$y.jpg",
+ "$tile_url/$z/" . ($row - 1) . "/" . ($col - 1) . ".jpg",
+ "$tile_url/$z/" . ($row - 1) . "/$col.jpg",
+ "$tile_url/$z/$row/" . ($col - 1) . ".jpg",
+ "$tile_url/$z/$row/$col.jpg",
];
}
-1; \ No newline at end of file
+sub base_tile_url {
+ return 'http://www.wmts.stadt-zuerich.ch/Luftbild/MapServer/WMTS/tile/1.0.0/Luftbild/default/nativeTileMatrixSet';
+}
+
+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) = @_;
+
+ my $numZoomLevels = ZOOM_LEVELS;
+ my $zoomOffset = MIN_ZOOM_LEVEL;
+# if ($params{any_zoom}) {
+# $numZoomLevels = 10;
+# $zoomOffset = 0;
+# }
+
+ # TODO Adjust zoom level dependent upon population density
+ my $default_zoom = DEFAULT_ZOOM;
+
+ # Map centre may be overridden in the query string
+ $params{latitude} = Utils::truncate_coordinate($c->req->params->{lat} + 0)
+ if defined $c->req->params->{lat};
+ $params{longitude} = Utils::truncate_coordinate($c->req->params->{lon} + 0)
+ if defined $c->req->params->{lon};
+
+ my $zoom = defined $c->req->params->{zoom} ? $c->req->params->{zoom} + 0 : $default_zoom;
+ $zoom = $numZoomLevels - 1 if $zoom >= $numZoomLevels;
+ $zoom = 0 if $zoom < 0;
+ $params{zoom_act} = $zoomOffset + $zoom;
+
+ ($params{x_tile}, $params{y_tile}, $params{matrix_id}) = 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});
+ }
+
+ $c->stash->{map} = {
+ %params,
+ type => 'zurich',
+ map_type => 'OpenLayers.Layer.WMTS',
+ tiles => $self->map_tiles( %params ),
+ copyright => $self->copyright(),
+ zoom => $zoom,
+ zoomOffset => $zoomOffset,
+ numZoomLevels => $numZoomLevels,
+ };
+}
+
+# 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 - 1;
+ $matrix_id = 0 if $matrix_id < 0;
+
+ my @scales = ( '250000', '125000', '64000', '32000', '16000', '8000', '4000', '2000', '1000', '500' );
+ my $tileOrigin = { lat => 30814423, lon => -29386322 };
+ my $tileSize = 256;
+ my $res = $scales[$zoom] / (39.3701 * 96); # OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH
+
+ my $fx = ( $x - $tileOrigin->{lon} ) / ($res * $tileSize);
+ my $fy = ( $tileOrigin->{lat} - $y ) / ($res * $tileSize);
+
+ return ( $fx, $fy, $matrix_id );
+}
+
+# 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, $matrix_id) = latlon_to_tile($lat, $lon, $zoom);
+
+ # Try and have point near centre of map
+ if ($x_tile - int($x_tile) > 0.5) {
+ $x_tile += 1;
+ }
+ 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 @scales = ( '250000', '125000', '64000', '32000', '16000', '8000', '4000', '2000', '1000', '500' );
+ my $tileOrigin = { lat => 30814423, lon => -29386322 };
+ my $tileSize = 256;
+ my $res = $scales[$zoom] / (39.3701 * 96); # OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH
+
+ my $x = $fx * $res * $tileSize + $tileOrigin->{lon};
+ my $y = $tileOrigin->{lat} - $fy * $res * $tileSize;
+
+ my ($lat, $lon) = Geo::Coordinates::CH1903::to_latlon($x, $y);
+
+ 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 ($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);
+}
+
+# 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 = 256 * ($p - $c + 1);
+ $p = int($p + .5 * ($p <=> 0));
+ return $p;
+}
+
+sub click_to_tile {
+ my ($pin_tile, $pin) = @_;
+ $pin -= 256 while $pin > 256;
+ $pin += 256 while $pin < 0;
+ return $pin_tile + $pin / 256;
+}
+
+# Given some click co-ords (the tile they were on, and where in the
+# tile they were), convert to WGS84 and return.
+# XXX Note use of MIN_ZOOM_LEVEL here. (Copied from OSM, needed here?)
+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->req->params->{zoom} ? $c->req->params->{zoom} : DEFAULT_ZOOM);
+ my ($lat, $lon) = tile_to_latlon($tile_x, $tile_y, $zoom);
+ return ( $lat, $lon );
+}
+
+1;
diff --git a/perllib/Geo/Coordinates/CH1903.pm b/perllib/Geo/Coordinates/CH1903.pm
new file mode 100644
index 000000000..611126962
--- /dev/null
+++ b/perllib/Geo/Coordinates/CH1903.pm
@@ -0,0 +1,115 @@
+#!/usr/bin/perl
+#
+# Geo::Coordinates::CH1903
+# Conversion between WGS84 and Swiss CH1903.
+#
+# Copyright (c) 2012 UK Citizens Online Democracy. This module is free
+# software; you can redistribute it and/or modify it under the same terms as
+# Perl itself.
+#
+# WWW: http://www.mysociety.org/
+
+package Geo::Coordinates::CH1903;
+
+$Geo::Coordinates::CH1903::VERSION = '1.00';
+
+use strict;
+
+=head1 NAME
+
+Geo::Coordinates::CH1903
+
+=head1 VERSION
+
+1.00
+
+=head1 SYNOPSIS
+
+ use Geo::Coordinates::CH1903;
+
+ my ($lat, $lon) = ...;
+ my ($e, $n) = Geo::Coordinates::CH1903::from_latlon($lat, $lon);
+ my ($lat, $lon) = Geo::Coordinates::CH1903::to_latlon($e, $n);
+
+=head1 FUNCTIONS
+
+=over 4
+
+=cut
+
+sub deg2sec($) {
+ my $angle = shift;
+
+ my $deg = int($angle);
+ my $min = int(($angle - $deg) * 60);
+ my $sec = ((($angle - $deg) * 60) - $min) * 60;
+
+ return $sec + ($min * 60) + ($deg * 3600);
+}
+
+sub from_latlon($$) {
+ my ($lat, $lon) = @_;
+
+ $lat = deg2sec($lat);
+ $lon = deg2sec($lon);
+
+ my $lat_aux = ($lat - 169028.66) / 10000;
+ my $lon_aux = ($lon - 26782.5) / 10000;
+
+ my $x = 600072.37
+ + (211455.93 * $lon_aux)
+ - (10938.51 * $lon_aux * $lat_aux)
+ - (0.36 * $lon_aux * $lat_aux**2)
+ - (44.54 * $lon_aux**3);
+
+ my $y = 200147.07
+ + (308807.95 * $lat_aux)
+ + (3745.25 * $lon_aux**2)
+ + (76.63 * $lat_aux**2)
+ - (194.56 * $lon_aux**2 * $lat_aux)
+ + (119.79 * $lat_aux**3);
+
+ return ($x, $y);
+}
+
+sub to_latlon($$) {
+ my ($x, $y) = @_;
+
+ my $x_aux = ($x - 600000) / 1000000;
+ my $y_aux = ($y - 200000) / 1000000;
+
+ my $lat = 16.9023892
+ + (3.238272 * $y_aux)
+ - (0.270978 * $x_aux**2)
+ - (0.002528 * $y_aux**2)
+ - (0.0447 * $x_aux**2 * $y_aux)
+ - (0.0140 * $y_aux**3);
+
+ my $lon = 2.6779094
+ + (4.728982 * $x_aux)
+ + (0.791484 * $x_aux * $y_aux)
+ + (0.1306 * $x_aux * $y_aux**2)
+ - (0.0436 * $x_aux**3);
+
+ $lat = $lat * 100 / 36;
+ $lon = $lon * 100 / 36;
+
+ return ($lat, $lon);
+}
+
+=head1 AUTHOR AND COPYRIGHT
+
+Maths courtesy of the Swiss Federal Office of Topography:
+http://www.swisstopo.admin.ch/internet/swisstopo/en/home/products/software/products/skripts.html
+
+Written by Matthew Somerville
+
+Copyright (c) UK Citizens Online Democracy.
+
+This module is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut
+
+1;
+