add alternative formats for coordinate search: (#10805)

* zoom/x/y – copy/paste from an osm.org URL or web map with map-hash param (this also sets map zoom to the respective value)
* x/y – like the above, but does not set zoom level
* x y – where x and y are numbers in the user's locale's number format
This commit is contained in:
Martin Raifer
2025-03-11 21:20:21 +01:00
committed by GitHub
parent a33c340356
commit 607f953465
4 changed files with 59 additions and 12 deletions
+2
View File
@@ -38,6 +38,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
# unreleased (v2.33.0-dev)
#### :sparkles: Usability & Accessibility
* Allow searching for coordinates in localized number format in search box ([#10805])
#### :scissors: Operations
#### :camera: Street-Level
#### :white_check_mark: Validation
@@ -49,6 +50,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
#### :mortar_board: Walkthrough / Help
#### :hammer: Development
[#10805]: https://github.com/openstreetmap/iD/pull/10805
[#10299]: https://github.com/openstreetmap/iD/issues/10299
[#10843]: https://github.com/openstreetmap/iD/pull/10843
+7 -5
View File
@@ -120,7 +120,7 @@ export function uiFeatureList(context) {
var result = [];
var graph = context.graph();
var visibleCenter = context.map().extent().center();
var q = search.property('value').toLowerCase();
var q = search.property('value').toLowerCase().trim();
if (!q) return result;
@@ -132,8 +132,9 @@ export function uiFeatureList(context) {
const isLatLonValid = latLon[0] >= -90 && latLon[0] <= 90 && latLon[1] >= -180 && latLon[1] <= 180;
let isLonLatValid = lonLat[0] >= -90 && lonLat[0] <= 90 && lonLat[1] >= -180 && lonLat[1] <= 180;
isLonLatValid &&= !q.match(/[NSEW]/i);
isLonLatValid &&= lonLat[0] !== lonLat[1];
isLonLatValid &&= !q.match(/[NSEW]/i); // don't flip coords with explicit cardinal directions
isLonLatValid &&= !locationMatch[2]; // don't flip zoom/x/y coords
isLonLatValid &&= lonLat[0] !== lonLat[1]; // don't flip when lat=lon
if (isLatLonValid) {
result.push({
@@ -141,7 +142,8 @@ export function uiFeatureList(context) {
geometry: 'point',
type: t('inspector.location'),
name: dmsCoordinatePair([latLon[1], latLon[0]]),
location: latLon
location: latLon,
zoom: locationMatch[2]
});
}
if (isLonLatValid) {
@@ -369,7 +371,7 @@ export function uiFeatureList(context) {
d3_event.preventDefault();
if (d.location) {
context.map().centerZoomEase([d.location[1], d.location[0]], 19);
context.map().centerZoomEase([d.location[1], d.location[0]], d.zoom || 19);
} else if (d.entity) {
utilHighlightEntities([d.id], false, context);
+28 -7
View File
@@ -188,7 +188,7 @@ export function decimalCoordinatePair(coord) {
// Return the parsed value that @mapbox/sexagesimal can't parse
// return value format : [D, D] ex:[ 35.1861, 136.83161 ]
export function dmsMatcher(q) {
export function dmsMatcher(q, _localeCode = undefined) {
const matchers = [
// D M SS , D M SS ex: 35 11 10.1 , 136 49 53.8
{
@@ -199,9 +199,7 @@ export function dmsMatcher(q) {
const lng = (+match[6]) + (+match[7]) / 60 + (+match[8]) / 3600;
const isNegLat = match[1] === '-' ? -lat : lat;
const isNegLng = match[5] === '-' ? -lng : lng;
const d = [isNegLat, isNegLng];
return d;
return [isNegLat, isNegLng];
}
},
// D MM , D MM ex: 35 11.1683 , 136 49.8966
@@ -213,12 +211,35 @@ export function dmsMatcher(q) {
const lng = +match[5] + (+match[6]) / 60;
const isNegLat = match[1] === '-' ? -lat : lat;
const isNegLng = match[4] === '-' ? -lng : lng;
const d = [isNegLat, isNegLng];
return d;
return [isNegLat, isNegLng];
}
},
// zoom/x/y ex: 2/1.23/34.44
{
condition: /^\s*(\d+\.?\d*)\s*\/\s*(-?\d+\.?\d*)\s*\/\s*(-?\d+\.?\d*)\s*$/,
parser: function(q) {
const match = this.condition.exec(q);
const lat = +match[2];
const lng = +match[3];
const zoom = +match[1];
return [lat, lng, zoom];
}
},
// x/y , x, y , x y where x and y are localized floats, e.g. in German locale: 49,4109399, 8,7147086
{
condition: { test: q => !!localizedNumberCoordsParser(q) },
parser: localizedNumberCoordsParser
}
];
function localizedNumberCoordsParser(q, ) {
const parseLocaleFloat = localizer.floatParser(_localeCode || localizer.localeCode());
let parts = q.split(/,?\s+|\s*[\/\\]\s*/);
if (parts.length !== 2) return false;
const lat = parseLocaleFloat(parts[0]);
const lng = parseLocaleFloat(parts[1]);
if (isNaN(lat) || isNaN(lng)) return false;
return [lat, lng];
}
for (const matcher of matchers) {
if (matcher.condition.test(q)){
return matcher.parser(q);
+22
View File
@@ -21,6 +21,28 @@ describe('iD.units', function() {
expect(result[0]).to.be.closeTo( -35.18614, 0.00001);
expect(result[1]).to.be.closeTo(-136.83161, 0.00001);
});
it('parses z/x/y coordinate', () => {
var result = iD.dmsMatcher('2/-1.23/34.44');
expect(result[0]).to.be.closeTo(-1.23, 0.00001);
expect(result[1]).to.be.closeTo(34.44, 0.00001);
expect(result[2]).to.eql(2);
});
it('parses x/y coordinate', () => {
var result = iD.dmsMatcher('-1.23/34.44');
expect(result[0]).to.be.closeTo(-1.23, 0.00001);
expect(result[1]).to.be.closeTo(34.44, 0.00001);
});
it('parses z/x/y coordinate', () => {
var result = iD.dmsMatcher('2/-1.23/34.44');
expect(result[0]).to.be.closeTo(-1.23, 0.00001);
expect(result[1]).to.be.closeTo(34.44, 0.00001);
expect(result[2]).to.eql(2);
});
it('parses coordinate with localized numbers', () => {
var result = iD.dmsMatcher('49,4109399, 8,7147086', 'de');
expect(result[0]).to.be.closeTo(49.4109399, 0.00001);
expect(result[1]).to.be.closeTo( 8.7147086, 0.00001);
});
it('handles invalid input', function() {
var result = iD.dmsMatcher('!@#$');