diff options
author | Matthew Somerville <matthew@mysociety.org> | 2013-06-06 23:10:13 +0100 |
---|---|---|
committer | Matthew Somerville <matthew@mysociety.org> | 2013-06-06 23:32:25 +0100 |
commit | ca9350dd8b247e262c6860897256b40b911610ec (patch) | |
tree | 614fbdf88bfdced8954a7d2cf84913b92849c916 | |
parent | 510317a4bedfcabebab7329b0b5c7809a8c5f1c1 (diff) |
Improve Google Maps support somewhat.
-rw-r--r-- | perllib/FixMyStreet/Map/Google.pm | 35 | ||||
-rw-r--r-- | templates/web/default/maps/google.html | 20 | ||||
-rw-r--r-- | web/js/map-google.js | 327 |
3 files changed, 370 insertions, 12 deletions
diff --git a/perllib/FixMyStreet/Map/Google.pm b/perllib/FixMyStreet/Map/Google.pm index c0d83e35a..9deefc033 100644 --- a/perllib/FixMyStreet/Map/Google.pm +++ b/perllib/FixMyStreet/Map/Google.pm @@ -3,12 +3,17 @@ # FixMyStreet:Map::Google # Google maps on FixMyStreet. # -# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. +# Copyright (c) 2013 UK Citizens Online Democracy. All rights reserved. # Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ package FixMyStreet::Map::Google; use strict; +use mySociety::Gaze; +use Utils; + +use constant ZOOM_LEVELS => 6; +use constant MIN_ZOOM_LEVEL => 13; # display_map C PARAMS # PARAMS include: @@ -17,9 +22,37 @@ use strict; # 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 = 19; + $zoomOffset = 0; + } + + # Adjust zoom level dependent upon population density + my $dist = $c->stash->{distance} + || mySociety::Gaze::get_radius_containing_population( $params{latitude}, $params{longitude}, 200_000 ); + my $default_zoom = $c->cobrand->default_map_zoom() ? $c->cobrand->default_map_zoom() : $numZoomLevels - 4; + $default_zoom = $numZoomLevels - 3 if $dist < 10; + + # 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; + $c->stash->{map} = { %params, type => 'google', + zoom => $zoom, + zoomOffset => $zoomOffset, + numZoomLevels => $numZoomLevels, }; } diff --git a/templates/web/default/maps/google.html b/templates/web/default/maps/google.html index 69020ea81..741edec40 100644 --- a/templates/web/default/maps/google.html +++ b/templates/web/default/maps/google.html @@ -1,13 +1,31 @@ [% map_js = BLOCK %] -<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> +<style> +#map_box img { + max-width: none; +} +#map_box { + color: #000; +} +</style> +<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script> <script type="text/javascript" src="[% version('/js/map-google.js') %]"></script> +<script type="text/javascript" src="[% version('/js/jquery.ba-hashchange.min.js') %]"></script> [% END %] [% map_html = BLOCK %] <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 %] 'pins': [% INCLUDE maps/pins_js.html %] } </script> diff --git a/web/js/map-google.js b/web/js/map-google.js index 742b55d47..d03223d14 100644 --- a/web/js/map-google.js +++ b/web/js/map-google.js @@ -1,22 +1,329 @@ -$(function(){ +/* + XXX Lots overlap with map-OpenLayers.js - refactor! + XXX Things still need to be changed for mobile use, probably won't work + there. + TODO Pin size on report page +*/ + +function PaddingControl(div) { + div.style.padding = '40px'; +} + +function fixmystreet_update_pin(lonlat) { + document.getElementById('fixmystreet.latitude').value = lonlat.lat(); + document.getElementById('fixmystreet.longitude').value = lonlat.lng(); + + $.getJSON('/report/new/ajax', { + latitude: $('#fixmystreet\\.latitude').val(), + longitude: $('#fixmystreet\\.longitude').val() + }, function(data) { + if (data.error) { + if (!$('#side-form-error').length) { + $('<div id="side-form-error"/>').insertAfter($('#side-form')); + } + $('#side-form-error').html('<h1>' + translation_strings.reporting_a_problem + '</h1><p>' + data.error + '</p>').show(); + $('#side-form').hide(); + return; + } + $('#side-form, #site-logo').show(); + $('#councils_text').html(data.councils_text); + $('#form_category_row').html(data.category); + if ( data.extra_name_info && !$('#form_fms_extra_title').length ) { + // there might be a first name field on some cobrands + var lb = $('#form_first_name').prev(); + if ( lb.length === 0 ) { lb = $('#form_name').prev(); } + lb.before(data.extra_name_info); + } + }); + + if (!$('#side-form-error').is(':visible')) { + $('#side-form, #site-logo').show(); + } +} + +var infowindow = new google.maps.InfoWindow(); +function make_infowindow(marker) { + return function() { + infowindow.setContent(marker.title + "<br><a href=/report/" + marker.id + ">" + translation_strings.more_details + "</a>"); + infowindow.open(fixmystreet.map, marker); + }; +} + +function fms_markers_list(pins, transform) { + var markers = []; + if (fixmystreet.markers) { + for (var m=0; m<fixmystreet.markers.length; m++) { + fixmystreet.markers[m].setMap(null); + } + } + for (var i=0; i<pins.length; i++) { + var pin = pins[i]; + var pin_args = { + position: new google.maps.LatLng( pin[0], pin[1] ), + //size: pin[5] || 'normal', + id: pin[3], + title: pin[4] || '', + map: fixmystreet.map + }; + if (pin[2] == 'green') { + pin_args.icon = "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|87dd00"; + } + if (pin[2] == 'yellow') { + pin_args.icon = "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|ffd600"; + } + var marker = new google.maps.Marker(pin_args); + if (fixmystreet.page == 'around' || fixmystreet.page == 'reports' || fixmystreet.page == 'my') { + var l = new google.maps.event.addListener(marker, 'click', make_infowindow(marker)); + } + markers.push( marker ); + } + return markers; +} + +function fms_map_clicked(e) { + var lonlat = e.latLng; + if (fixmystreet.page == 'new') { + /* Already have a pin */ + fixmystreet.report_marker.setPosition(lonlat); + } else { + var m = new google.maps.Marker({ + position: lonlat, + draggable: true, + animation: google.maps.Animation.DROP, + map: fixmystreet.map + }); + var l = google.maps.event.addListener(m, 'dragend', function(){ + fixmystreet_update_pin( m.getPosition() ); + }); + fixmystreet.report_marker = m; + google.maps.event.removeListener(fixmystreet.event_update_map); + for (m=0; m<fixmystreet.markers.length; m++) { + fixmystreet.markers[m].setMap(null); + } + } + + // check to see if markers are visible. We click the + // link so that it updates the text in case they go + // back + if ( ! 1 ) { // XXX fixmystreet.markers.getVisibility() ) + fixmystreet.state_pins_were_hidden = true; + $('#hide_pins_link').click(); + } + + // Store pin location in form fields, and check coverage of point + fixmystreet_update_pin(lonlat); + + // Already did this first time map was clicked, so no need to do it again. + if (fixmystreet.page == 'new') { + return; + } + + $('#side').hide(); + if (typeof heightFix !== 'undefined') { + heightFix('#report-a-problem-sidebar', '.content', 26); + } + + // If we clicked the map somewhere inconvenient + // TODO + + $('#sub_map_links').hide(); + fixmystreet.page = 'new'; + location.hash = 'report'; +} + +/* Pan data handler */ +function fms_read_pin_json(obj) { + var current, current_near; + if (typeof(obj.current) != 'undefined' && (current = document.getElementById('current'))) { + current.innerHTML = obj.current; + } + if (typeof(obj.current_near) != 'undefined' && (current_near = document.getElementById('current_near'))) { + current_near.innerHTML = obj.current_near; + } + fixmystreet.markers = fms_markers_list( obj.pins, false ); +} + +function fms_update_pins() { + var b = fixmystreet.map.getBounds(), + b_sw = b.getSouthWest(), + b_ne = b.getNorthEast(), + bbox = b_sw.lng() + ',' + b_sw.lat() + ',' + b_ne.lng() + ',' + b_ne.lat(), + params = { + bbox: bbox + }; + if (fixmystreet.all_pins) { + params.all_pins = 1; + } + $.getJSON('/ajax', params, fms_read_pin_json); +} + +function fms_map_initialize() { var centre = new google.maps.LatLng( fixmystreet.latitude, fixmystreet.longitude ); - var map = new google.maps.Map(document.getElementById("map"), { + var map_args = { mapTypeId: google.maps.MapTypeId.ROADMAP, center: centre, - zoom: 16, + zoom: 13 + fixmystreet.zoom, disableDefaultUI: true, - navigationControl: true, - navigationControlOptions: { - style: google.maps.NavigationControlStyle.SMALL + panControl: true, + panControlOptions: { + position: google.maps.ControlPosition.RIGHT_TOP + }, + zoomControl: true, + zoomControlOptions: { + position: google.maps.ControlPosition.RIGHT_TOP }, mapTypeControl: true, mapTypeControlOptions: { + position: google.maps.ControlPosition.RIGHT_TOP, style: google.maps.MapTypeControlStyle.DROPDOWN_MENU } + }; + if (!fixmystreet.zoomToBounds) { + map_args.minZoom = 13; + map_args.maxZoom = 18; + } + fixmystreet.map = new google.maps.Map(document.getElementById("map"), map_args); + + /* Space above the top right controls */ + var paddingDiv = document.createElement('div'); + var paddingControl = new PaddingControl(paddingDiv); + paddingDiv.index = 0; + fixmystreet.map.controls[google.maps.ControlPosition.RIGHT_TOP].push(paddingDiv); + + if (fixmystreet.state_map && fixmystreet.state_map == 'full') { + // TODO Work better with window resizing, this is pretty 'set up' only at present + var $content = $('.content'), mb = $('#map_box'), + q = ( $content.offset().left - mb.offset().left + $content.width() ) / 2; + if (q < 0) { q = 0; } + // Need to try and fake the 'centre' being 75% from the left + fixmystreet.map.panBy(-q, -25); + } + + if (document.getElementById('mapForm')) { + var l = google.maps.event.addListener(fixmystreet.map, 'click', fms_map_clicked); + } + + $(window).hashchange(function(){ + if (location.hash == '#report' && $('.rap-notes').is(':visible')) { + $('.rap-notes-close').click(); + return; + } + + if (location.hash && location.hash != '#') { + return; + } + + // Okay, back to around view. + fixmystreet.report_marker.setMap(null); + fixmystreet.event_update_map = google.maps.event.addListener(fixmystreet.map, 'idle', fms_update_pins); + google.maps.event.trigger(fixmystreet.map, 'idle'); + if ( fixmystreet.state_pins_were_hidden ) { + // If we had pins hidden when we clicked map (which had to show the pin layer as I'm doing it in one layer), hide them again. + $('#hide_pins_link').click(); + } + $('#side-form').hide(); + $('#side').show(); + $('#sub_map_links').show(); + //only on mobile + $('#mob_sub_map_links').remove(); + $('.mobile-map-banner').html('<a href="/">' + translation_strings.home + '</a> ' + translation_strings.place_pin_on_map); + fixmystreet.page = 'around'; + }); + + if ( fixmystreet.area.length ) { + for (var i=0; i<fixmystreet.area.length; i++) { + var args = { + url: "http://mapit.mysociety.org/area/" + fixmystreet.area[i] + ".kml?simplify_tolerance=0.0001", + clickable: false, + preserveViewport: true, + map: fixmystreet.map + }; + if ( fixmystreet.area.length == 1 ) { + args.preserveViewport = false; + } + var a = new google.maps.KmlLayer(args); + a.setMap(fixmystreet.map); + } + } + + if (fixmystreet.page == 'around') { + fixmystreet.event_update_map = google.maps.event.addListener(fixmystreet.map, 'idle', fms_update_pins); + } + + fixmystreet.markers = fms_markers_list( fixmystreet.pins, true ); + + /* + if ( fixmystreet.zoomToBounds ) { + var bounds = fixmystreet.markers.getDataExtent(); + if (bounds) { + var center = bounds.getCenterLonLat(); + var z = fixmystreet.map.getZoomForExtent(bounds); + if ( z < 13 && $('html').hasClass('mobile') ) { + z = 13; + } + fixmystreet.map.setCenter(center, z); + } + } + */ + + $('#hide_pins_link').click(function(e) { + var i, m; + e.preventDefault(); + var showhide = [ + 'Show pins', 'Hide pins', + 'Dangos pinnau', 'Cuddio pinnau', + "Vis nåler", "Gjem nåler", + "Zeige Stecknadeln", "Stecknadeln ausblenden" + ]; + for (i=0; i<showhide.length; i+=2) { + if (this.innerHTML == showhide[i]) { + for (m=0; m<fixmystreet.markers.length; m++) { + fixmystreet.markers[m].setMap(fixmystreet.map); + } + this.innerHTML = showhide[i+1]; + } else if (this.innerHTML == showhide[i+1]) { + for (m=0; m<fixmystreet.markers.length; m++) { + fixmystreet.markers[m].setMap(null); + } + this.innerHTML = showhide[i]; + } + } }); - google.maps.event.addListener(map, "zoom_changed", function() { - if (map.getZoom() < 13) map.setZoom(13); - if (map.getZoom() > 17) map.setZoom(17); + $('#all_pins_link').click(function(e) { + var i; + e.preventDefault(); + for (i=0; i<fixmystreet.markers.length; i++) { + fixmystreet.markers[i].setMap(fixmystreet.map); + } + var texts = [ + 'en', 'Show old', 'Hide old', + 'nb', 'Inkluder utdaterte problemer', 'Skjul utdaterte rapporter', + 'cy', 'Cynnwys hen adroddiadau', 'Cuddio hen adroddiadau' + ]; + for (i=0; i<texts.length; i+=3) { + if (this.innerHTML == texts[i+1]) { + this.innerHTML = texts[i+2]; + fixmystreet.markers.protocol.options.params = { all_pins: 1 }; + fixmystreet.markers.refresh( { force: true } ); + lang = texts[i]; + } else if (this.innerHTML == texts[i+2]) { + this.innerHTML = texts[i+1]; + fixmystreet.markers.protocol.options.params = { }; + fixmystreet.markers.refresh( { force: true } ); + lang = texts[i]; + } + } + if (lang == 'cy') { + document.getElementById('hide_pins_link').innerHTML = 'Cuddio pinnau'; + } else if (lang == 'nb') { + document.getElementById('hide_pins_link').innerHTML = 'Gjem nåler'; + } else { + document.getElementById('hide_pins_link').innerHTML = 'Hide pins'; + } }); -}); + +} + +google.maps.visualRefresh = true; +google.maps.event.addDomListener(window, 'load', fms_map_initialize); |