diff options
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Bristol.pm | 4 | ||||
-rw-r--r-- | perllib/FixMyStreet/Map/Bristol.pm | 77 | ||||
-rw-r--r-- | perllib/FixMyStreet/Map/WMTSBase.pm | 339 | ||||
-rw-r--r-- | templates/web/bristol/maps/fms.html | 13 | ||||
-rw-r--r-- | templates/web/bristol/maps/noscript_map.html | 70 | ||||
-rw-r--r-- | templates/web/bristol/maps/openlayers.html | 49 | ||||
-rw-r--r-- | web/cobrands/bristol/base.scss | 4 | ||||
-rw-r--r-- | web/js/map-wmts-bristol.js | 176 |
8 files changed, 732 insertions, 0 deletions
diff --git a/perllib/FixMyStreet/Cobrand/Bristol.pm b/perllib/FixMyStreet/Cobrand/Bristol.pm index ad4fe75f3..a6bab287a 100644 --- a/perllib/FixMyStreet/Cobrand/Bristol.pm +++ b/perllib/FixMyStreet/Cobrand/Bristol.pm @@ -19,6 +19,10 @@ sub example_places { return ( 'BS1 5TR', "Broad Quay" ); } +sub map_type { + 'Bristol'; +} + sub disambiguate_location { my $self = shift; my $string = shift; diff --git a/perllib/FixMyStreet/Map/Bristol.pm b/perllib/FixMyStreet/Map/Bristol.pm new file mode 100644 index 000000000..7098ceb40 --- /dev/null +++ b/perllib/FixMyStreet/Map/Bristol.pm @@ -0,0 +1,77 @@ +# FixMyStreet:Map::Bristol +# Bristol use their own tiles on their cobrand + +package FixMyStreet::Map::Bristol; +use base 'FixMyStreet::Map::WMTSBase'; + +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 tile_parameters { + my $self = shift; + my $params = { + url => 'https://maps.bristol.gov.uk/arcgis/rest/services/base/2015_BCC_96dpi/MapServer/WMTS/tile', + wmts_version => '1.0.0', + layer_name => '2015_BCC_96dpi', + 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', + # The original tile origin values from the getCapabilities call are + # -5220400.0/4470200.0, but this results in the map tile being offset + # slightly. These corrected values were figured out manually by + # trial and error... + origin_x => -5220385.5, + origin_y => 4470189.0, + }; + return $params; +} + +sub scales { + my $self = shift; + my @scales = ( + '192000', # resolution: 50.800101600203206 + '96000', # resolution: 25.400050800101603 + '48000', # resolution: 12.700025400050801 + '24000', # resolution: 6.350012700025401 + '12000', # resolution: 3.1750063500127004 + '6000', # resolution: 1.5875031750063502 + '3000', # resolution: 0.7937515875031751 + '1250', # resolution: 0.33072982812632296 + '750', # resolution: 0.19843789687579377 + ); + return @scales; +} + +sub copyright { + return '© BCC'; +} + +# 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/WMTSBase.pm b/perllib/FixMyStreet/Map/WMTSBase.pm new file mode 100644 index 000000000..13b6d8091 --- /dev/null +++ b/perllib/FixMyStreet/Map/WMTSBase.pm @@ -0,0 +1,339 @@ +# FixMyStreet:Map::WMTSBase +# Makes it easier for cobrands to use their own WMTS base map. +# This cannot be used directly; you must subclass it and implement several +# methods. See, e.g. FixMyStreet::Map::Zurich or FixMyStreet::Map::Bristol. + +package FixMyStreet::Map::WMTSBase; + +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 { + my $params = { + url => '', # URL of the map tiles, up to the /{z}/{x}/{y} part + wmts_version => '1.0.0', + layer_style => '', + matrix_set => '', + suffix => '', # appended to tile URLs + size => 256, # pixels + dpi => 96, + inches_per_unit => 0, # See OpenLayers.INCHES_PER_UNIT for some options. + origin_x => 0, + origin_y => 0, + projection => 'EPSG:3857', # Passed through to OpenLayers.Projection + }; + return $params; +} + +# This is used to determine which template to render the map with +sub map_type { + return '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 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 + ]; +} + +# 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, + ); + # 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_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} } + ]; + + my @scales = $self->scales; + return { + %params, + type => $self->map_type, + 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->{default_zoom}, + tile_size => $self->tile_parameters->{size}, + tile_dpi => $self->tile_parameters->{dpi}, + tile_url => $self->tile_parameters->{url}, + tile_suffix => $self->tile_parameters->{suffix}, + layer_name => $self->tile_parameters->{layer_name}, + 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}, + scales => encode_json \@scales, + }; +} + +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->{layer_style}, $params->{matrix_set}; +} + +# Given a lat/lon, convert it to tile co-ordinates (precise). +sub latlon_to_tile($$$$) { + my ($self, $lat, $lon, $zoom) = @_; + + my ($x, $y) = $self->reproject_from_latlon($lat, $lon); + + my $tile_params = $self->tile_parameters; + + my $matrix_id = $zoom + $self->zoom_parameters->{id_offset}; + my @scales = $self->scales; + my $tileOrigin = { + lon => $tile_params->{origin_x}, + lat => $tile_params->{origin_y} + }; + my $res = $scales[$matrix_id] / + ($tile_params->{inches_per_unit} * $tile_params->{dpi}); + # OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH + + my $fx = ( $x - $tileOrigin->{lon} ) / ($res * $tile_params->{size}); + my $fy = ( $tileOrigin->{lat} - $y ) / ($res * $tile_params->{size}); + + 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). +# +# 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 ($self, $lat, $lon, $zoom, $rows, $cols) = @_; + my ($x_tile, $y_tile, $matrix_id) + = my @ret + = $self->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 ($self, $fx, $fy, $zoom) = @_; + + my $tile_params = $self->tile_parameters; + my $matrix_id = $zoom + $self->zoom_parameters->{id_offset}; + my @scales = $self->scales; + my $tileOrigin = { + lon => $tile_params->{origin_x}, + lat => $tile_params->{origin_y} + }; + my $res = $scales[$matrix_id] / + ($tile_params->{inches_per_unit} * $tile_params->{dpi}); + # OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH + + my $x = $fx * $res * $tile_params->{size} + $tileOrigin->{lon}; + my $y = $tileOrigin->{lat} - $fy * $res * $tile_params->{size}; + + my ($lat, $lon) = $self->reproject_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 ($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 = $self->tile_to_px($pin_x_tile, $x_tile); + my $pin_y = $self->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 ($self, $p, $c) = @_; + $p = $self->tile_parameters->{size} * ($p - $c); + $p = int($p + .5 * ($p <=> 0)); + return $p; +} + +sub click_to_tile { + my ($self, $pin_tile, $pin) = @_; + my $tile_size = $self->tile_parameters->{size}; + $pin -= $tile_size while $pin > $tile_size; + $pin += $tile_size while $pin < 0; + return $pin_tile + $pin / $tile_size; +} + +# 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 = $self->click_to_tile($pin_tile_x, $pin_x); + my $tile_y = $self->click_to_tile($pin_tile_y, $pin_y); + my $zoom = (defined $c->get_param('zoom') ? $c->get_param('zoom') : $self->zoom_parameters->{default_zoom}); + my ($lat, $lon) = $self->tile_to_latlon($tile_x, $tile_y, $zoom); + return ( $lat, $lon ); +} + +1; diff --git a/templates/web/bristol/maps/fms.html b/templates/web/bristol/maps/fms.html new file mode 100644 index 000000000..a1a2a3f7b --- /dev/null +++ b/templates/web/bristol/maps/fms.html @@ -0,0 +1,13 @@ +[% map_js = BLOCK %] +<!-- <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=en-GB"></script> --> +<script type="text/javascript" src="[% version('/js/OpenLayers.2.11.zurich.js') %]"></script> +<script type="text/javascript" src="[% version('/js/OpenLayers.Projection.OrdnanceSurvey.js') %]"></script> +<script type="text/javascript" src="[% version('/js/map-OpenLayers.js') %]"></script> +<script type="text/javascript" src="[% version('/js/map-wmts-bristol.js') %]"></script> +<script type="text/javascript" src="[% version('/js/jquery.ba-hashchange.min.js') %]"></script> +<!--[if lte IE 6]> + <link rel="stylesheet" href="/js/OpenLayers-2.13.1/theme/default/ie6-style.css" type="text/css"> +<![endif]--> +[% END %] + +[% map_html = INCLUDE maps/openlayers.html %] diff --git a/templates/web/bristol/maps/noscript_map.html b/templates/web/bristol/maps/noscript_map.html new file mode 100644 index 000000000..5c2a2c064 --- /dev/null +++ b/templates/web/bristol/maps/noscript_map.html @@ -0,0 +1,70 @@ +<div class="noscript square-map__outer"> + <div class="square-map__inner"> + <div id="[% nsm_prefix %]drag"> + [%- FOR row IN map.tiles -%] + [%- FOR tile IN row -%] + [%- top_px = tile.row_offset * map.tile_size -%] + [%- left_px = tile.col_offset * map.tile_size %] + <[% map.img_type %] + class="square-map__tile" + alt="[% tile.alt %]" + id="[% nsm_prefix %]t[% tile.dotted_id %]" + name="tile_[% tile.dotted_id %]" + src="[% tile.src %]" + style="width: [% 100 / map.cols %]%; height: auto; float: left;"> + [%- END -%] + [% END %] + </div> + <div id="[% nsm_prefix %]pins">[% FOR pin IN map.pins %][% INCLUDE pin %][% END %]</div> + [% INCLUDE compass %] + </div> +</div> + +[% BLOCK compass %] +[% + north = c.uri_with( { lat = map.compass.north.0, lon = map.compass.north.1, zoom = map.zoom } ) + south = c.uri_with( { lat = map.compass.south.0, lon = map.compass.south.1, zoom = map.zoom } ) + east = c.uri_with( { lat = map.compass.east.0, lon = map.compass.east.1, zoom = map.zoom } ) + west = c.uri_with( { lat = map.compass.west.0, lon = map.compass.west.1, zoom = map.zoom } ) + world = c.uri_with( { zoom = 0 } ); + SET zoom_in = c.uri_with( { lat = map.latitude, lon = map.longitude, zoom = map.zoom + 1 } ) IF map.zoom < map.numZoomLevels - 1; + SET zoom_out = c.uri_with( { lat = map.latitude, lon = map.longitude, zoom = map.zoom - 1 } ) IF map.zoom > 0; + SET zoom_in = '#' IF map.zoom >= map.numZoomLevels - 1; + SET zoom_out = '#' IF map.zoom <= 0; +%] +<div style="position: absolute; left: 4px; top: 4px;" class="olControlPanZoom olControlNoSelect" unselectable="on"> + <div style="position: absolute; left: 13px; top: 4px; width: 18px; height: 18px;"><a rel="nofollow" href="[% north %]"><img style="position: relative; width: 18px; height: 18px;" src="/js/OpenLayers-2.13.1/img/north-mini.png" border="0"></a></div> + <div style="position: absolute; left: 4px; top: 22px; width: 18px; height: 18px;"><a rel="nofollow" href="[% west %]"><img style="position: relative; width: 18px; height: 18px;" src="/js/OpenLayers-2.13.1/img/west-mini.png" border="0"></a></div> + <div style="position: absolute; left: 22px; top: 22px; width: 18px; height: 18px;"><a rel="nofollow" href="[% east %]"><img style="position: relative; width: 18px; height: 18px;" src="/js/OpenLayers-2.13.1/img/east-mini.png" border="0"></a></div> + <div style="position: absolute; left: 13px; top: 40px; width: 18px; height: 18px;"><a rel="nofollow" href="[% south %]"><img style="position: relative; width: 18px; height: 18px;" src="/js/OpenLayers-2.13.1/img/south-mini.png" border="0"></a></div> + <div style="position: absolute; left: 13px; top: 63px; width: 18px; height: 18px;"><a rel="nofollow" href="[% zoom_in %]"><img style="position: relative; width: 18px; height: 18px;" src="/js/OpenLayers-2.13.1/img/zoom-plus-mini.png" border="0"></a></div> + <div style="position: absolute; left: 13px; top: 81px; width: 18px; height: 18px;"><a rel="nofollow" href="[% world %]"><img style="position: relative; width: 18px; height: 18px;" src="/js/OpenLayers-2.13.1/img/zoom-world-mini.png" border="0"></a></div> + <div style="position: absolute; left: 13px; top: 99px; width: 18px; height: 18px;"><a rel="nofollow" href="[% zoom_out %]"><img style="position: relative; width: 18px; height: 18px;" src="/js/OpenLayers-2.13.1/img/zoom-minus-mini.png" border="0"></a></div> +</div> +[% END %] + + +[% BLOCK pin %] + +[% IF pin.id %] +<a title="[% pin.title | html %]" href="[% c.uri_for('/report/' _ pin.id) %]"> +[%- END -%] +<img + data-foo="[% pin.px %],[% pin.py %]/ [% map.tile_size %]*[% map.cols %],[% map.rows %]" + border="0" + class="pin" + src="[% c.uri_for( c.cobrand.path_to_pin_icons _ 'pin-' _ pin.colour _ '.png') %]" + alt="[% loc('Problem') %]" + style=" + top: [% pin.py / ( map.tile_size * map.rows ) * 100 %]%; + left: [% pin.px / ( map.tile_size * map.cols ) * 100 %]%; + position: absolute; + margin-left: -24px; /* Half of 48px wide image */ + margin-top: -64px; /* All of 64px tall image */ + " +> +[%- IF pin.id -%] +</a> +[% END %] + +[% END %] diff --git a/templates/web/bristol/maps/openlayers.html b/templates/web/bristol/maps/openlayers.html new file mode 100644 index 000000000..4b178e21a --- /dev/null +++ b/templates/web/bristol/maps/openlayers.html @@ -0,0 +1,49 @@ +[% IF map.clickable %] + [% map.img_type = 'input type="image"' %] +[% ELSE %] + [% map.img_type = 'img' %] +[% END %] + +<input type="hidden" name="zoom" value="[% map.zoom %]"> +<script type="text/javascript"> +var fixmystreet = { + 'page': '[% page %]', + 'area': [ [% map.area.join(',') %] ], + 'all_pins': '[% all_pins %]', + 'latitude': [% map.latitude %], + 'longitude': [% map.longitude %], +[% IF map.any_zoom -%] + 'zoomToBounds': 1, +[%- END %] +[% IF map.zoom -%] + 'zoom': [% map.zoom %], +[%- END %] + 'pin_prefix': '[% c.cobrand.path_to_pin_icons %]', + 'numZoomLevels': [% map.numZoomLevels %], + 'zoomOffset': [% map.zoomOffset %], + 'map_type': [% map.map_type %], + 'pins': [% INCLUDE maps/pins_js.html %], + + 'wmts_config': { + 'map_projection': '[% map.map_projection %]', + 'tile_dpi': [% map.tile_dpi %], + 'tile_url': '[% map.tile_url %]', + 'tile_suffix': '[% map.tile_suffix %]', + 'layer_name': '[% map.layer_name %]', + 'layer_style': '[% map.layer_style %]', + 'matrix_set': '[% map.matrix_set %]', + 'scales': [% map.scales %], + 'origin_x': [% map.origin_x %], + 'origin_y': [% map.origin_y %] + } +} +</script> +<div id="map_box" aria-hidden="true"> + [% pre_map %] + <div id="map"> + [% INCLUDE 'maps/noscript_map.html' %] + </div> + [% IF map.copyright %] + <div class="olControlAttribution" style="position: absolute;">[% map.copyright %]</div> + [% END %] + diff --git a/web/cobrands/bristol/base.scss b/web/cobrands/bristol/base.scss index b5cb6b85e..746e17064 100644 --- a/web/cobrands/bristol/base.scss +++ b/web/cobrands/bristol/base.scss @@ -125,3 +125,7 @@ a#geolocate_link { label { @extend %bold-font; } + +#map #drag .square-map__tile { + position: static; +} diff --git a/web/js/map-wmts-bristol.js b/web/js/map-wmts-bristol.js new file mode 100644 index 000000000..a1889beed --- /dev/null +++ b/web/js/map-wmts-bristol.js @@ -0,0 +1,176 @@ +/* + * Maps for FMS using Bristol City Council's WMTS tile server + */ + +// From the 'fullExtent' key from http://maps.bristol.gov.uk/arcgis/rest/services/base/2015_BCC_96dpi/MapServer?f=pjson +var layer_bounds = new OpenLayers.Bounds( + 268756.31099999975, // W + 98527.70309999958, // S + 385799.51099999994, // E + 202566.10309999995); // N + +var matrix_ids = [ + { + "identifier": "0", + "supportedCRS": "urn:ogc:def:crs:EPSG::27700", + "scaleDenominator": 181428.9342864172, + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 432, + "matrixHeight": 337 + }, + { + "identifier": "1", + "supportedCRS": "urn:ogc:def:crs:EPSG::27700", + "scaleDenominator": 90714.4671432086, + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 863, + "matrixHeight": 673 + }, + { + "identifier": "2", + "supportedCRS": "urn:ogc:def:crs:EPSG::27700", + "scaleDenominator": 45357.2335716043, + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 1725, + "matrixHeight": 1345 + }, + { + "identifier": "3", + "supportedCRS": "urn:ogc:def:crs:EPSG::27700", + "scaleDenominator": 22678.61678580215, + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 3449, + "matrixHeight": 2690 + }, + { + "identifier": "4", + "supportedCRS": "urn:ogc:def:crs:EPSG::27700", + "scaleDenominator": 11339.308392901075, + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 6898, + "matrixHeight": 5379 + }, + { + "identifier": "5", + "supportedCRS": "urn:ogc:def:crs:EPSG::27700", + "scaleDenominator": 5669.654196450538, + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 13795, + "matrixHeight": 10758 + }, + { + "identifier": "6", + "supportedCRS": "urn:ogc:def:crs:EPSG::27700", + "scaleDenominator": 2834.827098225269, + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 27590, + "matrixHeight": 21515 + }, + { + "identifier": "7", + "supportedCRS": "urn:ogc:def:crs:EPSG::27700", + "scaleDenominator": 1181.177957593862, + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 66215, + "matrixHeight": 51634 + }, + { + "identifier": "8", + "supportedCRS": "urn:ogc:def:crs:EPSG::27700", + "scaleDenominator": 708.7067745563172, + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 110359, + "matrixHeight": 86057 + } +]; + +/* + * set_map_config() is called on dom ready in map-OpenLayers.js + * to setup the way the map should operate. + */ + function set_map_config(perm) { + // This stuff is copied from js/map-bing-ol.js + + var nav_opts = { zoomWheelEnabled: false }; + if (fixmystreet.page == 'around' && $('html').hasClass('mobile')) { + nav_opts = {}; + } + fixmystreet.nav_control = new OpenLayers.Control.Navigation(nav_opts); + + fixmystreet.controls = [ + new OpenLayers.Control.ArgParser(), + fixmystreet.nav_control + ]; + if ( fixmystreet.page != 'report' || !$('html').hasClass('mobile') ) { + fixmystreet.controls.push( new OpenLayers.Control.PanZoomFMS({id: 'fms_pan_zoom' }) ); + } + + /* Linking back to around from report page, keeping track of map moves */ + if ( fixmystreet.page == 'report' ) { + fixmystreet.controls.push( new OpenLayers.Control.PermalinkFMS('key-tool-problems-nearby', '/around') ); + } + + fixmystreet.map_type = OpenLayers.Layer.WMTS; + + // Set DPI - default is 72 + OpenLayers.DOTS_PER_INCH = fixmystreet.wmts_config.tile_dpi; + + fixmystreet.map_options = { + maxExtent: layer_bounds, + units: 'm', + scales: fixmystreet.wmts_config.scales + }; + + fixmystreet.layer_options = [{ + projection: new OpenLayers.Projection(fixmystreet.wmts_config.map_projection), + name: fixmystreet.wmts_config.layer_name, + layer: fixmystreet.wmts_config.layer_name, + formatSuffix: fixmystreet.wmts_config.tile_suffix.replace(".", ""), + matrixSet: fixmystreet.wmts_config.matrix_set, + requestEncoding: "REST", + url: fixmystreet.wmts_config.tile_url, + style: fixmystreet.wmts_config.layer_style, + matrixIds: matrix_ids, + tileOrigin: new OpenLayers.LonLat(fixmystreet.wmts_config.origin_x, fixmystreet.wmts_config.origin_y) + }]; + + // Give main code a new bbox_strategy that translates between + // lat/lon and our WMTS layer's coordinates + fixmystreet.bbox_strategy = new OpenLayers.Strategy.ReprojectBBOX({ratio: 1}); +} + +OpenLayers.Strategy.ReprojectBBOX = OpenLayers.Class(OpenLayers.Strategy.BBOX, { + getMapBounds: function() { + // Get the map bounds but return them in lat/lon, not + // local coordinates + if (this.layer.map === null) { + return null; + } + + var localBounds = this.layer.map.getExtent(); + // Transform bound corners into WGS84 + localBounds.transform( new OpenLayers.Projection(fixmystreet.wmts_config.map_projection), new OpenLayers.Projection("EPSG:4326") ); + return localBounds; + }, + + CLASS_NAME: "OpenLayers.Strategy.ReprojectBBOX" +}); + +function fms_marker_size_for_zoom(zoom) { + if (zoom >= 7) { + return 'normal'; + } else if (zoom >= 4) { + return 'small'; + } else { + return 'mini'; + } +} |