var fixmystreet = fixmystreet || {}; (function() { fixmystreet.maps = fixmystreet.maps || {}; $.extend(fixmystreet.maps, { // This function might be passed either an OpenLayers.LonLat (so has // lon and lat), or an OpenLayers.Geometry.Point (so has x and y). update_pin: function(lonlat) { var transformedLonlat = lonlat.clone().transform( fixmystreet.map.getProjectionObject(), new OpenLayers.Projection("EPSG:4326") ); var lat = transformedLonlat.lat || transformedLonlat.y; var lon = transformedLonlat.lon || transformedLonlat.x; document.getElementById('fixmystreet.latitude').value = lat; document.getElementById('fixmystreet.longitude').value = lon; return { 'url': { 'lon': lon, 'lat': lat }, 'state': { 'lon': lonlat.lon, 'lat': lonlat.lat } }; }, display_around: function() { // Required after changing the size of the map element fixmystreet.map.updateSize(); // Dragging the map should fetch new local reports from server if (fixmystreet.bbox_strategy) { fixmystreet.bbox_strategy.activate(); } // Should not be able to drag normal pins!! drag.deactivate(); // Force a redraw to return (de)selected marker to normal size // Redraw for all pages, kick off a refresh too for around // TODO Put 'new report' pin in different layer to simplify this and elsewhere fixmystreet.maps.markers_resize(); fixmystreet.markers.refresh({force: true}); }, begin_report: function(lonlat) { if (typeof lonlat.clone !== 'function') { lonlat = new OpenLayers.LonLat(lonlat.lon, lonlat.lat); } if (fixmystreet.page == 'new') { /* Already have a pin */ fixmystreet.markers.features[0].move(lonlat); } else { var markers = fixmystreet.maps.markers_list( [ [ lonlat.lat, lonlat.lon, fixmystreet.pin_new_report_colour ] ], false ); fixmystreet.bbox_strategy.deactivate(); fixmystreet.markers.removeAllFeatures(); fixmystreet.markers.addFeatures( markers ); drag.activate(); } // check to see if markers are visible. We click the // link so that it updates the text in case they go // back if ( ! fixmystreet.markers.getVisibility() ) { $('#hide_pins_link').click(); } return lonlat; }, markers_list: function(pins, transform) { var markers = []; var size = fixmystreet.maps.marker_size(); var selected_size = fixmystreet.maps.selected_marker_size(); for (var i=0; i= 15) { return window.selected_problem_id ? 'small' : 'normal'; } else if (zoom >= 13) { return window.selected_problem_id ? 'mini' : 'small'; } else { return 'mini'; } }, selected_marker_size: function() { var zoom = fixmystreet.map.getZoom() + fixmystreet.zoomOffset; if (zoom >= 15) { return 'big'; } else if (zoom >= 13) { return 'normal'; } else { return 'small'; } }, // Handle a single report pin being moved by dragging it on the map. // pin_moved_callback is called with a new EPSG:4326 OpenLayers.LonLat if // the user drags the pin and confirms its new location. admin_drag: function(pin_moved_callback, confirm_change) { confirm_change = confirm_change || false; var original_lonlat; var drag = new OpenLayers.Control.DragFeatureFMS( fixmystreet.markers, { onStart: function(feature, e) { // Keep track of where the feature started, so we can put it // back if the user cancels the operation. original_lonlat = new OpenLayers.LonLat(feature.geometry.x, feature.geometry.y); }, onComplete: function(feature, e) { var lonlat = feature.geometry.clone(); lonlat.transform( fixmystreet.map.getProjectionObject(), new OpenLayers.Projection("EPSG:4326") ); if ((confirm_change && window.confirm(translation_strings.correct_position)) || !confirm_change) { // Let the callback know about the newly confirmed position pin_moved_callback(lonlat); } else { // Put it back fixmystreet.markers.features[0].move(original_lonlat); } } } ); fixmystreet.map.addControl( drag ); drag.activate(); }, // `markers.redraw()` in markers_highlight will trigger an // `overFeature` event if the mouse cursor is still over the same // marker on the map, which would then run markers_highlight // again, causing an infinite flicker while the cursor remains over // the same marker. We really only want to redraw the markers when // the cursor moves from one marker to another (ie: when there is an // overFeature followed by an outFeature followed by an overFeature). // Therefore, we keep track of the previous event in // fixmystreet.latest_map_hover_event and only call markers_highlight // if we know the previous event was different to the current one. // (See the `overFeature` and `outFeature` callbacks inside of // fixmystreet.select_feature). markers_highlight: function(problem_id) { for (var i = 0; i < fixmystreet.markers.features.length; i++) { if (typeof problem_id == 'undefined') { // There is no highlighted marker, so unfade this marker fixmystreet.markers.features[i].attributes.faded = 0; } else if (problem_id == fixmystreet.markers.features[i].attributes.id) { // This is the highlighted marker, unfade it fixmystreet.markers.features[i].attributes.faded = 0; } else { // This is not the hightlighted marker, fade it fixmystreet.markers.features[i].attributes.faded = 1; } } fixmystreet.markers.redraw(); } }); var drag = { activate: function() { this._drag = new OpenLayers.Control.DragFeatureFMS( fixmystreet.markers, { onComplete: function(feature, e) { fixmystreet.update_pin( feature.geometry ); } } ); fixmystreet.map.addControl( this._drag ); this._drag.activate(); }, deactivate: function() { if (this._drag) { this._drag.deactivate(); } } }; /* Make sure pins aren't going to reload just because we're zooming out, * we already have the pins when the page loaded */ function zoomToBounds(bounds) { if (!bounds) { return; } fixmystreet.markers.strategies[0].deactivate(); var center = bounds.getCenterLonLat(); var z = fixmystreet.map.getZoomForExtent(bounds); if ( z < 13 && $('html').hasClass('mobile') ) { z = 13; } fixmystreet.map.setCenter(center, z); // Reactivate the strategy and make it think it's done an update fixmystreet.markers.strategies[0].activate(); fixmystreet.markers.strategies[0].calculateBounds(); fixmystreet.markers.strategies[0].resolution = fixmystreet.map.getResolution(); } function sidebar_highlight(problem_id) { if (typeof problem_id !== 'undefined') { var $a = $('.item-list--reports a[href$="/' + problem_id + '"]'); $a.parent().addClass('hovered'); } else { $('.item-list--reports .hovered').removeClass('hovered'); } } function marker_click(problem_id, evt) { var $a = $('.item-list--reports a[href$="/' + problem_id + '"]'); if (!$a[0]) { return; } // All of this, just so that ctrl/cmd-click on a pin works?! var event; if (typeof window.MouseEvent === 'function') { event = new MouseEvent('click', evt); $a[0].dispatchEvent(event); } else if (document.createEvent) { event = document.createEvent("MouseEvents"); event.initMouseEvent( 'click', true, true, window, 1, 0, 0, 0, 0, evt.ctrlKey, evt.altKey, evt.shiftKey, evt.metaKey, 0, null); $a[0].dispatchEvent(event); } else if (document.createEventObject) { event = document.createEventObject(); event.metaKey = evt.metaKey; event.ctrlKey = evt.ctrlKey; if (event.metaKey === undefined) { event.metaKey = event.ctrlKey; } $a[0].fireEvent("onclick", event); } else { $a[0].click(); } } function categories_or_status_changed() { // If the category or status has changed we need to re-fetch map markers fixmystreet.markers.refresh({force: true}); } function parse_query_string() { var qs = {}; if (!location.search) { return qs; } location.search.substring(1).split('&').forEach(function(i) { var s = i.split('='), k = s[0], v = s[1] && decodeURIComponent(s[1].replace(/\+/g, ' ')); qs[k] = v; }); return qs; } function replace_query_parameter(qs, id, key) { var value = $('#' + id).val(); if (value) { qs[key] = (typeof value === 'string') ? value : value.join(','); } else { delete qs[key]; } return value; } function categories_or_status_changed_history() { if (!('pushState' in history)) { return; } var qs = parse_query_string(); var filter_categories = replace_query_parameter(qs, 'filter_categories', 'filter_category'); var filter_statuses = replace_query_parameter(qs, 'statuses', 'status'); var sort_key = replace_query_parameter(qs, 'sort', 'sort'); delete qs.p; var new_url; if ($.isEmptyObject(qs)) { new_url = location.href.replace(location.search, ""); } else if (location.search) { new_url = location.href.replace(location.search, '?' + $.param(qs)); } else { new_url = location.href + '?' + $.param(qs); } history.pushState({ filter_change: { 'filter_categories': filter_categories, 'statuses': filter_statuses, 'sort': sort_key } }, null, new_url); } function setup_inspector_marker_drag() { // On the 'inspect report' page the pin is draggable, so we need to // update the easting/northing fields when it's dragged. if (!$('form#report_inspect_form').length) { // Not actually on the inspect report page return; } fixmystreet.maps.admin_drag(function(lonlat) { var bng = lonlat.clone().transform( new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:27700") // TODO: Handle other projections ); $("#problem_northing").text(bng.y.toFixed(1)); $("#problem_easting").text(bng.x.toFixed(1)); $("#problem_latitude").text(lonlat.y.toFixed(6)); $("#problem_longitude").text(lonlat.x.toFixed(6)); $("form#report_inspect_form input[name=latitude]").val(lonlat.y); $("form#report_inspect_form input[name=longitude]").val(lonlat.x); }, false); } function onload() { if ( fixmystreet.area.length ) { var extent = new OpenLayers.Bounds(); var lr = new OpenLayers.Geometry.LinearRing([ new OpenLayers.Geometry.Point(20E6,20E6), new OpenLayers.Geometry.Point(10E6,20E6), new OpenLayers.Geometry.Point(0,20E6), new OpenLayers.Geometry.Point(-10E6,20E6), new OpenLayers.Geometry.Point(-20E6,20E6), new OpenLayers.Geometry.Point(-20E6,0), new OpenLayers.Geometry.Point(-20E6,-20E6), new OpenLayers.Geometry.Point(-10E6,-20E6), new OpenLayers.Geometry.Point(0,-20E6), new OpenLayers.Geometry.Point(10E6,-20E6), new OpenLayers.Geometry.Point(20E6,-20E6), new OpenLayers.Geometry.Point(20E6,0) ]); var loaded = 0; var new_geometry = new OpenLayers.Geometry.Polygon(lr); var style_area = function() { loaded++; var style = this.styleMap.styles['default']; if ( fixmystreet.area_format ) { style.defaultStyle = fixmystreet.area_format; } else { $.extend(style.defaultStyle, { fillColor: 'black', strokeColor: 'black' }); } var geometry = this.features[0].geometry; if (geometry.CLASS_NAME == 'OpenLayers.Geometry.Collection') { $.each(geometry.components, function(i, polygon) { new_geometry.addComponents(polygon.components); extent.extend(polygon.getBounds()); }); } else if (geometry.CLASS_NAME == 'OpenLayers.Geometry.Polygon') { new_geometry.addComponents(geometry.components); extent.extend(this.getDataExtent()); } if (loaded == fixmystreet.area.length) { var f = this.features[0].clone(); f.geometry = new_geometry; this.removeAllFeatures(); this.addFeatures([f]); var qs = parse_query_string(); if (!qs.bbox) { zoomToBounds(extent); } } else { fixmystreet.map.removeLayer(this); } }; for (var i=0; i