File : core/lib/GeoCoder.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 theConfig from '../../data/Config.js';
26
import OverpassAPIDataLoader from './OverpassAPIDataLoader.js';
27
import NominatimDataLoader from './NominatimDataLoader.js';
28
import GeoCoderAddress from './GeoCoderAddress.js';
29
import { ZERO, ONE } from '../../main/Constants.js';
30
31
/* ------------------------------------------------------------------------------------------------------------------------- */
32
/**
33
A simple container to store an address created by the geocoder
34
*/
35
/* ------------------------------------------------------------------------------------------------------------------------- */
36
/* ------------------------------------------------------------------------------------------------------------------------- */
37
/**
38
This class call Nominatim and parse the response
39
*/
40
/* ------------------------------------------------------------------------------------------------------------------------- */
41
42
class GeoCoder {
43
44
    /**
45
    The distance used in the OverpassAPI query for places
46
    @type {Number}
47
    */
48
49
    static get #queryDistance ( ) {
50
        return Math.max (
51
            theConfig.geoCoder.distances.hamlet,
52
            theConfig.geoCoder.distances.village,
53
            theConfig.geoCoder.distances.city,
54
            theConfig.geoCoder.distances.town
55
        );
56
    }
57
58
    /**
59
    The Lat and Lng for thr geocoding
60
    @type {Array.<Number>}
61
    */
62
63
    #latLng;
64
65
    /**
66
    The OverpassAPIDataLoader object
67
    @type {OverpassAPIDataLoader}
68
    */
69
70
    #overpassAPIDataLoader;
71
72
    /**
73
    The NominatimDataLoader object
74
    @type {NominatimDataLoader}
75
    */
76
77
    #nominatimDataLoader;
78
79
    /**
80
    this method merge the data from Nominatim and theOverpassAPI
81
    @return {GeoCoderAddress} the address at the given point
82
    */
83
84
    #mergeData ( ) {
85
        let city =
86
            this.#nominatimDataLoader.city
87
            ||
88
            this.#nominatimDataLoader.country
89
            ||
90
            '';
91
92
        const place = this.#overpassAPIDataLoader.place;
93
        if ( place && place !== city ) {
94
            city += ' (' + place + ')';
95
        }
96
97
        let street = ( this.#nominatimDataLoader.street || '' ).replaceAll ( ';', ' ' );
98
99
        let nominatimName = this.#nominatimDataLoader.name || '';
100
101
        if ( street.includes ( nominatimName ) || city.includes ( nominatimName ) ) {
102
            nominatimName = '';
103
        }
104
105
        return new GeoCoderAddress ( nominatimName, street, city );
106
    }
107
108
    /**
109
    This method get the Overpass query
110
    */
111
112
    #getOverpassQueries ( ) {
113
        return [
114
115
            /* 'is_in(' + this.#latLng [ ZERO ] + ',' + this.#latLng [ ONE ] +
116
            ')->.e;area.e[admin_level][boundary="administrative"];out;' +*/
117
118
            'node(around:' + GeoCoder.#queryDistance + ',' + this.#latLng [ ZERO ] + ',' + this.#latLng [ ONE ] +
119
            ')[place];out;'
120
        ];
121
    }
122
123
    /**
124
    This method search the address
125
    @return {GeoCoderAddress} the address at the given point.
126
    */
127
128
    async #getAddressAsync ( ) {
129
130
        await this.#overpassAPIDataLoader.loadData ( this.#getOverpassQueries ( ), this.#latLng );
131
        await this.#nominatimDataLoader.loadData ( this.#latLng );
132
        return this.#mergeData ( );
133
    }
134
135
    /**
136
    This method is executed by the Promise to search an address. The #getAddressAsync return always a response,
137
    eventually with empty strings, so the onError function is never called
138
    @param {function} onOk The ok handler
139
    @param {function} onError The error handler
140
    */
141
142
    // eslint-disable-next-line no-unused-vars
143
    async #getAddressWithPromise ( onOk, onError ) {
144
        onOk ( await this.#getAddressAsync ( ) );
145
    }
146
147
    /**
148
    The constructor
149
    */
150
151
    constructor ( ) {
152
        Object.freeze ( this );
153
        this.#overpassAPIDataLoader = new OverpassAPIDataLoader (
154
            { searchWays : false, searchRelations : false, setGeometry : true }
155
        );
156
        this.#nominatimDataLoader = new NominatimDataLoader (
157
            {
158
                searchPlaces : true,
159
                searchWays : false,
160
                searchRelations : false,
161
                setGeometry : false
162
            }
163
        );
164
    }
165
166
    /**
167
    This async method search an address from a latitude and longitude
168
    @param {Array.<Number>} latLng the latitude and longitude to be used to search the address
169
    @return {GeoCoderAddress} the address at the given point. The GeoCoderAddress.statusOk must be verified
170
    before using the data.
171
    */
172
173
    async getAddressAsync ( latLng ) {
174
        this.#latLng = latLng;
175
        return this.#getAddressAsync ( );
176
    }
177
178
    /**
179
    This method search an address from a latitude and longitude with a Promise.
180
    @param {Array.<Number>} latLng the latitude and longitude to be used to search the address
181
    @return {Promise} A promise that fulfill with the address at the given point.
182
    */
183
184
    getAddressWithPromise ( latLng ) {
185
        this.#latLng = latLng;
186
        return new Promise ( ( onOk, onError ) => this.#getAddressWithPromise ( onOk, onError ) );
187
    }
188
189
}
190
export default GeoCoder;
191
192
/* --- End of file --------------------------------------------------------------------------------------------------------- */
193