File : core/lib/Geometry.js

1
/*
2
Copyright - 2017 2023 - wwwouaiebe - Contact: https://www.ouaie.be/
3
4
This  program is free software;
5
you can redistribute it and/or modify it under the terms of the
6
GNU General Public License as published by the Free Software Foundation;
7
either version 3 of the License, or any later version.
8
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU General Public License for more details.
13
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
*/
18
/*
19
Changes:
20
    - v4.0.0:
21
        - created from v3.6.0
22
Doc reviewed 202208
23
 */
24
25
import theTravelNotesData from '../../data/TravelNotesData.js';
26
import { DISTANCE, ZERO, ONE, TWO, DEGREES, LAT_LNG, LAT, LNG, EARTH_RADIUS } from '../../main/Constants.js';
27
import LatLngDistance from '../../containers/LatLngDistance.js';
28
import LatLngElevOnRoute from '../../containers/LatLngElevOnRoute.js';
29
30
import {
31
    LeafletLatLng,
32
    LeafletLatLngBounds,
33
    LeafletPoint,
34
    LeafletLineUtil,
35
    LeafletProjection
36
} from '../../leaflet/LeafletImports.js';
37
38
/* ------------------------------------------------------------------------------------------------------------------------- */
39
/**
40
This class contains methods for geometry operations requiring call to Leaflet functions
41
42
See theGeometry for the one and only one instance of this class
43
*/
44
/* ------------------------------------------------------------------------------------------------------------------------- */
45
46
class Geometry {
47
48
    /**
49
    Simple constant for computation
50
    @type {Number}
51
    */
52
53
    // eslint-disable-next-line no-magic-numbers
54
    static get #HUNDRED ( ) { return 100; }
55
56
    /**
57
    The constructor
58
    */
59
60
    constructor ( ) {
61
        Object.freeze ( this );
62
    }
63
64
    /**
65
    Compute the latitude, longitude, elevation, ascent and distance of a point on a route when only the distance between
66
    the beginning of the route and the point is know
67
    @param {Route} route The route
68
    @param {Number} distance The distance (units: meter)
69
    @return {LatLngElevOnRoute} A LatLngElevOnRoute with the desired values
70
    */
71
72
    getLatLngElevAtDist ( route, distance ) {
73
        if ( route.distance <= distance || ZERO >= distance ) {
74
            return null;
75
        }
76
        let nearestDistance = 0;
77
        const itineraryPointsIterator = route.itinerary.itineraryPoints.iterator;
78
        while ( nearestDistance < distance && ! itineraryPointsIterator.done ) {
79
            nearestDistance += itineraryPointsIterator.value.distance;
80
        }
81
        const previousItineraryPoint = itineraryPointsIterator.value;
82
        itineraryPointsIterator.done;
83
        const scale = ( previousItineraryPoint.distance - nearestDistance + distance ) / previousItineraryPoint.distance;
84
85
        return new LatLngElevOnRoute (
86
            previousItineraryPoint.lat + ( ( itineraryPointsIterator.value.lat - previousItineraryPoint.lat ) * scale ),
87
            previousItineraryPoint.lng + ( ( itineraryPointsIterator.value.lng - previousItineraryPoint.lng ) * scale ),
88
            distance,
89
            previousItineraryPoint.elev + ( ( itineraryPointsIterator.value.elev - previousItineraryPoint.elev ) * scale ),
90
            Geometry.#HUNDRED *
91
                ( itineraryPointsIterator.value.elev - previousItineraryPoint.elev ) /     previousItineraryPoint.distance
92
        );
93
    }
94
95
    /**
96
    This method search the nearest point on a route from a given point and compute the distance
97
    between the beginning of the route and the nearest point
98
    @param {Route} route The route object to be used
99
    @param {Array.<Number>} latLng The latitude and longitude of the point
100
    @return {LatLngDistance} An object with the latitude, longitude and distance
101
    */
102
103
    getClosestLatLngDistance ( route, latLng ) {
104
        if ( ZERO === route.itinerary.itineraryPoints.length ) {
105
            return null;
106
        }
107
        const itineraryPointIterator = route.itinerary.itineraryPoints.iterator;
108
        itineraryPointIterator.done;
109
        let minDistance = Number.MAX_VALUE;
110
111
        // projections of points are made
112
        const point = LeafletProjection.SphericalMercator.project (
113
            new LeafletLatLng ( latLng [ ZERO ], latLng [ ONE ] ) );
114
        let point1 = LeafletProjection.SphericalMercator.project (
115
            new LeafletLatLng ( itineraryPointIterator.value.lat, itineraryPointIterator.value.lng )
116
        );
117
        let closestLatLng = null;
118
        let closestDistance = DISTANCE.defaultValue;
119
        let endSegmentDistance = itineraryPointIterator.value.distance;
120
        while ( ! itineraryPointIterator.done ) {
121
            const point2 = LeafletProjection.SphericalMercator.project (
122
                new LeafletLatLng ( itineraryPointIterator.value.lat, itineraryPointIterator.value.lng )
123
            );
124
            let distance = LeafletLineUtil.pointToSegmentDistance ( point, point1, point2 );
125
            if ( distance < minDistance ) {
126
                minDistance = distance;
127
                closestLatLng = LeafletProjection.SphericalMercator.unproject (
128
                    LeafletLineUtil.closestPointOnSegment ( point, point1, point2 )
129
                );
130
                closestDistance =
131
                    endSegmentDistance -
132
                    closestLatLng.distanceTo (
133
                        new LeafletLatLng ( itineraryPointIterator.value.lat, itineraryPointIterator.value.lng )
134
                    );
135
            }
136
            endSegmentDistance += itineraryPointIterator.value.distance;
137
            point1 = point2;
138
        }
139
        return new LatLngDistance ( closestLatLng.lat, closestLatLng.lng, closestDistance );
140
    }
141
142
    /**
143
    This method build a LeafletLatLngBounds object from an array of points
144
    @param {Array.<Array.<number>>} latLngs the array of latitude and longitude
145
    @return {LeafletObject} a Leaflet latLngBounds object
146
    */
147
148
    getLatLngBounds ( latLngs ) {
149
        const sw = new LeafletLatLng ( LAT_LNG.maxLat, LAT_LNG.maxLng );
150
        const ne = new LeafletLatLng ( LAT_LNG.minLat, LAT_LNG.minLng );
151
        latLngs.forEach (
152
            latLng => {
153
                sw.lat = Math.min ( sw.lat, latLng [ ZERO ] );
154
                sw.lng = Math.min ( sw.lng, latLng [ ONE ] );
155
                ne.lat = Math.max ( ne.lat, latLng [ ZERO ] );
156
                ne.lng = Math.max ( ne.lng, latLng [ ONE ] );
157
            }
158
        );
159
        return new LeafletLatLngBounds ( sw, ne );
160
    }
161
162
    /**
163
    This method returns a LeafletLatLngBounds that represents a square
164
    @param {Array.<Number>} latLngCenter The latitude and longitude of the center of the square
165
    @param {Number} dimension The half length of the square side in meter.
166
    */
167
168
    getSquareBoundingBox ( latLngCenter, dimension ) {
169
        const latCenterRad = latLngCenter [ ZERO ] * DEGREES.toRadians;
170
        const deltaLat = ( dimension / EARTH_RADIUS ) * DEGREES.fromRadians;
171
        const deltaLng =
172
            Math.acos (
173
                ( Math.cos ( dimension / EARTH_RADIUS ) - ( Math.sin ( latCenterRad ) ** TWO ) ) /
174
                ( Math.cos ( latCenterRad ) ** TWO )
175
            ) * DEGREES.fromRadians;
176
        return new LeafletLatLngBounds (
177
            new LeafletLatLng ( latLngCenter [ ZERO ] - deltaLat, latLngCenter [ ONE ] - deltaLng ),
178
            new LeafletLatLng ( latLngCenter [ ZERO ] + deltaLat, latLngCenter [ ONE ] + deltaLng )
179
        );
180
    }
181
182
    /**
183
    This method transforms a lat lng coordinate to pixel coordinate relative to the CRS origin using the Leaflet
184
    method map.project
185
    @param {Array.<Number>} latLng The latitude and longitude of the point
186
    @param {Number} zoom The zoom factor to use
187
    @return {Array.<Number>} An array with the projected point
188
    */
189
190
    project ( latLng, zoom ) {
191
        const projection = theTravelNotesData.map.project ( new LeafletLatLng ( latLng [ LAT ], latLng [ LNG ] ), zoom );
192
        return [ projection.x, projection.y ];
193
    }
194
195
    /**
196
    Transform a screen coordinate to a latLng using the Leaflet map.containerPointToLatLng method
197
    @param {Number} xScreen  The x screen coordinate
198
    @param {Number} yScreen  The y screen coordinate
199
    @return {Array.<Number>} The latitude and longitude of the point
200
    */
201
202
    screenCoordToLatLng ( xScreen, yScreen ) {
203
        const latLng = theTravelNotesData.map.containerPointToLatLng ( new LeafletPoint ( xScreen, yScreen ) );
204
        return [ latLng.lat, latLng.lng ];
205
    }
206
207
    /**
208
    Add two points
209
    @param {Array.<Number>} point1 the first point to add
210
    @param {Array.<Number>} point2 the second point to add
211
    @return {Array.<Number>}  The result point
212
    */
213
214
    addPoints ( point1, point2 ) {
215
        return [
216
            point1 [ ZERO ] + point2 [ ZERO ],
217
            point1 [ ONE ] + point2 [ ONE ]
218
        ];
219
    }
220
221
    /**
222
    Subtrack two points
223
    @param {Array.<Number>} point1 the first point
224
    @param {Array.<Number>} point2 the point to subtrack
225
    @return {Array.<Number>} The result point
226
    */
227
228
    subtrackPoints ( point1, point2 ) {
229
        return [
230
            point1 [ ZERO ] - point2 [ ZERO ],
231
            point1 [ ONE ] - point2 [ ONE ]
232
        ];
233
    }
234
}
235
236
/* ------------------------------------------------------------------------------------------------------------------------- */
237
/**
238
The one and only one instance of Geometry class
239
@type {Geometry}
240
*/
241
/* ------------------------------------------------------------------------------------------------------------------------- */
242
243
const theGeometry = new Geometry ( );
244
245
export default theGeometry;
246
247
/* --- End of file --------------------------------------------------------------------------------------------------------- */
248