Expand DMS format and fix sec rounding logic (#10066)

Most DMS format parsed by @mapbox/sexagesimal, 
add a expandable matcher and two new formats for coordinates in DMS format in search bar

example: `35 11 10.1 , 136 49 53.8` (D M SS format), or `35 11.168 , 136 49.896` (D MM format)
This commit is contained in:
NaVis0mple
2024-02-02 22:26:47 +08:00
committed by GitHub
parent 8d1c1cfbce
commit 01d650d450
6 changed files with 146 additions and 28 deletions
+1 -1
View File
@@ -16,6 +16,6 @@ jobs:
with:
check_filenames: true
skip: ./.git,./data/territory_languages.json,./data/imagery.json,./data/languages.json,./data/address_formats.json,./dist/locales,./docs/img,./dist/img
ignore_words_list: "auxilary,casette,cemetary,chancel,childs,extentions,falsy,files',froms,generat,guerilla,inflight,kindergarden,nd,specialties,tos,vias,visibles"
ignore_words_list: "auxilary,casette,cemetary,chancel,childs,extentions,falsy,files',fillL,froms,generat,guerilla,inflight,kindergarden,nd,ot,pavillion,specialties,tos,vias,visibles"
exclude_file: .codespellignorelines
only_warn: 1
+2
View File
@@ -43,6 +43,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
* Hide tag suggestions for tags like `name_1` in raw tag editor autocomplete ([#9422])
* Show `(empty)` as a tag value option in the raw tag editor when a multi selections contains at least one feature which does not have the particular tag ([#9876], thanks [@k-yle])
* Allow to search for OSM notes by id in search bar ([#10062], thanks [@NaVis0mple])
* Add support for coordinates in `<degree> <minutes>[ <seconds>]` format to search bar ([#10066], thanks [@NaVis0mple])
#### :scissors: Operations
#### :camera: Street-Level
#### :white_check_mark: Validation
@@ -66,6 +67,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
[#9983]: https://github.com/openstreetmap/iD/issues/9983
[#9992]: https://github.com/openstreetmap/iD/issues/9992
[#10062]: https://github.com/openstreetmap/iD/pull/10062
[#10066]: https://github.com/openstreetmap/iD/pull/10066
[id-tagging-schema#1076]: https://github.com/openstreetmap/id-tagging-schema/pull/1076
[@ramith-kulal]: https://github.com/ramith-kulal
[@mangerlahn]: https://github.com/mangerlahn
+2 -2
View File
@@ -5,7 +5,7 @@ import * as sexagesimal from '@mapbox/sexagesimal';
import { presetManager } from '../presets';
import { t } from '../core/localizer';
import { dmsCoordinatePair } from '../util/units';
import { dmsCoordinatePair, dmsMatcher } from '../util/units';
import { coreGraph } from '../core/graph';
import { geoSphericalDistance } from '../geo/geo';
import { geoExtent } from '../geo';
@@ -125,7 +125,7 @@ export function uiFeatureList(context) {
if (!q) return result;
var locationMatch = sexagesimal.pair(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
var locationMatch = sexagesimal.pair(q.toUpperCase()) || dmsMatcher(q);
if (locationMatch) {
var loc = [Number(locationMatch[0]), Number(locationMatch[1])];
+3
View File
@@ -55,3 +55,6 @@ export { utilUnicodeCharsTruncated } from './util';
export { utilUniqueDomId } from './util';
export { utilWrap } from './util';
export { utilCleanOsmString } from './util';
export { dmsCoordinatePair } from './units';
export { dmsMatcher } from './units';
+83 -25
View File
@@ -106,33 +106,50 @@ function clamp(x, min, max) {
return Math.max(min, Math.min(x, max));
}
function displayCoordinate(deg, pos, neg) {
var locale = localizer.localeCode();
var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
var sec = (min - Math.floor(min)) * 60;
var displayDegrees = t('units.arcdegrees', {
quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
});
var displayCoordinate;
function roundToDecimal (target, decimalPlace) {
target = Number(target);
decimalPlace = Number(decimalPlace);
const factor = Math.pow(10, decimalPlace);
return Math.round(target * factor) / factor;
}
if (Math.floor(sec) > 0) {
displayCoordinate = displayDegrees +
t('units.arcminutes', {
quantity: Math.floor(min).toLocaleString(locale)
}) +
t('units.arcseconds', {
quantity: Math.round(sec).toLocaleString(locale)
});
} else if (Math.floor(min) > 0) {
displayCoordinate = displayDegrees +
t('units.arcminutes', {
quantity: Math.round(min).toLocaleString(locale)
});
} else {
displayCoordinate = t('units.arcdegrees', {
quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
});
function displayCoordinate(deg, pos, neg) {
var displayCoordinate;
var locale = localizer.localeCode();
var degreesFloor = Math.floor(Math.abs(deg));
var min = (Math.abs(deg) - degreesFloor) * 60;
var minFloor = Math.floor(min);
var sec = (min - minFloor) * 60;
// if you input 45°,90°0'0.5" , sec should be 0.5 instead 0.499999…
// in order to mitigate precision errors after calculating, round two time
// 0.499999… => 0.5
var fix = roundToDecimal(sec, 8);
// 0.5 => 1
var secRounded = roundToDecimal(fix, 0);
if (secRounded === 60) {
secRounded = 0;
minFloor += 1;
if (minFloor === 60) {
minFloor = 0;
degreesFloor += 1;
}
}
displayCoordinate =
t('units.arcdegrees', {
quantity: degreesFloor.toLocaleString(locale)
}) +
(minFloor !== 0 || secRounded !== 0 ?
t('units.arcminutes', {
quantity: minFloor.toLocaleString(locale)
}) : '') +
(secRounded !== 0 ?
t('units.arcseconds', {
quantity: secRounded.toLocaleString(locale)
}) : '' );
if (deg === 0) {
return displayCoordinate;
@@ -168,3 +185,44 @@ export function decimalCoordinatePair(coord) {
longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION)
});
}
// Return the parsed value that @mapbox/sexagesimal can't parse
// return value format : [D, D] ex:[ 35.1861, 136.83161 ]
export function dmsMatcher(q) {
const matchers = [
// D M SS , D M SS ex: 35 11 10.1 , 136 49 53.8
{
condition: /^\s*(-?)\s*(\d+)\s+(\d+)\s+(\d+\.?\d*)\s*\,\s*(-?)\s*(\d+)\s+(\d+)\s+(\d+\.?\d*)\s*$/,
parser: function(q) {
const match = this.condition.exec(q);
const lat = (+match[2]) + (+match[3]) / 60 + (+match[4]) / 3600;
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;
}
},
// D MM , D MM ex: 35 11.1683 , 136 49.8966
{
condition: /^\s*(-?)\s*(\d+)\s+(\d+\.?\d*)\s*\,\s*(-?)\s*(\d+)\s+(\d+\.?\d*)\s*$/,
parser: function(q) {
const match = this.condition.exec(q);
const lat = +match[2] + (+match[3]) / 60;
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;
}
}
];
for (const matcher of matchers) {
if (matcher.condition.test(q)){
return matcher.parser(q);
}
}
return null;
}
+55
View File
@@ -0,0 +1,55 @@
describe('iD.units', function() {
describe('dmsMatcher', function() {
it('parses D M SS format', function() {
var result = iD.dmsMatcher('35 11 10.1 , 136 49 53.8');
expect(result[0]).to.be.closeTo( 35.18614, 0.00001);
expect(result[1]).to.be.closeTo(136.83161, 0.00001);
});
it('parses D M SS format, with negative value', function() {
var result = iD.dmsMatcher('-35 11 10.1 , -136 49 53.8');
expect(result[0]).to.be.closeTo( -35.18614, 0.00001);
expect(result[1]).to.be.closeTo(-136.83161, 0.00001);
});
it('parses D MM format', function() {
var result = iD.dmsMatcher('35 11.1683 , 136 49.8966');
expect(result[0]).to.be.closeTo( 35.18614, 0.00001);
expect(result[1]).to.be.closeTo(136.83161, 0.00001);
});
it('parses D MM format, with negative value', function() {
var result = iD.dmsMatcher('-35 11.1683 , -136 49.8966');
expect(result[0]).to.be.closeTo( -35.18614, 0.00001);
expect(result[1]).to.be.closeTo(-136.83161, 0.00001);
});
it('handles invalid input', function() {
var result = iD.dmsMatcher('!@#$');
expect(result).to.be.null;
});
});
describe('dmsCoordinatePair', function() {
it('formats coordinate pair', function () {
var result = iD.dmsCoordinatePair([90 + 0.5/3600, 45]);
expect(result).to.be.eql('45°N, 90°01″E');
});
it('formats 0°', function () {
var result = iD.dmsCoordinatePair([0, 0]);
expect(result).to.be.eql('0°, 0°');
});
it('formats negative value', function () {
var result = iD.dmsCoordinatePair([-179, -90]);
expect(result).to.be.eql('90°S, 179°W');
});
it('formats 180° lng, should be E or W', function () {
// The longitude at this line can be given as either east or west.
var result = iD.dmsCoordinatePair([180, 0]);
expect(result).to.be.oneOf(['0°, 180°W', '0°, 180E°']);
});
it('formats value over 90°lat or 180°lng', function () {
var result = iD.dmsCoordinatePair([181, 91]);
expect(result).to.be.oneOf(['90°N, 179°W']);
});
});
});