diff options
Diffstat (limited to 'perllib/FixMyStreet/Map/WMSBase.pm')
-rw-r--r-- | perllib/FixMyStreet/Map/WMSBase.pm | 151 |
1 files changed, 151 insertions, 0 deletions
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; |