fixes leaflet-elevation in build

This commit is contained in:
Christian Beutel
2024-03-16 19:26:26 +01:00
parent 3dd963c6f4
commit eae455112b
48 changed files with 941 additions and 136 deletions

4
web/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "wanderer",
"version": "0.0.1",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wanderer",
"version": "0.0.1",
"version": "0.1.0",
"dependencies": {
"@fortawesome/fontawesome-free": "^6.5.1",
"@sveltejs/adapter-node": "^4.0.1",

View File

@@ -1,7 +1,6 @@
<script lang="ts">
import type { Trail } from "$lib/models/trail";
import { currentUser } from "$lib/stores/user_store";
import { getFileURL } from "$lib/util/file_util";
import { createMarkerFromWaypoint } from "$lib/util/leaflet_util";
import type { Map, Marker } from "leaflet";
import { onMount } from "svelte";

View File

@@ -208,13 +208,13 @@
</section>
<hr class="border-separator" />
{/if}
<section class="trail-info-panel-tabs px-4 py-2 bg-white">
<section class="trail-info-panel-tabs px-4 py-2 bg-background">
<Tabs {tabs} bind:activeTab></Tabs>
</section>
</div>
<section class="trail-info-panel-content px-8">
<div
class="grid grid-cols-1 mt-4 gap-8 overflow-scroll"
class="grid grid-cols-1 mt-4 gap-8"
class:md:grid-cols-[1fr_18rem]={mode == "overview"}
>
{#if activeTab == 0}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -1 +0,0 @@
<svg viewBox="0 0 36 36" style="display: var(--ele-toggle-bg, inline-block);"><path fill="var(--ele-area, #addb55)" fill-opacity=".8" opacity=".9" d="m 28.3,23.9 -4.2,-8 -3.9,-0.2 -2.7,1.8 -2.4,-5.5 -5.2,6.2 0,5.7" /><path fill="none" stroke="var(--ele-line, var(--ele-stroke, #000))" stroke-width="1.5" d="m 9.9,17.9 1.1,0 4.3,-6.2 2.5,5.6 2.5,-2.3 3.7,0.2 4,8.3 0,0.4" /><path fill="none" stroke="#737373" stroke-linecap="square" d="m 8.6,8.1 0,19.8 m 21.3,-2.4 -23.8,0" /></svg>

Before

Width:  |  Height:  |  Size: 483 B

View File

@@ -1,13 +0,0 @@
html,
body,
.leaflet-map {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
}
body {
display: flex;
flex-direction: column;
}

View File

@@ -1,8 +0,0 @@
.dist-marker {
font-size: 9px;
border: 1px solid #777;
border-radius: 10px;
text-align: center;
color: #000;
background: #fff;
}

View File

@@ -1 +0,0 @@
.dist-marker{font-size:9px;border:1px solid #777;border-radius:10px;text-align:center;color:#000;background:#fff}

View File

@@ -1,42 +0,0 @@
.leaflet-ruler {
height: 35px;
width: 35px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAACx0lEQVRIS52VWahOURTHXRSJzBkiJcpYxgeS4oUi90GR4VK6DzcUD3gQIQ8UCXkwlCJRkjJEpqT7oGTMeHONmacyRRl/v9ve2n2d7/vOtevXPmefff5r7bXXXruiSf42gKkToTu0hY/wEk7CnWIyFWX0B/N9OkyF1tANdsEz6AIL4Sl8h8NwEK6nmsUMNGPSalgePFxLfwl2hrG39O1hM8yFobAKpsAGWAE/NJRloBfj++ErnIYesDj1qsjzRsYN2VhwdTPgUaGBkUF0e/D0TxDrRD8ehsFw6AcP4FrgCL17EpsrWAKDUgNtwuQP9FehJsyeSb8FbsOV8O0efVeYHOZV0h8N8w3dWXBOVWpgDwO/oBqc1BxciV7Pg3OJhz66ivPB+PoM8TnqRQPTeNkKfeALtAQ39QYsgE85xNsFz+voFe8AldFALS8DYU3waAd9b5gAv3OKn2HefagK4q64VgPm9k0we0wtN9pN6w+mY9qywqLnWeKXGa/WwHwYFSwrZtw/w9Ic4p5oxesLPG8Qd/UacMf13IE38ArGwN3EQJbnintOHsJsMOaG5Z+4/2vAY282eALd4FvQqoj4Aca3gQfPw1hSPBr4xoNH3bz1EO0D428r9LwpY3vB+nSolOfRQVeg1yOCgXH01pu+GeL+Y1hOwWOYBZlhieJxBU94eA2Lwo9WRw+X8fUEx0PUaPFowKW6Cg+UBc6M6AzrMsR1xtJR1vO4CkNk1TMLJoXBlcHQpvAePW+0eFyBRe45jAbL7fskhqm4MbdGmYruUU94l8zNfIyl4hhfLQteHqaiLYq7J4YlipvnXkgWRotgyRYNWB4st6bpizLiHsiO4H1gMrhnRVtarpcxy0vd+9eLPMvzhuMf1E7QmyC78xrQmDk+BC6Am98CLoKXi/dvWll1xjvjeAkD9YVXpkJ6ae7/BOPus4b+p9X9BdB/zv8zawwXAAAAAElFTkSuQmCC"); /* <div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div> */
background-repeat: no-repeat;
background-position: center;
}
.leaflet-ruler-clicked,
.leaflet-ruler:hover {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAE/ElEQVRIS6WVeWwUVRzHfzOzs7Pdi+7Rdq/ubrH0sNaKBFMCSEvBQLivFkhAE8GDYAyHATWgETUoMZ7BGCMBNKYiQbTKpYQKBqKlWmBbadmW7bbbFjv0mu2eM298M5SlpS3U+LL/zL6Zz/f7vr/3e4+AsQ6j80EgxTkKhGwkQRqQiLp5UtEOSDwOXf660TDEPfkGR75RTT8VF1EpJRJ6LsbrVuaZg2YtrWND8WD5FVajV9IcTyCOoYhv2QgcgE5fzWDmaAKU0eHa3RMStszNMqIdM+zUY3YdPFvRCG+VOCFFQ0N3mIfNJ3ywb3Em/BHgYNeZFnTc20OOU1Efd7eZtwBUxyWh4QLJGS6jFv04MU2bNWeCQRnoi8L7czPuG+RLJ31g0Smhsqk3dqGVa7wZZOZBb/31oQKm8ZMZRbxyU6FV9XaJmyQGZsNxBD/Ud8FXNWy/rzci9ER4Spqa5tYrit3jmNI8M2DnCRNvnm1Bu35tjcR4lH1HIDtbp+2NNuSmqCyTrBr4dMEDuH4A754LCLvOtQo44zouKp7hQagGIK+CIFgoipqPEHru6OocWJhtlAWk6GZ8cSXs64sd49qbSxMCaenu7+ZkGuftW5JJSy/FBARlh+vDF1tDfWEerYWullNDcjK5c9Q0Or/jcad++3SbbP8ueBn+S7glkOIsNTDUQf+mRxmtkoIIj6Bgb024hYtVhGPK9dDl7RsJvhPDtw3AcWzYuSd8vTcqOS8DrcUIKsUiWcDgyKghRLFgZ1E6vFhohfXfN8YP1bFVfW3N0/E0Ggu8CMNxfY9z7b5SCU4o6dMkhc4RYM61aulwU8fWSSqaIqEqEIRZBzzBSCg+HoIdncPglHgBG9ENdj4inEAXhZuBdQSYXBuWZOnfOLI61yTB1ld4Y1/+xe6Ndvo33Q+ON5Rc0GHOB+DS6gmF2XF6dkZy0eeLM8lU3EDGd6qiQRAnQnvz3wkBXFANdr5jkHMJXrS/NtTIRk5yN3wrErEMgsuNpkp13ViRZ0zdOtUOWiUJ2R/WxPibfmYk+Mp8E/XCT01y4y0/1HBfuCygMDljVzYW0DnmJKhuC8Lsg7WB7kCzQxa4y7nUF08euQblHlZMoqij93J+2yBBpzgjlzcUMJLAmeu9sOjrq+1cR7PtNvzVYrv+5WkOUvpAiqUYx+JlI6cwfPlosQyuHcGYnYECq9r2AV62O5mB9PcuItxjj2ho6uz/hcsRESbH4TX5qQv3LsigNbjJ7Huq+tkIT75enM4Mdx76mbvhXzYW54mIwOBeNd2l+eTs03nyYYIPqrhaSVGbC213xfLf4fIKAB9yqp4IW/VMvtKqVYJJrUhEeCdzGS5lbki22Wu5btYg0IwV2hrYIb0ywoN8VCTbXadDUTRz/9IJsCrfLL82CP7LQCyGcVab56HColQkCKL3ctWxTs/5+WMSAHwPGBih0rNxotqGL42R4DqLpebhKTNta7fvgWBfN7yybIoQjYdzoLPVey+RxHFtdrhfy05J2laxKjep5EAd3oqhhHNtqrm6YOoT6RKcIOXSwEdb1ojXLv35fDRQ99mYBKR6GOyu3zBhcowXTvR3+JdCWhqjVhk9FEW7Z5Wtw1vuFlwatb9XQmuD51J/MFQ+qgAheIdemSl5FoDgOuhs3o0/4vWOKcZkh/4bEJFKxAMA/6QhgsjzAvrH3+SXn0YZAinU/wuTosgK53+p+wAAAABJRU5ErkJggg=="); /* <div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div> */
}
.leaflet-ruler-clicked {
height: 35px;
width: 35px;
background-repeat: no-repeat;
background-position: center;
border-color: chartreuse !important;
}
.leaflet-bar.leaflet-ruler {
background-color: #ffffff;
}
.leaflet-control.leaflet-ruler {
cursor: pointer;
}
.result-tooltip {
background-color: white;
border-width: medium;
border-color: #de0000;
font-size: smaller;
}
.moving-tooltip {
background-color: rgba(255, 255, 255, .7);
background-clip: padding-box;
opacity: 0.5;
border: dotted;
border-color: red;
font-size: smaller;
}
.plus-length {
padding-left: 45px;
}

View File

@@ -1 +0,0 @@
.leaflet-ruler{height:35px;width:35px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAACx0lEQVRIS52VWahOURTHXRSJzBkiJcpYxgeS4oUi90GR4VK6DzcUD3gQIQ8UCXkwlCJRkjJEpqT7oGTMeHONmacyRRl/v9ve2n2d7/vOtevXPmefff5r7bXXXruiSf42gKkToTu0hY/wEk7CnWIyFWX0B/N9OkyF1tANdsEz6AIL4Sl8h8NwEK6nmsUMNGPSalgePFxLfwl2hrG39O1hM8yFobAKpsAGWAE/NJRloBfj++ErnIYesDj1qsjzRsYN2VhwdTPgUaGBkUF0e/D0TxDrRD8ehsFw6AcP4FrgCL17EpsrWAKDUgNtwuQP9FehJsyeSb8FbsOV8O0efVeYHOZV0h8N8w3dWXBOVWpgDwO/oBqc1BxciV7Pg3OJhz66ivPB+PoM8TnqRQPTeNkKfeALtAQ39QYsgE85xNsFz+voFe8AldFALS8DYU3waAd9b5gAv3OKn2HefagK4q64VgPm9k0we0wtN9pN6w+mY9qywqLnWeKXGa/WwHwYFSwrZtw/w9Ic4p5oxesLPG8Qd/UacMf13IE38ArGwN3EQJbnintOHsJsMOaG5Z+4/2vAY282eALd4FvQqoj4Aca3gQfPw1hSPBr4xoNH3bz1EO0D428r9LwpY3vB+nSolOfRQVeg1yOCgXH01pu+GeL+Y1hOwWOYBZlhieJxBU94eA2Lwo9WRw+X8fUEx0PUaPFowKW6Cg+UBc6M6AzrMsR1xtJR1vO4CkNk1TMLJoXBlcHQpvAePW+0eFyBRe45jAbL7fskhqm4MbdGmYruUU94l8zNfIyl4hhfLQteHqaiLYq7J4YlipvnXkgWRotgyRYNWB4st6bpizLiHsiO4H1gMrhnRVtarpcxy0vd+9eLPMvzhuMf1E7QmyC78xrQmDk+BC6Am98CLoKXi/dvWll1xjvjeAkD9YVXpkJ6ae7/BOPus4b+p9X9BdB/zv8zawwXAAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center}.leaflet-ruler-clicked,.leaflet-ruler:hover{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAE/ElEQVRIS6WVeWwUVRzHfzOzs7Pdi+7Rdq/ubrH0sNaKBFMCSEvBQLivFkhAE8GDYAyHATWgETUoMZ7BGCMBNKYiQbTKpYQKBqKlWmBbadmW7bbbFjv0mu2eM298M5SlpS3U+LL/zL6Zz/f7vr/3e4+AsQ6j80EgxTkKhGwkQRqQiLp5UtEOSDwOXf660TDEPfkGR75RTT8VF1EpJRJ6LsbrVuaZg2YtrWND8WD5FVajV9IcTyCOoYhv2QgcgE5fzWDmaAKU0eHa3RMStszNMqIdM+zUY3YdPFvRCG+VOCFFQ0N3mIfNJ3ywb3Em/BHgYNeZFnTc20OOU1Efd7eZtwBUxyWh4QLJGS6jFv04MU2bNWeCQRnoi8L7czPuG+RLJ31g0Smhsqk3dqGVa7wZZOZBb/31oQKm8ZMZRbxyU6FV9XaJmyQGZsNxBD/Ud8FXNWy/rzci9ER4Spqa5tYrit3jmNI8M2DnCRNvnm1Bu35tjcR4lH1HIDtbp+2NNuSmqCyTrBr4dMEDuH4A754LCLvOtQo44zouKp7hQagGIK+CIFgoipqPEHru6OocWJhtlAWk6GZ8cSXs64sd49qbSxMCaenu7+ZkGuftW5JJSy/FBARlh+vDF1tDfWEerYWullNDcjK5c9Q0Or/jcad++3SbbP8ueBn+S7glkOIsNTDUQf+mRxmtkoIIj6Bgb024hYtVhGPK9dDl7RsJvhPDtw3AcWzYuSd8vTcqOS8DrcUIKsUiWcDgyKghRLFgZ1E6vFhohfXfN8YP1bFVfW3N0/E0Ggu8CMNxfY9z7b5SCU4o6dMkhc4RYM61aulwU8fWSSqaIqEqEIRZBzzBSCg+HoIdncPglHgBG9ENdj4inEAXhZuBdQSYXBuWZOnfOLI61yTB1ld4Y1/+xe6Ndvo33Q+ON5Rc0GHOB+DS6gmF2XF6dkZy0eeLM8lU3EDGd6qiQRAnQnvz3wkBXFANdr5jkHMJXrS/NtTIRk5yN3wrErEMgsuNpkp13ViRZ0zdOtUOWiUJ2R/WxPibfmYk+Mp8E/XCT01y4y0/1HBfuCygMDljVzYW0DnmJKhuC8Lsg7WB7kCzQxa4y7nUF08euQblHlZMoqij93J+2yBBpzgjlzcUMJLAmeu9sOjrq+1cR7PtNvzVYrv+5WkOUvpAiqUYx+JlI6cwfPlosQyuHcGYnYECq9r2AV62O5mB9PcuItxjj2ho6uz/hcsRESbH4TX5qQv3LsigNbjJ7Huq+tkIT75enM4Mdx76mbvhXzYW54mIwOBeNd2l+eTs03nyYYIPqrhaSVGbC213xfLf4fIKAB9yqp4IW/VMvtKqVYJJrUhEeCdzGS5lbki22Wu5btYg0IwV2hrYIb0ywoN8VCTbXadDUTRz/9IJsCrfLL82CP7LQCyGcVab56HColQkCKL3ctWxTs/5+WMSAHwPGBih0rNxotqGL42R4DqLpebhKTNta7fvgWBfN7yybIoQjYdzoLPVey+RxHFtdrhfy05J2laxKjep5EAd3oqhhHNtqrm6YOoT6RKcIOXSwEdb1ojXLv35fDRQ99mYBKR6GOyu3zBhcowXTvR3+JdCWhqjVhk9FEW7Z5Wtw1vuFlwatb9XQmuD51J/MFQ+qgAheIdemSl5FoDgOuhs3o0/4vWOKcZkh/4bEJFKxAMA/6QhgsjzAvrH3+SXn0YZAinU/wuTosgK53+p+wAAAABJRU5ErkJggg==)}.leaflet-ruler-clicked{height:35px;width:35px;background-repeat:no-repeat;background-position:center;border-color:#7fff00!important}.leaflet-bar.leaflet-ruler{background-color:#fff}.leaflet-control.leaflet-ruler{cursor:pointer}.result-tooltip{background-color:#fff;border-width:medium;border-color:#de0000;font-size:smaller}.moving-tooltip{background-color:rgba(255,255,255,.7);background-clip:padding-box;opacity:.5;border:dotted;border-color:red;font-size:smaller}.plus-length{padding-left:45px}

View File

@@ -13,18 +13,18 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
options: Options,
__mileFactor: 0.621371, // 1 km = (0.621371 mi)
__footFactor: 3.28084, // 1 m = (3.28084 ft)
__D3: 'https://unpkg.com/d3@7.8.4/dist/d3.min.js',
__TOGEOJSON: 'https://unpkg.com/@tmcw/togeojson@5.6.2/dist/togeojson.umd.js',
__LGEOMUTIL: 'https://unpkg.com/leaflet-geometryutil@0.10.1/src/leaflet.geometryutil.js',
__LALMOSTOVER: 'https://unpkg.com/leaflet-almostover@1.0.1/src/leaflet.almostover.js',
__LHOTLINE: '../libs/leaflet-hotline.min.js',
__LDISTANCEM: '../libs/leaflet-distance-marker.min.js',
__LEDGESCALE: '../libs/leaflet-edgescale.min.js',
__LCHART: '../src/components/chart.js',
__LMARKER: '../src/components/marker.js',
__LSUMMARY: '../src/components/summary.js',
__modulesFolder: '../src/handlers/',
__btnIcon: '../images/elevation.svg',
__D3: '/vendor/leaflet-elevation/libs/d3.min.js',
__TOGEOJSON: '/vendor/leaflet-elevation/libs/togeojson.umd.js',
__LGEOMUTIL: '/vendor/leaflet-elevation/libs/leaflet.geometryutil.js',
__LALMOSTOVER: '/vendor/leaflet-elevation/libs/leaflet.almostover.js',
__LHOTLINE: '/vendor/leaflet-elevation/libs/leaflet-hotline.min.js',
__LDISTANCEM: '/vendor/leaflet-elevation/libs/leaflet-distance-marker.min.js',
__LEDGESCALE: '/vendor/leaflet-elevation/libs/leaflet-edgescale.min.js',
__LCHART: '/vendor/leaflet-elevation/src/components/chart.js',
__LMARKER: '/vendor/leaflet-elevation/src/components/marker.js',
__LSUMMARY: '/vendor/leaflet-elevation/src/components/summary.js',
__modulesFolder: '/vendor/leaflet-elevation/src/handlers/',
__btnIcon: '/vendor/leaflet-elevation/images/elevation.svg',
/*
* Add data to the diagram either from GPX or GeoJSON and update the axis domain and data
@@ -226,7 +226,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
case this.__LEDGESCALE: condition = typeof L.Control.EdgeScale !== 'function'; break;
case this.__LHOTLINE: condition = typeof L.Hotline !== 'function'; break;
}
return condition !== false ? import(_.resolveURL(src, this.options.srcFolder)) : Promise.resolve();
return condition !== false ? import(src) : Promise.resolve();
},
/**
@@ -441,7 +441,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
_initHotLine(layer) {
let prop = typeof this.options.hotline == 'string' ? this.options.hotline : 'elevation';
return this.options.hotline ? this.import(this.__LHOTLINE)
return this.options.hotline ? this.import(/* @vite-ignore */this.__LHOTLINE)
.then(() => {
layer.eachLayer((trkseg) => {
if (trkseg.feature.geometry.type != "Point") {
@@ -722,7 +722,7 @@ export const Elevation = L.Control.Elevation = L.Control.extend({
},
_initSummary(container) {
this.import(this.__LSUMMARY).then((m) => {
this.import(/* @vite-ignore */this.__LSUMMARY).then((m) => {
this._summary = new (m || Elevation).Summary({ summary: this.options.summary }, this);
this.on('elechart_init', () => {

View File

@@ -17,6 +17,7 @@ const HOUR = MIN * 60;
const DAY = HOUR * 24;
export function resolveURL(src, baseUrl) {
console.log(baseUrl, src);
return (new URL(src, (src.startsWith('../') || src.startsWith('./')) ? baseUrl : undefined)).toString()
};

View File

@@ -1,50 +0,0 @@
/**
* src/utils.js
*/
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import '../test/setup/jsdom.js'
import { iAvg, iMin, iMax, iSum } from "../src/utils.js";
const toFixed = (n) => +n.toFixed(2);
const test = suite('src/utils.js');
test('iAvg()', () => {
let avg;
avg = iAvg(100, undefined, 1); assert.is(toFixed(avg), 100); // average for [100] is 100
avg = iAvg(100, avg, 2); assert.is(toFixed(avg), 100); // average for [100, 100] is 100
avg = iAvg(200, avg, 3); assert.is(toFixed(avg), 133.33); // average for [100, 100, 200] is 133.33
avg = iAvg(200, avg, 4); assert.is(toFixed(avg), 150); // average for [100, 100, 200, 200] is 150
avg = iAvg(NaN, avg, 5); assert.ok(isNaN(avg)); // average for [100, 100, 200, 200, NaN] is NaN
});
test('iMin()', () => {
let min;
min = iMin(100, undefined); assert.is(toFixed(min), 100); // min for [100] is 100
min = iMin(NaN, min); assert.is(toFixed(min), 100); // min for [100, NaN] is 100
min = iMin(0, min); assert.is(toFixed(min), 0); // min for [100, NaN, 0] is 100
min = iMin(-200, min); assert.is(toFixed(min), -200); // min for [100, NaN, 0, -200] is -200
min = iMin(200, min); assert.is(toFixed(min), -200); // min for [100, NaN, -100, -200, 200] is -200
});
test('iMax()', () => {
let max;
max = iMax(100, undefined); assert.is(toFixed(max), 100); // max for [100] is 100
max = iMax(NaN, max); assert.is(toFixed(max), 100); // max for [100, NaN] is 100
max = iMax(0, max); assert.is(toFixed(max), 100); // max for [100, NaN, 0] is 100
max = iMax(-200, max); assert.is(toFixed(max), 100); // max for [100, NaN, 0, -200] is 100
max = iMax(200, max); assert.is(toFixed(max), 200); // max for [100, NaN, -100, -200, 200] is 200
});
test('iSum()', () => {
let sum;
sum = iSum(10.25, undefined); assert.is(toFixed(sum), 10.25); // sum for [10.25] is 10.25
sum = iSum(0, sum); assert.is(toFixed(sum), 10.25); // sum for [10.25, 0] is 10.25
sum = iSum(-0.25, sum); assert.is(toFixed(sum), 10); // sum for [10.25, 0, -0.25] is 10
sum = iSum(-10, sum); assert.is(toFixed(sum), 0); // sum for [10.25, 0, -0.25, -10] is 0
sum = iSum(NaN, sum); assert.ok(isNaN(sum)); // sum for [10.25, 0, -0.25, -10, NaN] is NaN
});
test.run();

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,150 @@
L.Map.mergeOptions({
// @option almostOver: Boolean = true
// Set it to false to disable this plugin
almostOver: true,
// @option almostDistance: Number = 25
// Tolerance in pixels
almostDistance: 25, // pixels
// @option almostSamplingPeriod: Number = 50
// To reduce the 'mousemove' event frequency. In milliseconds
almostSamplingPeriod: 50, // ms
// @option almostOnMouseMove Boolean = true
// Set it to false to disable track 'mousemove' events and improve performance
// if AlmostOver is only need for 'click' events.
almostOnMouseMove: true,
});
L.Handler.AlmostOver = L.Handler.extend({
includes: L.Evented || L.Mixin.Events,
initialize: function (map) {
this._map = map;
this._layers = [];
this._previous = null;
this._marker = null;
this._buffer = 0;
// Reduce 'mousemove' event frequency
this.__mouseMoveSampling = (function () {
var timer = new Date();
return function (e) {
var date = new Date(),
filtered = (date - timer) < this._map.options.almostSamplingPeriod;
if (filtered || this._layers.length === 0) {
return; // Ignore movement
}
timer = date;
this._map.fire('mousemovesample', {latlng: e.latlng});
};
})();
},
addHooks: function () {
if (this._map.options.almostOnMouseMove) {
this._map.on('mousemove', this.__mouseMoveSampling, this);
this._map.on('mousemovesample', this._onMouseMove, this);
}
this._map.on('click dblclick', this._onMouseClick, this);
var map = this._map;
function computeBuffer() {
this._buffer = this._map.layerPointToLatLng([0, 0]).lat -
this._map.layerPointToLatLng([this._map.options.almostDistance,
this._map.options.almostDistance]).lat;
}
this._map.on('viewreset zoomend', computeBuffer, this);
this._map.whenReady(computeBuffer, this);
},
removeHooks: function () {
this._map.off('mousemovesample');
this._map.off('mousemove', this.__mouseMoveSampling, this);
this._map.off('click dblclick', this._onMouseClick, this);
},
addLayer: function (layer) {
if (typeof layer.eachLayer == 'function') {
layer.eachLayer(function (l) {
this.addLayer(l);
}, this);
}
else {
if (typeof this.indexLayer == 'function') {
this.indexLayer(layer);
}
this._layers.push(layer);
}
},
removeLayer: function (layer) {
if (typeof layer.eachLayer == 'function') {
layer.eachLayer(function (l) {
this.removeLayer(l);
}, this);
}
else {
if (typeof this.unindexLayer == 'function') {
this.unindexLayer(layer);
}
var index = this._layers.indexOf(layer);
if (0 <= index) {
this._layers.splice(index, 1);
}
}
this._previous = null;
},
getClosest: function (latlng) {
var snapfunc = L.GeometryUtil.closestLayerSnap,
distance = this._map.options.almostDistance;
var snaplist = [];
if (typeof this.searchBuffer == 'function') {
snaplist = this.searchBuffer(latlng, this._buffer);
}
else {
snaplist = this._layers;
}
return snapfunc(this._map, snaplist, latlng, distance, false);
},
_onMouseMove: function (e) {
var closest = this.getClosest(e.latlng);
if (closest) {
if (!this._previous) {
this._map.fire('almost:over', {layer: closest.layer,
latlng: closest.latlng});
}
else if (L.stamp(this._previous.layer) != L.stamp(closest.layer)) {
this._map.fire('almost:out', {layer: this._previous.layer});
this._map.fire('almost:over', {layer: closest.layer,
latlng: closest.latlng});
}
this._map.fire('almost:move', {layer: closest.layer,
latlng: closest.latlng});
}
else {
if (this._previous) {
this._map.fire('almost:out', {layer: this._previous.layer});
}
}
this._previous = closest;
},
_onMouseClick: function (e) {
var closest = this.getClosest(e.latlng);
if (closest) {
this._map.fire('almost:' + e.type, {layer: closest.layer,
latlng: closest.latlng});
}
},
});
if (L.LayerIndexMixin !== undefined) {
L.Handler.AlmostOver.include(L.LayerIndexMixin);
}
L.Map.addInitHook('addHandler', 'almostOver', L.Handler.AlmostOver);

View File

@@ -0,0 +1,767 @@
// Packaging/modules magic dance.
(function (factory) {
var L;
if (typeof define === 'function' && define.amd) {
// AMD
define(['leaflet'], factory);
} else if (typeof module !== 'undefined') {
// Node/CommonJS
L = require('leaflet');
module.exports = factory(L);
} else {
// Browser globals
if (typeof window.L === 'undefined')
throw 'Leaflet must be loaded first';
factory(window.L);
}
}(function (L) {
"use strict";
L.Polyline._flat = L.LineUtil.isFlat || L.Polyline._flat || function (latlngs) {
// true if it's a flat array of latlngs; false if nested
return !L.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
};
/**
* @fileOverview Leaflet Geometry utilities for distances and linear referencing.
* @name L.GeometryUtil
*/
L.GeometryUtil = L.extend(L.GeometryUtil || {}, {
/**
Shortcut function for planar distance between two {L.LatLng} at current zoom.
@tutorial distance-length
@param {L.Map} map Leaflet map to be used for this method
@param {L.LatLng} latlngA geographical point A
@param {L.LatLng} latlngB geographical point B
@returns {Number} planar distance
*/
distance: function (map, latlngA, latlngB) {
return map.latLngToLayerPoint(latlngA).distanceTo(map.latLngToLayerPoint(latlngB));
},
/**
Shortcut function for planar distance between a {L.LatLng} and a segment (A-B).
@param {L.Map} map Leaflet map to be used for this method
@param {L.LatLng} latlng - The position to search
@param {L.LatLng} latlngA geographical point A of the segment
@param {L.LatLng} latlngB geographical point B of the segment
@returns {Number} planar distance
*/
distanceSegment: function (map, latlng, latlngA, latlngB) {
var p = map.latLngToLayerPoint(latlng),
p1 = map.latLngToLayerPoint(latlngA),
p2 = map.latLngToLayerPoint(latlngB);
return L.LineUtil.pointToSegmentDistance(p, p1, p2);
},
/**
Shortcut function for converting distance to readable distance.
@param {Number} distance distance to be converted
@param {String} unit 'metric' or 'imperial'
@returns {String} in yard or miles
*/
readableDistance: function (distance, unit) {
var isMetric = (unit !== 'imperial'),
distanceStr;
if (isMetric) {
// show metres when distance is < 1km, then show km
if (distance > 1000) {
distanceStr = (distance / 1000).toFixed(2) + ' km';
}
else {
distanceStr = distance.toFixed(1) + ' m';
}
}
else {
distance *= 1.09361;
if (distance > 1760) {
distanceStr = (distance / 1760).toFixed(2) + ' miles';
}
else {
distanceStr = distance.toFixed(1) + ' yd';
}
}
return distanceStr;
},
/**
Returns true if the latlng belongs to segment A-B
@param {L.LatLng} latlng - The position to search
@param {L.LatLng} latlngA geographical point A of the segment
@param {L.LatLng} latlngB geographical point B of the segment
@param {?Number} [tolerance=0.2] tolerance to accept if latlng belongs really
@returns {boolean}
*/
belongsSegment: function(latlng, latlngA, latlngB, tolerance) {
tolerance = tolerance === undefined ? 0.2 : tolerance;
var hypotenuse = latlngA.distanceTo(latlngB),
delta = latlngA.distanceTo(latlng) + latlng.distanceTo(latlngB) - hypotenuse;
return delta/hypotenuse < tolerance;
},
/**
* Returns total length of line
* @tutorial distance-length
*
* @param {L.Polyline|Array<L.Point>|Array<L.LatLng>} coords Set of coordinates
* @returns {Number} Total length (pixels for Point, meters for LatLng)
*/
length: function (coords) {
var accumulated = L.GeometryUtil.accumulatedLengths(coords);
return accumulated.length > 0 ? accumulated[accumulated.length-1] : 0;
},
/**
* Returns a list of accumulated length along a line.
* @param {L.Polyline|Array<L.Point>|Array<L.LatLng>} coords Set of coordinates
* @returns {Array<Number>} Array of accumulated lengths (pixels for Point, meters for LatLng)
*/
accumulatedLengths: function (coords) {
if (typeof coords.getLatLngs == 'function') {
coords = coords.getLatLngs();
}
if (coords.length === 0)
return [];
var total = 0,
lengths = [0];
for (var i = 0, n = coords.length - 1; i< n; i++) {
total += coords[i].distanceTo(coords[i+1]);
lengths.push(total);
}
return lengths;
},
/**
Returns the closest point of a {L.LatLng} on the segment (A-B)
@tutorial closest
@param {L.Map} map Leaflet map to be used for this method
@param {L.LatLng} latlng - The position to search
@param {L.LatLng} latlngA geographical point A of the segment
@param {L.LatLng} latlngB geographical point B of the segment
@returns {L.LatLng} Closest geographical point
*/
closestOnSegment: function (map, latlng, latlngA, latlngB) {
var maxzoom = map.getMaxZoom();
if (maxzoom === Infinity)
maxzoom = map.getZoom();
var p = map.project(latlng, maxzoom),
p1 = map.project(latlngA, maxzoom),
p2 = map.project(latlngB, maxzoom),
closest = L.LineUtil.closestPointOnSegment(p, p1, p2);
return map.unproject(closest, maxzoom);
},
/**
Returns the closest latlng on layer.
Accept nested arrays
@tutorial closest
@param {L.Map} map Leaflet map to be used for this method
@param {Array<L.LatLng>|Array<Array<L.LatLng>>|L.PolyLine|L.Polygon} layer - Layer that contains the result
@param {L.LatLng} latlng - The position to search
@param {?boolean} [vertices=false] - Whether to restrict to path vertices.
@returns {L.LatLng} Closest geographical point or null if layer param is incorrect
*/
closest: function (map, layer, latlng, vertices) {
var latlngs,
mindist = Infinity,
result = null,
i, n, distance, subResult;
if (layer instanceof Array) {
// if layer is Array<Array<T>>
if (layer[0] instanceof Array && typeof layer[0][0] !== 'number') {
// if we have nested arrays, we calc the closest for each array
// recursive
for (i = 0; i < layer.length; i++) {
subResult = L.GeometryUtil.closest(map, layer[i], latlng, vertices);
if (subResult && subResult.distance < mindist) {
mindist = subResult.distance;
result = subResult;
}
}
return result;
} else if (layer[0] instanceof L.LatLng
|| typeof layer[0][0] === 'number'
|| typeof layer[0].lat === 'number') { // we could have a latlng as [x,y] with x & y numbers or {lat, lng}
layer = L.polyline(layer);
} else {
return result;
}
}
// if we don't have here a Polyline, that means layer is incorrect
// see https://github.com/makinacorpus/Leaflet.GeometryUtil/issues/23
if (! ( layer instanceof L.Polyline ) )
return result;
// deep copy of latlngs
latlngs = JSON.parse(JSON.stringify(layer.getLatLngs().slice(0)));
// add the last segment for L.Polygon
if (layer instanceof L.Polygon) {
// add the last segment for each child that is a nested array
var addLastSegment = function(latlngs) {
if (L.Polyline._flat(latlngs)) {
latlngs.push(latlngs[0]);
} else {
for (var i = 0; i < latlngs.length; i++) {
addLastSegment(latlngs[i]);
}
}
};
addLastSegment(latlngs);
}
// we have a multi polygon / multi polyline / polygon with holes
// use recursive to explore and return the good result
if ( ! L.Polyline._flat(latlngs) ) {
for (i = 0; i < latlngs.length; i++) {
// if we are at the lower level, and if we have a L.Polygon, we add the last segment
subResult = L.GeometryUtil.closest(map, latlngs[i], latlng, vertices);
if (subResult.distance < mindist) {
mindist = subResult.distance;
result = subResult;
}
}
return result;
} else {
// Lookup vertices
if (vertices) {
for(i = 0, n = latlngs.length; i < n; i++) {
var ll = latlngs[i];
distance = L.GeometryUtil.distance(map, latlng, ll);
if (distance < mindist) {
mindist = distance;
result = ll;
result.distance = distance;
}
}
return result;
}
// Keep the closest point of all segments
for (i = 0, n = latlngs.length; i < n-1; i++) {
var latlngA = latlngs[i],
latlngB = latlngs[i+1];
distance = L.GeometryUtil.distanceSegment(map, latlng, latlngA, latlngB);
if (distance <= mindist) {
mindist = distance;
result = L.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB);
result.distance = distance;
}
}
return result;
}
},
/**
Returns the closest layer to latlng among a list of layers.
@tutorial closest
@param {L.Map} map Leaflet map to be used for this method
@param {Array<L.ILayer>} layers Set of layers
@param {L.LatLng} latlng - The position to search
@returns {object} ``{layer, latlng, distance}`` or ``null`` if list is empty;
*/
closestLayer: function (map, layers, latlng) {
var mindist = Infinity,
result = null,
ll = null,
distance = Infinity;
for (var i = 0, n = layers.length; i < n; i++) {
var layer = layers[i];
if (layer instanceof L.LayerGroup) {
// recursive
var subResult = L.GeometryUtil.closestLayer(map, layer.getLayers(), latlng);
if (subResult.distance < mindist) {
mindist = subResult.distance;
result = subResult;
}
} else {
// Single dimension, snap on points, else snap on closest
if (typeof layer.getLatLng == 'function') {
ll = layer.getLatLng();
distance = L.GeometryUtil.distance(map, latlng, ll);
}
else {
ll = L.GeometryUtil.closest(map, layer, latlng);
if (ll) distance = ll.distance; // Can return null if layer has no points.
}
if (distance < mindist) {
mindist = distance;
result = {layer: layer, latlng: ll, distance: distance};
}
}
}
return result;
},
/**
Returns the n closest layers to latlng among a list of input layers.
@param {L.Map} map - Leaflet map to be used for this method
@param {Array<L.ILayer>} layers - Set of layers
@param {L.LatLng} latlng - The position to search
@param {?Number} [n=layers.length] - the expected number of output layers.
@returns {Array<object>} an array of objects ``{layer, latlng, distance}`` or ``null`` if the input is invalid (empty list or negative n)
*/
nClosestLayers: function (map, layers, latlng, n) {
n = typeof n === 'number' ? n : layers.length;
if (n < 1 || layers.length < 1) {
return null;
}
var results = [];
var distance, ll;
for (var i = 0, m = layers.length; i < m; i++) {
var layer = layers[i];
if (layer instanceof L.LayerGroup) {
// recursive
var subResult = L.GeometryUtil.closestLayer(map, layer.getLayers(), latlng);
results.push(subResult);
} else {
// Single dimension, snap on points, else snap on closest
if (typeof layer.getLatLng == 'function') {
ll = layer.getLatLng();
distance = L.GeometryUtil.distance(map, latlng, ll);
}
else {
ll = L.GeometryUtil.closest(map, layer, latlng);
if (ll) distance = ll.distance; // Can return null if layer has no points.
}
results.push({layer: layer, latlng: ll, distance: distance});
}
}
results.sort(function(a, b) {
return a.distance - b.distance;
});
if (results.length > n) {
return results.slice(0, n);
} else {
return results;
}
},
/**
* Returns all layers within a radius of the given position, in an ascending order of distance.
@param {L.Map} map Leaflet map to be used for this method
@param {Array<ILayer>} layers - A list of layers.
@param {L.LatLng} latlng - The position to search
@param {?Number} [radius=Infinity] - Search radius in pixels
@return {object[]} an array of objects including layer within the radius, closest latlng, and distance
*/
layersWithin: function(map, layers, latlng, radius) {
radius = typeof radius == 'number' ? radius : Infinity;
var results = [];
var ll = null;
var distance = 0;
for (var i = 0, n = layers.length; i < n; i++) {
var layer = layers[i];
if (typeof layer.getLatLng == 'function') {
ll = layer.getLatLng();
distance = L.GeometryUtil.distance(map, latlng, ll);
}
else {
ll = L.GeometryUtil.closest(map, layer, latlng);
if (ll) distance = ll.distance; // Can return null if layer has no points.
}
if (ll && distance < radius) {
results.push({layer: layer, latlng: ll, distance: distance});
}
}
var sortedResults = results.sort(function(a, b) {
return a.distance - b.distance;
});
return sortedResults;
},
/**
Returns the closest position from specified {LatLng} among specified layers,
with a maximum tolerance in pixels, providing snapping behaviour.
@tutorial closest
@param {L.Map} map Leaflet map to be used for this method
@param {Array<ILayer>} layers - A list of layers to snap on.
@param {L.LatLng} latlng - The position to snap
@param {?Number} [tolerance=Infinity] - Maximum number of pixels.
@param {?boolean} [withVertices=true] - Snap to layers vertices or segment points (not only vertex)
@returns {object} with snapped {LatLng} and snapped {Layer} or null if tolerance exceeded.
*/
closestLayerSnap: function (map, layers, latlng, tolerance, withVertices) {
tolerance = typeof tolerance == 'number' ? tolerance : Infinity;
withVertices = typeof withVertices == 'boolean' ? withVertices : true;
var result = L.GeometryUtil.closestLayer(map, layers, latlng);
if (!result || result.distance > tolerance)
return null;
// If snapped layer is linear, try to snap on vertices (extremities and middle points)
if (withVertices && typeof result.layer.getLatLngs == 'function') {
var closest = L.GeometryUtil.closest(map, result.layer, result.latlng, true);
if (closest.distance < tolerance) {
result.latlng = closest;
result.distance = L.GeometryUtil.distance(map, closest, latlng);
}
}
return result;
},
/**
Returns the Point located on a segment at the specified ratio of the segment length.
@param {L.Point} pA coordinates of point A
@param {L.Point} pB coordinates of point B
@param {Number} the length ratio, expressed as a decimal between 0 and 1, inclusive.
@returns {L.Point} the interpolated point.
*/
interpolateOnPointSegment: function (pA, pB, ratio) {
return L.point(
(pA.x * (1 - ratio)) + (ratio * pB.x),
(pA.y * (1 - ratio)) + (ratio * pB.y)
);
},
/**
Returns the coordinate of the point located on a line at the specified ratio of the line length.
@param {L.Map} map Leaflet map to be used for this method
@param {Array<L.LatLng>|L.PolyLine} latlngs Set of geographical points
@param {Number} ratio the length ratio, expressed as a decimal between 0 and 1, inclusive
@returns {Object} an object with latLng ({LatLng}) and predecessor ({Number}), the index of the preceding vertex in the Polyline
(-1 if the interpolated point is the first vertex)
*/
interpolateOnLine: function (map, latLngs, ratio) {
latLngs = (latLngs instanceof L.Polyline) ? latLngs.getLatLngs() : latLngs;
var n = latLngs.length;
if (n < 2) {
return null;
}
// ensure the ratio is between 0 and 1;
ratio = Math.max(Math.min(ratio, 1), 0);
if (ratio === 0) {
return {
latLng: latLngs[0] instanceof L.LatLng ? latLngs[0] : L.latLng(latLngs[0]),
predecessor: -1
};
}
if (ratio == 1) {
return {
latLng: latLngs[latLngs.length -1] instanceof L.LatLng ? latLngs[latLngs.length -1] : L.latLng(latLngs[latLngs.length -1]),
predecessor: latLngs.length - 2
};
}
// project the LatLngs as Points,
// and compute total planar length of the line at max precision
var maxzoom = map.getMaxZoom();
if (maxzoom === Infinity)
maxzoom = map.getZoom();
var pts = [];
var lineLength = 0;
for(var i = 0; i < n; i++) {
pts[i] = map.project(latLngs[i], maxzoom);
if(i > 0)
lineLength += pts[i-1].distanceTo(pts[i]);
}
var ratioDist = lineLength * ratio;
// follow the line segments [ab], adding lengths,
// until we find the segment where the points should lie on
var cumulativeDistanceToA = 0, cumulativeDistanceToB = 0;
for (var i = 0; cumulativeDistanceToB < ratioDist; i++) {
var pointA = pts[i], pointB = pts[i+1];
cumulativeDistanceToA = cumulativeDistanceToB;
cumulativeDistanceToB += pointA.distanceTo(pointB);
}
if (pointA == undefined && pointB == undefined) { // Happens when line has no length
var pointA = pts[0], pointB = pts[1], i = 1;
}
// compute the ratio relative to the segment [ab]
var segmentRatio = ((cumulativeDistanceToB - cumulativeDistanceToA) !== 0) ? ((ratioDist - cumulativeDistanceToA) / (cumulativeDistanceToB - cumulativeDistanceToA)) : 0;
var interpolatedPoint = L.GeometryUtil.interpolateOnPointSegment(pointA, pointB, segmentRatio);
return {
latLng: map.unproject(interpolatedPoint, maxzoom),
predecessor: i-1
};
},
/**
Returns a float between 0 and 1 representing the location of the
closest point on polyline to the given latlng, as a fraction of total line length.
(opposite of L.GeometryUtil.interpolateOnLine())
@param {L.Map} map Leaflet map to be used for this method
@param {L.PolyLine} polyline Polyline on which the latlng will be search
@param {L.LatLng} latlng The position to search
@returns {Number} Float between 0 and 1
*/
locateOnLine: function (map, polyline, latlng) {
var latlngs = polyline.getLatLngs();
if (latlng.equals(latlngs[0]))
return 0.0;
if (latlng.equals(latlngs[latlngs.length-1]))
return 1.0;
var point = L.GeometryUtil.closest(map, polyline, latlng, false),
lengths = L.GeometryUtil.accumulatedLengths(latlngs),
total_length = lengths[lengths.length-1],
portion = 0,
found = false;
for (var i=0, n = latlngs.length-1; i < n; i++) {
var l1 = latlngs[i],
l2 = latlngs[i+1];
portion = lengths[i];
if (L.GeometryUtil.belongsSegment(point, l1, l2, 0.001)) {
portion += l1.distanceTo(point);
found = true;
break;
}
}
if (!found) {
throw "Could not interpolate " + latlng.toString() + " within " + polyline.toString();
}
return portion / total_length;
},
/**
Returns a clone with reversed coordinates.
@param {L.PolyLine} polyline polyline to reverse
@returns {L.PolyLine} polyline reversed
*/
reverse: function (polyline) {
return L.polyline(polyline.getLatLngs().slice(0).reverse());
},
/**
Returns a sub-part of the polyline, from start to end.
If start is superior to end, returns extraction from inverted line.
@param {L.Map} map Leaflet map to be used for this method
@param {L.PolyLine} polyline Polyline on which will be extracted the sub-part
@param {Number} start ratio, expressed as a decimal between 0 and 1, inclusive
@param {Number} end ratio, expressed as a decimal between 0 and 1, inclusive
@returns {Array<L.LatLng>} new polyline
*/
extract: function (map, polyline, start, end) {
if (start > end) {
return L.GeometryUtil.extract(map, L.GeometryUtil.reverse(polyline), 1.0-start, 1.0-end);
}
// Bound start and end to [0-1]
start = Math.max(Math.min(start, 1), 0);
end = Math.max(Math.min(end, 1), 0);
var latlngs = polyline.getLatLngs(),
startpoint = L.GeometryUtil.interpolateOnLine(map, polyline, start),
endpoint = L.GeometryUtil.interpolateOnLine(map, polyline, end);
// Return single point if start == end
if (start == end) {
var point = L.GeometryUtil.interpolateOnLine(map, polyline, end);
return [point.latLng];
}
// Array.slice() works indexes at 0
if (startpoint.predecessor == -1)
startpoint.predecessor = 0;
if (endpoint.predecessor == -1)
endpoint.predecessor = 0;
var result = latlngs.slice(startpoint.predecessor+1, endpoint.predecessor+1);
result.unshift(startpoint.latLng);
result.push(endpoint.latLng);
return result;
},
/**
Returns true if first polyline ends where other second starts.
@param {L.PolyLine} polyline First polyline
@param {L.PolyLine} other Second polyline
@returns {bool}
*/
isBefore: function (polyline, other) {
if (!other) return false;
var lla = polyline.getLatLngs(),
llb = other.getLatLngs();
return (lla[lla.length-1]).equals(llb[0]);
},
/**
Returns true if first polyline starts where second ends.
@param {L.PolyLine} polyline First polyline
@param {L.PolyLine} other Second polyline
@returns {bool}
*/
isAfter: function (polyline, other) {
if (!other) return false;
var lla = polyline.getLatLngs(),
llb = other.getLatLngs();
return (lla[0]).equals(llb[llb.length-1]);
},
/**
Returns true if first polyline starts where second ends or start.
@param {L.PolyLine} polyline First polyline
@param {L.PolyLine} other Second polyline
@returns {bool}
*/
startsAtExtremity: function (polyline, other) {
if (!other) return false;
var lla = polyline.getLatLngs(),
llb = other.getLatLngs(),
start = lla[0];
return start.equals(llb[0]) || start.equals(llb[llb.length-1]);
},
/**
Returns horizontal angle in degres between two points.
@param {L.Point} a Coordinates of point A
@param {L.Point} b Coordinates of point B
@returns {Number} horizontal angle
*/
computeAngle: function(a, b) {
return (Math.atan2(b.y - a.y, b.x - a.x) * 180 / Math.PI);
},
/**
Returns slope (Ax+B) between two points.
@param {L.Point} a Coordinates of point A
@param {L.Point} b Coordinates of point B
@returns {Object} with ``a`` and ``b`` properties.
*/
computeSlope: function(a, b) {
var s = (b.y - a.y) / (b.x - a.x),
o = a.y - (s * a.x);
return {'a': s, 'b': o};
},
/**
Returns LatLng of rotated point around specified LatLng center.
@param {L.LatLng} latlngPoint: point to rotate
@param {double} angleDeg: angle to rotate in degrees
@param {L.LatLng} latlngCenter: center of rotation
@returns {L.LatLng} rotated point
*/
rotatePoint: function(map, latlngPoint, angleDeg, latlngCenter) {
var maxzoom = map.getMaxZoom();
if (maxzoom === Infinity)
maxzoom = map.getZoom();
var angleRad = angleDeg*Math.PI/180,
pPoint = map.project(latlngPoint, maxzoom),
pCenter = map.project(latlngCenter, maxzoom),
x2 = Math.cos(angleRad)*(pPoint.x-pCenter.x) - Math.sin(angleRad)*(pPoint.y-pCenter.y) + pCenter.x,
y2 = Math.sin(angleRad)*(pPoint.x-pCenter.x) + Math.cos(angleRad)*(pPoint.y-pCenter.y) + pCenter.y;
return map.unproject(new L.Point(x2,y2), maxzoom);
},
/**
Returns the bearing in degrees clockwise from north (0 degrees)
from the first L.LatLng to the second, at the first LatLng
@param {L.LatLng} latlng1: origin point of the bearing
@param {L.LatLng} latlng2: destination point of the bearing
@returns {float} degrees clockwise from north.
*/
bearing: function(latlng1, latlng2) {
var rad = Math.PI / 180,
lat1 = latlng1.lat * rad,
lat2 = latlng2.lat * rad,
lon1 = latlng1.lng * rad,
lon2 = latlng2.lng * rad,
y = Math.sin(lon2 - lon1) * Math.cos(lat2),
x = Math.cos(lat1) * Math.sin(lat2) -
Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
var bearing = ((Math.atan2(y, x) * 180 / Math.PI) + 360) % 360;
return bearing >= 180 ? bearing-360 : bearing;
},
/**
Returns the point that is a distance and heading away from
the given origin point.
@param {L.LatLng} latlng: origin point
@param {float} heading: heading in degrees, clockwise from 0 degrees north.
@param {float} distance: distance in meters
@returns {L.latLng} the destination point.
Many thanks to Chris Veness at http://www.movable-type.co.uk/scripts/latlong.html
for a great reference and examples.
*/
destination: function(latlng, heading, distance) {
heading = (heading + 360) % 360;
var rad = Math.PI / 180,
radInv = 180 / Math.PI,
R = 6378137, // approximation of Earth's radius
lon1 = latlng.lng * rad,
lat1 = latlng.lat * rad,
rheading = heading * rad,
sinLat1 = Math.sin(lat1),
cosLat1 = Math.cos(lat1),
cosDistR = Math.cos(distance / R),
sinDistR = Math.sin(distance / R),
lat2 = Math.asin(sinLat1 * cosDistR + cosLat1 *
sinDistR * Math.cos(rheading)),
lon2 = lon1 + Math.atan2(Math.sin(rheading) * sinDistR *
cosLat1, cosDistR - sinLat1 * Math.sin(lat2));
lon2 = lon2 * radInv;
lon2 = lon2 > 180 ? lon2 - 360 : lon2 < -180 ? lon2 + 360 : lon2;
return L.latLng([lat2 * radInv, lon2]);
},
/**
Returns the the angle of the given segment and the Equator in degrees,
clockwise from 0 degrees north.
@param {L.Map} map: Leaflet map to be used for this method
@param {L.LatLng} latlngA: geographical point A of the segment
@param {L.LatLng} latlngB: geographical point B of the segment
@returns {Float} the angle in degrees.
*/
angle: function(map, latlngA, latlngB) {
var pointA = map.latLngToContainerPoint(latlngA),
pointB = map.latLngToContainerPoint(latlngB),
angleDeg = Math.atan2(pointB.y - pointA.y, pointB.x - pointA.x) * 180 / Math.PI + 90;
angleDeg += angleDeg < 0 ? 360 : 0;
return angleDeg;
},
/**
Returns a point snaps on the segment and heading away from the given origin point a distance.
@param {L.Map} map: Leaflet map to be used for this method
@param {L.LatLng} latlngA: geographical point A of the segment
@param {L.LatLng} latlngB: geographical point B of the segment
@param {float} distance: distance in meters
@returns {L.latLng} the destination point.
*/
destinationOnSegment: function(map, latlngA, latlngB, distance) {
var angleDeg = L.GeometryUtil.angle(map, latlngA, latlngB),
latlng = L.GeometryUtil.destination(latlngA, angleDeg, distance);
return L.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB);
},
});
return L.GeometryUtil;
}));

File diff suppressed because one or more lines are too long