diff options
-rw-r--r-- | web/cobrands/fixmystreet/assets.js | 97 | ||||
-rw-r--r-- | web/cobrands/fixmystreet/fixmystreet.js | 3 | ||||
-rw-r--r-- | web/js/map-OpenLayers.js | 21 |
3 files changed, 105 insertions, 16 deletions
diff --git a/web/cobrands/fixmystreet/assets.js b/web/cobrands/fixmystreet/assets.js index d5dd3bc32..600cd6ab8 100644 --- a/web/cobrands/fixmystreet/assets.js +++ b/web/cobrands/fixmystreet/assets.js @@ -4,6 +4,8 @@ var fixmystreet = fixmystreet || {}; var selected_feature = null; var fault_popup = null; +var selected_usrn = null; +var usrn_field = null; function close_fault_popup() { if (!!fault_popup) { @@ -32,6 +34,11 @@ function asset_selected(e) { return; } + // Pick up the USRN for the location of this asset. NB we do this *before* + // handling the attributes on the selected feature in case the feature has + // its own USRN which should take precedence. + fixmystreet.assets.select_usrn(lonlat); + // Set the extra field to the value of the selected feature $.each(this.fixmystreet.attributes, function (field_name, attribute_name) { var field_value; @@ -164,18 +171,9 @@ function select_nearest_asset() { // No marker to be found so bail out return; } - var closest_feature; - var closest_distance = null; - for (var i = 0; i < this.features.length; i++) { - var candidate = this.features[i]; - var distance = candidate.geometry.distanceTo(marker.geometry); - if (closest_distance === null || distance < closest_distance) { - closest_feature = candidate; - closest_distance = distance; - } - } - if (closest_distance <= threshold && !!closest_feature) { - get_select_control(this).select(closest_feature); + var nearest_feature = this.getNearestFeature(marker.geometry, threshold); + if (nearest_feature) { + get_select_control(this).select(nearest_feature); } } @@ -443,6 +441,35 @@ fixmystreet.assets = { fixmystreet.map.addControl(fixmystreet.assets.controls[i]); fixmystreet.assets.controls[i].activate(); } + }, + + select_usrn: function(lonlat) { + var usrn_providers = fixmystreet.map.getLayersBy('fixmystreet', { + test: function(options) { + return options && options.usrn; + } + }); + if (usrn_providers.length) { + var usrn_layer = usrn_providers[0]; + usrn_field = usrn_layer.fixmystreet.usrn.field; + var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat); + var feature = usrn_layer.getFeatureAtPoint(point); + if (feature == null) { + // The click wasn't directly over a road, try and find one + // nearby + feature = usrn_layer.getNearestFeature(point, 10); + } + if (feature !== null) { + selected_usrn = feature.attributes[usrn_layer.fixmystreet.usrn.attribute]; + } else { + selected_usrn = null; + } + fixmystreet.assets.update_usrn_field(); + } + }, + + update_usrn_field: function() { + $("input[name="+usrn_field+"]").val(selected_usrn); } }; @@ -450,4 +477,50 @@ $(function() { fixmystreet.assets.init(); }); +OpenLayers.Layer.Vector.prototype.getFeatureAtPoint = function(point) { + for (var i = 0; i < this.features.length; i++) { + var feature = this.features[i]; + if (!feature.geometry || !feature.geometry.containsPoint) { + continue; + } + if (feature.geometry.containsPoint(point)) { + return feature; + } + } + return null; +}; + + +/* + * Returns this layer's feature that's closest to the given + * OpenLayers.Geometry.Point, as long as it's within <threshold> metres. + * Returns null if no feature meeting these criteria is found. + */ +OpenLayers.Layer.Vector.prototype.getNearestFeature = function(point, threshold) { + var nearest_feature = null; + var nearest_distance = null; + for (var i = 0; i < this.features.length; i++) { + var candidate = this.features[i]; + if (!candidate.geometry || !candidate.geometry.distanceTo) { + continue; + } + var details = candidate.geometry.distanceTo(point, {details: true}); + if (nearest_distance === null || details.distance < nearest_distance) { + nearest_distance = details.distance; + // The units used for details.distance aren't metres, they're + // whatever the map projection uses. Convert to metres in order to + // draw a meaningful comparison to the threshold value. + var p1 = new OpenLayers.Geometry.Point(details.x0, details.y0); + var p2 = new OpenLayers.Geometry.Point(details.x1, details.y1); + var line = new OpenLayers.Geometry.LineString([p1, p2]); + var distance_m = line.getGeodesicLength(this.map.getProjectionObject()); + + if (distance_m <= threshold) { + nearest_feature = candidate; + } + } + } + return nearest_feature; +}; + })(); diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js index 0aa01e483..c7bbc8cad 100644 --- a/web/cobrands/fixmystreet/fixmystreet.js +++ b/web/cobrands/fixmystreet/fixmystreet.js @@ -417,6 +417,9 @@ $.extend(fixmystreet.set_up, { } else { $category_meta.empty(); } + if (fixmystreet.assets) { + fixmystreet.assets.update_usrn_field(); + } }); if (fixmystreet.hooks.update_problem_fields) { diff --git a/web/js/map-OpenLayers.js b/web/js/map-OpenLayers.js index 0f6cca2b5..5ebb9a18e 100644 --- a/web/js/map-OpenLayers.js +++ b/web/js/map-OpenLayers.js @@ -40,19 +40,32 @@ $.extend(fixmystreet.utils, { }; $.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) { + // This function might be passed either an OpenLayers.LonLat (so has + // lon and lat), or an OpenLayers.Geometry.Point (so has x and y). + if (lonlat.x !== undefined && lonlat.y !== undefined) { + // It's a Point, convert to a LatLon + lonlat = new OpenLayers.LonLat(lonlat.x, lonlat.y); + } + 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; + var lat = transformedLonlat.lat; + var lon = transformedLonlat.lon; document.getElementById('fixmystreet.latitude').value = lat; document.getElementById('fixmystreet.longitude').value = lon; + + // This tight coupling isn't ideal. A better solution would be for the + // asset code to register an event handler somewhere, but the correct + // place isn't apparent. + if (fixmystreet.assets) { + fixmystreet.assets.select_usrn(lonlat); + } + return { 'url': { 'lon': lon, 'lat': lat }, 'state': { 'lon': lonlat.lon, 'lat': lonlat.lat } |