Add inputs and code to display and convert to and from decimal degress and dhms for lat&long. Update marker on map when values change.
parent
4987ea1b21
commit
314236dea8
|
@ -0,0 +1,190 @@
|
||||||
|
/**
|
||||||
|
* dms module
|
||||||
|
* @module dms
|
||||||
|
*/
|
||||||
|
// Just return a value to define the module export.
|
||||||
|
// This example returns an object, but the module
|
||||||
|
// can return a function as the exported value.
|
||||||
|
// Matches DMS DmsCoordinates
|
||||||
|
// http://regexpal.com/?flags=gim®ex=^%28-%3F\d%2B%28%3F%3A\.\d%2B%29%3F%29[%C2%B0%3Ad]%3F\s%3F%28%3F%3A%28\d%2B%28%3F%3A\.\d%2B%29%3F%29[%27%E2%80%B2%3A]%3F\s%3F%28%3F%3A%28\d%2B%28%3F%3A\.\d%2B%29%3F%29[%22%E2%80%B3]%3F%29%3F%29%3F\s%3F%28[NSEW]%29%3F&input=40%3A26%3A46N%2C79%3A56%3A55W%0A40%3A26%3A46.302N%2079%3A56%3A55.903W%0A40%C2%B026%E2%80%B247%E2%80%B3N%2079%C2%B058%E2%80%B236%E2%80%B3W%0A40d%2026%E2%80%B2%2047%E2%80%B3%20N%2079d%2058%E2%80%B2%2036%E2%80%B3%20W%0A40.446195N%2079.948862W%0A40.446195%2C%20-79.948862%0A40%C2%B0%2026.7717%2C%20-79%C2%B0%2056.93172%0A
|
||||||
|
const dmsRe = /^(-?\d+(?:\.\d+)?)[°:d]?\s?(?:(\d+(?:\.\d+)?)['′ʹ:]?\s?(?:(\d+(?:\.\d+)?)["″ʺ]?)?)?\s?([NSEW])?/i;
|
||||||
|
/**
|
||||||
|
* Removes the decimal part of a number without rounding up.
|
||||||
|
* @param {number} n
|
||||||
|
* @returns {number}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function truncate(n) {
|
||||||
|
return n > 0 ? Math.floor(n) : Math.ceil(n);
|
||||||
|
}
|
||||||
|
export class Dms {
|
||||||
|
_dd;
|
||||||
|
_hemisphere;
|
||||||
|
/**
|
||||||
|
* Value in decimal degrees
|
||||||
|
* @member {number}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get dd() {
|
||||||
|
return this._dd;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Hemisphere
|
||||||
|
* @member {string}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
get hemisphere() {
|
||||||
|
return this._hemisphere;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @constructor module:dms.Dms
|
||||||
|
* @param {number} dd
|
||||||
|
* @param {string} longOrLat
|
||||||
|
*/
|
||||||
|
constructor(dd, longOrLat) {
|
||||||
|
this._dd = dd;
|
||||||
|
this._hemisphere = /^[WE]|(?:lon)/i.test(longOrLat) ? dd < 0 ? "W" : "E" : dd < 0 ? "S" : "N";
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the DMS parts as an array.
|
||||||
|
* The first three elements of the returned array are numbers:
|
||||||
|
* degrees, minutes, and seconds respectively. The fourth
|
||||||
|
* element is a string indicating the hemisphere: "N", "S", "E", or "W".
|
||||||
|
* @returns {Array.<(number|string)>}
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
getDmsArray() {
|
||||||
|
return this.dmsArray;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the DMS parts as an array.
|
||||||
|
* The first three elements of the returned array are numbers:
|
||||||
|
* degrees, minutes, and seconds respectively. The fourth
|
||||||
|
* element is a string indicating the hemisphere: "N", "S", "E", or "W".
|
||||||
|
* @returns {Array.<(number|string)>}
|
||||||
|
*/
|
||||||
|
get dmsArray() {
|
||||||
|
const absDD = Math.abs(this._dd);
|
||||||
|
const degrees = truncate(absDD);
|
||||||
|
const minutes = truncate((absDD - degrees) * 60);
|
||||||
|
const seconds = (absDD - degrees - minutes / 60) * Math.pow(60, 2);
|
||||||
|
return [degrees, minutes, seconds, this._hemisphere];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the DMS value as a string.
|
||||||
|
* @param {number} [precision] - number of digits after the decimal point in seconds
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
toString(precision) {
|
||||||
|
const dmsArray = this.getDmsArray();
|
||||||
|
const second = isNaN(Number(precision)) ? dmsArray[2] : dmsArray[2].toFixed(precision);
|
||||||
|
return `${dmsArray[0]}°${dmsArray[1]}′${second}″ ${dmsArray[3]}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @typedef {Object} DmsArrays
|
||||||
|
* @property {Array.<(number|string)>} longitude
|
||||||
|
* @property {Array.<(number|string)>} latitude
|
||||||
|
*/
|
||||||
|
export default class DmsCoordinates {
|
||||||
|
lat;
|
||||||
|
lon;
|
||||||
|
// Results of match will be [full coords string, Degrees, minutes (if any), seconds (if any), hemisphere (if any)]
|
||||||
|
// E.g., ["40:26:46.302N", "40", "26", "46.302", "N"]
|
||||||
|
// E.g., ["40.446195N", "40.446195", undefined, undefined, "N"]
|
||||||
|
/**
|
||||||
|
* A regular expression matching DMS coordinate.
|
||||||
|
* Example matches:
|
||||||
|
* E.g., ["40:26:46.302N", "40", "26", "46.302", "N"]
|
||||||
|
* E.g., ["40.446195N", "40.446195", undefined, undefined, "N"]
|
||||||
|
* @type {RegExp}
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
static dmsRe = dmsRe;
|
||||||
|
_longitude;
|
||||||
|
_latitude;
|
||||||
|
/**
|
||||||
|
* Longitude
|
||||||
|
* @type {module:dms.Dms} longitude - Longitude (X coordinate);
|
||||||
|
*/
|
||||||
|
get longitude() {
|
||||||
|
return this._longitude;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Latitude
|
||||||
|
* @type {module:dms.Dms} longitude - Latitude (y coordinate);
|
||||||
|
*/
|
||||||
|
get latitude() {
|
||||||
|
return this._latitude;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Represents a location on the earth in WGS 1984 coordinates.
|
||||||
|
* @constructor module:dms.DmsCoordinates
|
||||||
|
* @param {number} latitude - WGS 84 Y coordinates
|
||||||
|
* @param {number} longitude - WGS 84 X coordinates
|
||||||
|
* @throws {TypeError} - latitude and longitude must be numbers.
|
||||||
|
* @throws {RangeError} - latitude must be between -180 and 180, and longitude between -90 and 90. Neither can be NaN.
|
||||||
|
*/
|
||||||
|
constructor(lat, lon) {
|
||||||
|
this.lat = lat;
|
||||||
|
this.lon = lon;
|
||||||
|
if (typeof lat !== "number" || typeof lon !== "number") {
|
||||||
|
throw TypeError("The longitude and latitude parameters must be numbers.");
|
||||||
|
}
|
||||||
|
if (isNaN(lon) || lon < -180 || lon > 180) {
|
||||||
|
throw RangeError("longitude must be between -180 and 180");
|
||||||
|
}
|
||||||
|
if (isNaN(lat) || lat < -90 || lat > 90) {
|
||||||
|
throw RangeError("latitude must be between -90 and 90");
|
||||||
|
}
|
||||||
|
this._longitude = new Dms(lon, "long");
|
||||||
|
this._latitude = new Dms(lat, "lat");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns an object containing arrays containing degree / minute / second components.
|
||||||
|
* @returns {DmsArrays}
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
getDmsArrays() {
|
||||||
|
return this.dmsArrays;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns an object containing arrays containing degree / minute / second components.
|
||||||
|
* @type {DmsArrays}
|
||||||
|
*/
|
||||||
|
get dmsArrays() {
|
||||||
|
return {
|
||||||
|
longitude: this.longitude.dmsArray,
|
||||||
|
latitude: this.latitude.dmsArray,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the coordinates to a comma-separated string.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
toString() {
|
||||||
|
return [this.latitude, this.longitude].join(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Parses a Degrees Minutes Seconds string into a Decimal Degrees number.
|
||||||
|
* @param {string} dmsStr A string containing a coordinate in either DMS or DD format.
|
||||||
|
* @return {Number} If dmsStr is a valid coordinate string, the value in decimal degrees will be returned. Otherwise NaN will be returned.
|
||||||
|
*/
|
||||||
|
export function parseDms(dmsStr) {
|
||||||
|
let output = NaN;
|
||||||
|
const dmsMatch = dmsRe.exec(dmsStr);
|
||||||
|
if (dmsMatch) {
|
||||||
|
const degrees = Number(dmsMatch[1]);
|
||||||
|
const minutes = typeof (dmsMatch[2]) !== "undefined" ? Number(dmsMatch[2]) / 60 : 0;
|
||||||
|
const seconds = typeof (dmsMatch[3]) !== "undefined" ? Number(dmsMatch[3]) / 3600 : 0;
|
||||||
|
const hemisphere = dmsMatch[4] || null;
|
||||||
|
if (hemisphere !== null && /[SW]/i.test(hemisphere)) {
|
||||||
|
output = -Math.abs(degrees) - minutes - seconds;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
output = degrees + minutes + seconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
https://github.com/WSDOT-GIS/dms-js
|
|
@ -351,7 +351,6 @@ function initPage() {
|
||||||
|
|
||||||
if (parseInt(ZM_OPT_USE_GEOLOCATION)) {
|
if (parseInt(ZM_OPT_USE_GEOLOCATION)) {
|
||||||
if (window.L) {
|
if (window.L) {
|
||||||
if (form.elements['newMonitor[Type]'].value != 'WebSite') {
|
|
||||||
const latitude = form.elements['newMonitor[Latitude]'].value;
|
const latitude = form.elements['newMonitor[Latitude]'].value;
|
||||||
const longitude = form.elements['newMonitor[Longitude]'].value;
|
const longitude = form.elements['newMonitor[Longitude]'].value;
|
||||||
map = L.map('LocationMap', {
|
map = L.map('LocationMap', {
|
||||||
|
@ -376,21 +375,56 @@ function initPage() {
|
||||||
const position = marker.getLatLng();
|
const position = marker.getLatLng();
|
||||||
const form = document.getElementById('contentForm');
|
const form = document.getElementById('contentForm');
|
||||||
form.elements['newMonitor[Latitude]'].value = position.lat;
|
form.elements['newMonitor[Latitude]'].value = position.lat;
|
||||||
|
LL2DMS(form.elements['newMonitor[Latitude]']);
|
||||||
form.elements['newMonitor[Longitude]'].value = position.lng;
|
form.elements['newMonitor[Longitude]'].value = position.lng;
|
||||||
|
LL2DMS(form.elements['newMonitor[Longitude]']);
|
||||||
});
|
});
|
||||||
map.invalidateSize();
|
map.invalidateSize();
|
||||||
$j("a[href='#pills-location']").on('shown.bs.tab', function(e) {
|
$j("a[href='#pills-location']").on('shown.bs.tab', function(e) {
|
||||||
map.invalidateSize();
|
map.invalidateSize();
|
||||||
});
|
});
|
||||||
} // end if not website
|
|
||||||
} else {
|
} else {
|
||||||
console.log('Location turned on but leaflet not installed.');
|
console.log('Location turned on but leaflet not installed.');
|
||||||
}
|
}
|
||||||
|
LL2DMS(form.elements['newMonitor[Latitude]']);
|
||||||
|
LL2DMS(form.elements['newMonitor[Longitude]']);
|
||||||
} // end if ZM_OPT_USE_GEOLOCATION
|
} // end if ZM_OPT_USE_GEOLOCATION
|
||||||
|
|
||||||
updateLinkedMonitorsUI();
|
updateLinkedMonitorsUI();
|
||||||
} // end function initPage()
|
} // end function initPage()
|
||||||
|
|
||||||
|
function LL2DMS(input) {
|
||||||
|
const latitude = document.getElementById('newMonitor[Latitude]');
|
||||||
|
const longitude = document.getElementById('newMonitor[Longitude]');
|
||||||
|
const dmsCoords = new DmsCoordinates(parseFloat(latitude.value), parseFloat(longitude.value));
|
||||||
|
|
||||||
|
if (input.id == 'newMonitor[Latitude]') {
|
||||||
|
const dms = document.getElementById('LatitudeDMS');
|
||||||
|
dms.value = dmsCoords.latitude.toString(2);
|
||||||
|
} else if (input.id == 'newMonitor[Longitude]') {
|
||||||
|
const dms = document.getElementById('LongitudeDMS');
|
||||||
|
dms.value = dmsCoords.longitude.toString(2);
|
||||||
|
} else {
|
||||||
|
console.log("Unknown input in LL2DMS");
|
||||||
|
}
|
||||||
|
updateMarker();
|
||||||
|
}
|
||||||
|
|
||||||
|
function DMS2LL(input) {
|
||||||
|
const latitude = document.getElementById('newMonitor[Latitude]');
|
||||||
|
const longitude = document.getElementById('newMonitor[Longitude]');
|
||||||
|
const dms = parseDms(input.value);
|
||||||
|
|
||||||
|
if (input.id == 'LatitudeDMS') {
|
||||||
|
latitude.value = dms.toFixed(8);
|
||||||
|
} else if (input.id == 'LongitudeDMS') {
|
||||||
|
longitude.value = dms.toFixed(8);
|
||||||
|
} else {
|
||||||
|
console.log('Unknown input in DMS2LL');
|
||||||
|
}
|
||||||
|
updateMarker();
|
||||||
|
}
|
||||||
|
|
||||||
function change_Path(event) {
|
function change_Path(event) {
|
||||||
const pathInput = document.getElementsByName("newMonitor[Path]")[0];
|
const pathInput = document.getElementsByName("newMonitor[Path]")[0];
|
||||||
|
|
||||||
|
@ -488,16 +522,20 @@ function update_estimated_ram_use() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateMarker() {
|
||||||
|
const latitude = document.getElementById('newMonitor[Latitude]').value;
|
||||||
|
const longitude = document.getElementById('newMonitor[Longitude]').value;
|
||||||
|
console.log("Updating marker at ", latitude, longitude);
|
||||||
|
const latlng = new L.LatLng(latitude, longitude);
|
||||||
|
marker.setLatLng(latlng);
|
||||||
|
map.setView(latlng, 8, {animation: true});
|
||||||
|
setTimeout(function() { map.invalidateSize(true); }, 100);
|
||||||
|
}
|
||||||
function updateLatitudeAndLongitude(latitude, longitude) {
|
function updateLatitudeAndLongitude(latitude, longitude) {
|
||||||
var form = document.getElementById('contentForm');
|
var form = document.getElementById('contentForm');
|
||||||
form.elements['newMonitor[Latitude]'].value = latitude;
|
form.elements['newMonitor[Latitude]'].value = latitude;
|
||||||
form.elements['newMonitor[Longitude]'].value = longitude;
|
form.elements['newMonitor[Longitude]'].value = longitude;
|
||||||
const latlng = new L.LatLng(latitude, longitude);
|
updateMarker(latitude, longitude);
|
||||||
marker.setLatLng(latlng);
|
|
||||||
map.setView(latlng, 8, {animation: true});
|
|
||||||
setTimeout(function() {
|
|
||||||
map.invalidateSize(true);
|
|
||||||
}, 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLocation() {
|
function getLocation() {
|
||||||
|
|
|
@ -1535,13 +1535,13 @@ echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnL
|
||||||
?>
|
?>
|
||||||
<li class="Latitude">
|
<li class="Latitude">
|
||||||
<label class="Latitude"><?php echo translate('Latitude') ?></label>
|
<label class="Latitude"><?php echo translate('Latitude') ?></label>
|
||||||
<input type="number" id="newMonitor[Latitude]" name="newMonitor[Latitude]" step="any" value="<?php echo $monitor->Latitude() ?>" min="-90" max="90" data-on-change="LL2DMS" placeholder="degrees"/>
|
<input type="number" id="newMonitor[Latitude]" name="newMonitor[Latitude]" step="any" value="<?php echo $monitor->Latitude() ?>" min="-90" max="90" data-on-input-this="LL2DMS" placeholder="degrees"/>
|
||||||
<input type="text" id="LatitudeDMS" data-on-change-this="DMS2LL" placeholder="DMS" />
|
<input type="text" id="LatitudeDMS" data-on-input-this="DMS2LL" placeholder="Degrees Minutes Seconds" />
|
||||||
</li>
|
</li>
|
||||||
<li class="Longitude">
|
<li class="Longitude">
|
||||||
<label class="Longitude"><?php echo translate('Longitude') ?></label>
|
<label class="Longitude"><?php echo translate('Longitude') ?></label>
|
||||||
<input type="number" id="newMonitor[Longitude]" name="newMonitor[Longitude]" step="any" value="<?php echo $monitor->Longitude() ?>" min="-180" max="180" data-on-change="LL2DMS" placeholder="degrees"/>
|
<input type="number" id="newMonitor[Longitude]" name="newMonitor[Longitude]" step="any" value="<?php echo $monitor->Longitude() ?>" min="-180" max="180" data-on-input-this="LL2DMS" placeholder="degrees"/>
|
||||||
<input type="text" id="LongitudeDMS" data-on-change-this="DMS2LL" placeholder="DMS"/>
|
<input type="text" id="LongitudeDMS" data-on-input-this="DMS2LL" placeholder="Degrees Minutes Seconds"/>
|
||||||
</li>
|
</li>
|
||||||
<li class="DMS">
|
<li class="DMS">
|
||||||
</li>
|
</li>
|
||||||
|
@ -1585,6 +1585,12 @@ echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnL
|
||||||
</div><!--content-->
|
</div><!--content-->
|
||||||
</div><!--page-->
|
</div><!--page-->
|
||||||
<script src="<?php echo cache_bust('js/MonitorLinkExpression.js') ?>"></script>
|
<script src="<?php echo cache_bust('js/MonitorLinkExpression.js') ?>"></script>
|
||||||
|
<!--<script src="<?php echo cache_bust('js/dms.js') ?>" type="module"></script>-->
|
||||||
|
<script type="module" nonce="<?php echo $cspNonce ?>">
|
||||||
|
import DmsCoordinates, { parseDms } from "/js/dms.js";
|
||||||
|
window.DmsCoordinates = DmsCoordinates;
|
||||||
|
window.parseDms = parseDms;
|
||||||
|
</script>
|
||||||
<?php
|
<?php
|
||||||
echo output_script_if_exists(array('js/leaflet/leaflet.js'), false);
|
echo output_script_if_exists(array('js/leaflet/leaflet.js'), false);
|
||||||
echo output_link_if_exists(array('js/leaflet/leaflet.css'), false);
|
echo output_link_if_exists(array('js/leaflet/leaflet.css'), false);
|
||||||
|
|
Loading…
Reference in New Issue