/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
/* vim: set et ts=4 sw=4: */
/*
* GNOME Maps is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* GNOME Maps is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with GNOME Maps; if not, see .
*
* Author: Rishi Raj Singh Jhelumi
*/
import Geocode from 'gi://GeocodeGlib';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Soup from 'gi://Soup';
import * as OSMNames from './osmNames.js';
import * as PhotonUtils from './photonUtils.js';
import {Place} from './place.js';
import * as Utils from './utils.js';
const _DEFAULT_TIMEOUT = 180;
const _DEFAULT_MAXSIZE = 536870912;
const _DEFAULT_OUTPUT_FORMAT = 'json';
const _DEFAULT_OUTPUT_COUNT = 1e6;
const _DEFAULT_OUTPUT_INFO = 'body';
const _DEFAULT_OUTPUT_SORT_ORDER = 'qt';
const BASE_URL = 'https://overpass-api.de/api/interpreter';
export class Overpass {
constructor(params) {
params = params || { };
// maximum allowed runtime for the query in seconds
this.timeout = params.timeout || _DEFAULT_TIMEOUT;
// maximum allowed memory for the query in bytes RAM on the server
this.maxsize = params.maxsize || _DEFAULT_MAXSIZE;
// output format : json or xml
this.outputFormat = params.outputFormat || _DEFAULT_OUTPUT_FORMAT;
// maximum number of results the output must contain
this.outputCount = params.outputCount || _DEFAULT_OUTPUT_COUNT;
// data output info must contain : ids, skel, body, meta
this.outputInfo = params.outputInfo || _DEFAULT_OUTPUT_INFO;
// data sort order : qt(fastest based on geography), ids, asc
this.outputSortOrder = params.outputSortOrder || _DEFAULT_OUTPUT_SORT_ORDER;
// HTTP Session Variables
this._session = new Soup.Session({ user_agent : 'gnome-maps/' + pkg.version });
}
populatePlace(place, callback) {
let url = this._getQueryUrl(Utils.osmTypeToString(place.osm_type),
place.osm_id);
let request = Soup.Message.new('GET', url);
this._session.send_and_read_async(request, GLib.PRIORITY_DEFAULT, null,
(source, res) => {
if (request.get_status() !== Soup.Status.OK) {
Utils.debug('Failed to fetch Overpass result: ' +
request.get_status());
callback(false);
return;
}
try {
let buffer = this._session.send_and_read_finish(res).get_data();
let jsonObj = JSON.parse(Utils.getBufferText(buffer));
this._populatePlace(place, jsonObj);
callback(true);
} catch(e) {
Utils.debug('Failed to parse Overpass result');
callback(false);
}
});
}
fetchPlace(osmType, osmId, callback) {
let url = this._getQueryUrl(osmType, osmId)
let request = Soup.Message.new('GET', url);
this._session.send_and_read_async(request, GLib.PRIORITY_DEFAULT, null,
(source, res) => {
if (request.get_status() !== Soup.Status.OK) {
Utils.debug('Failed to fetch Overpass result: ' +
request.get_status());
callback(null);
return;
}
try {
let buffer = this._session.send_and_read_finish(res).get_data();
let jsonObj = JSON.parse(Utils.getBufferText(buffer));
let place = this._createPlace(jsonObj, osmType, osmId);
callback(place);
} catch(e) {
Utils.debug('Failed to parse Overpass result');
callback(null);
}
})
}
_createPlace(overpassData, osmType, osmId) {
let element = overpassData.elements[0];
if (!(element && element.tags))
return null;
let [lat, lon] = this._getCoordsFromElement(element);
let photonProperties =
this._getPhotonProperties(element.tags, osmType, osmId);
let place = PhotonUtils.parsePlace(lat, lon, photonProperties);
this._populatePlace(place, overpassData);
place.prefilled = true;
return place;
}
_getCoordsFromElement(element) {
if (element.type === 'node')
return [element.lat, element.lon];
else
return [element.center.lat, element.center.lon];
}
_getPhotonProperties(tags, osmType, osmId) {
let properties = {};
properties.osm_type = this._getPhotonOsmType(osmType);
properties.osm_id = osmId;
if (tags.name)
properties.name = tags.name;
if (tags['addr:street'])
properties.street = tags['addr:street'];
if (tags['addr:housenumber'])
properties.housenumber = tags['addr:housenumber'];
if (tags['addr:postcode'])
properties.postcode = tags['addr:postcode'];
if (tags['addr:city'])
properties.city = tags['addr:city'];
if (tags['addr:country'])
properties.countrycode = tags['addr:country'];
if (tags.place)
this._setOsmKeyAndValue(properties, tags, 'place');
else if (tags.amenity)
this._setOsmKeyAndValue(properties, tags, 'amenity');
else if (tags.leisure)
this._setOsmKeyAndValue(properties, tags, 'leisure');
else if (tags.shop)
this._setOsmKeyAndValue(properties, tags, 'shop');
else if (tags.highway)
this._setOsmKeyAndValue(properties, tags, 'highway');
else if (tags.railway)
this._setOsmKeyAndValue(properties, tags, 'railway');
else if (tags.aeroway)
this._setOsmKeyAndValue(properties, tags, 'aeroway');
else if (tags.building)
this._setOsmKeyAndValue(properties, tags, 'building');
return properties;
}
_getPhotonOsmType(osmType) {
switch (osmType) {
case 'node': return 'N';
case 'way': return 'W';
case 'relation': return 'R';
default: return null;
}
}
_setOsmKeyAndValue(properties, tags, tag) {
properties.osm_key = tag;
properties.osm_value = tags[tag];
}
_populatePlace(place, overpassData) {
let element = overpassData.elements[0];
if (!(element && element.tags))
return;
let name = this._getLocalizedName(element.tags, place);
if (name)
place.name = name;
if (element.tags.name)
place.nativeName = element.tags.name;
place.updateFromTags(element.tags);
}
_getLocalizedName(tags, place) {
let language = Utils.getLanguage();
return OSMNames.getNameForLanguageAndCountry(tags, language,
place.country_code);
}
_getQueryUrl(osmType, osmId) {
return `${BASE_URL}?data=${this._generateOverpassQuery(osmType, osmId)}`;
}
_generateOverpassQuery(osmType, osmId) {
let timeout = this._getKeyValue('timeout', this.timeout);
let out = this._getKeyValue('out', this.outputFormat);
let maxSize = this._getKeyValue('maxsize', this.maxsize);
let data = this._getData(osmType, osmId);
let output = this._getOutput();
return `${timeout}${out}${maxSize};${data};${output};`;
}
_getKeyValue(key, value) {
return `[${key}:${value}]`;
}
_getData(osmType, osmId) {
return `${osmType}(${osmId})`;
}
_getOutput() {
return `out center ${this.outputInfo} ${this.outputSortOrder} ${this.outputCount}`;
}
}