mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 05:30:35 +02:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])];
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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°0′1″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']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user