/**
* This module provides functions to retrieve data from and upload data
* to an OSM server.
*
* @example
* import {Api, ChangesetApi, ApiConfig} from 'josm/api'
*
* @module josm/api
*/
/* global Java */
const URL = Java.type('java.net.URL')
const OsmApi = Java.type('org.openstreetmap.josm.io.OsmApi')
const Changeset = Java.type('org.openstreetmap.josm.data.osm.Changeset')
const OsmPrimitiveType = Java.type('org.openstreetmap.josm.data.osm.OsmPrimitiveType')
const PrimitiveId = Java.type('org.openstreetmap.josm.data.osm.PrimitiveId')
const SimplePrimitiveId = Java.type('org.openstreetmap.josm.data.osm.SimplePrimitiveId')
const NullProgressMonitor = Java.type('org.openstreetmap.josm.gui.progress.NullProgressMonitor')
const OsmServerChangesetReader = Java.type('org.openstreetmap.josm.io.OsmServerChangesetReader')
const OsmServerObjectReader = Java.type('org.openstreetmap.josm.io.OsmServerObjectReader')
const OsmServerBackreferenceReader = Java.type('org.openstreetmap.josm.io.OsmServerBackreferenceReader')
const Preferences = Java.type('org.openstreetmap.josm.data.Preferences')
const Bounds = Java.type('org.openstreetmap.josm.data.Bounds')
const LatLon = Java.type('org.openstreetmap.josm.data.coor.LatLon')
const BoundingBoxDownloader = Java.type('org.openstreetmap.josm.io.BoundingBoxDownloader')
const CredentialsManager = Java.type('org.openstreetmap.josm.io.auth.CredentialsManager')
const RequestorType = Java.type('java.net.Authenticator.RequestorType')
const PasswordAuthentication = Java.type('java.net.PasswordAuthentication')
import * as util from 'josm/util'
/**
* Specification of position as lat/lon-pair.
*
* @typedef LatLonSpec
* @property {number} lat the latitude
* @property {number} lon the longitude
*/
/**
* Creates a {@class org.openstreetmap.josm.data.coor.LatLon} from a
* javascript object.
*
* @example
* import { buildLatLon } from 'josm/api'
* const pos = buildLatLon({lat: 1, lon: 2});
*
* @param {module:josm/api~LatLonSpec} obj a specification of the position
* @static
* @returns {org.openstreetmap.josm.data.coor.LatLon}
* @summary Create a {@class org.openstreetmap.josm.data.coor.LatLon}
* from a javascript object.
*/
export function buildLatLon(obj) {
util.assert(util.isSomething(obj), 'obj: must not be null or undefined');
util.assert(typeof obj === 'object',
'obj: expected an object, got {0}', obj);
util.assert(util.isNumber(obj.lat),
'obj.lat: expected a number, got {0}', obj.lat);
util.assert(util.isNumber(obj.lon),
'obj.lon: expected a number, got {0}', obj.lon);
util.assert(LatLon.isValidLat(obj.lat),
'obj.lat: expected a valid lat in the range [-90,90], got {0}',
obj.lat);
util.assert(LatLon.isValidLon(obj.lon),
'obj.lon: expected a valid lon in the range [-180,180], got {0}',
obj.lon);
return new LatLon(obj.lat, obj.lon);
}
/**
* Specification of a bounds as JavaScript object.
*
* @typedef BoundsSpec1
* @property {number} minlat
* @property {number} minlon
* @property {number} maxlat
* @property {number} maxlon
* @example
* const bounds = {
* minlat: 46.9479186, minlon: 7.4619484,
* maxlat: 46.9497642, maxlon: 7.4660683
* }
*/
/**
* Specification of a bounds as JavaScript object.
*
* @typedef BoundsSpec2
* @property {module:josm/api~LatLonSpec} min the upper left point
* @property {module:josm/api~LatLonSpec} max the lower right point
* @example
* const bounds = {
* min: {lat: 46.9479186, lon: 7.4619484},
* max: {lat: 46.9497642, lon: 7.4660683}
* }
*/
/**
* Creates a {@class org.openstreetmap.josm.data.Bounds} instance from a javascript object.
*
* @example
* import { buildBounds } from 'josm/api'
* const bounds1 = buildBounds({
* minlat: 46.9479186, minlon: 7.4619484,
* maxlat: 46.9497642, maxlon: 7.4660683
* })
*
* const bounds2 = buildBounds({
* min: {lat: 46.9479186, lon: 7.4619484},
* max: {lat: 46.9497642, lon: 7.4660683}
* })
*
* @param {BoundsSpec1|BoundsSpec2} obj a javascript object
* @returns {org.openstreetmap.josm.data.Bounds} the bounds
* @static
*/
export function buildBounds(obj) {
util.assert(util.isSomething(obj), 'obj: must not be null or undefined')
util.assert(typeof obj === 'object',
'obj: expected an object, got {0}', obj)
function normalizeLat(obj,name) {
util.assert(util.isDef(obj[name]),
'{0}: missing mandatory property', name)
util.assert(util.isNumber(obj[name]),
'{0}: expected a number, got {1}', name, obj[name])
util.assert(LatLon.isValidLat(obj[name]),
'{0}: expected a valid lat, got {1}', name, obj[name])
return obj[name]
}
function normalizeLon(obj,name) {
util.assert(util.isDef(obj[name]),
'{0}: missing mandatory property', name)
util.assert(util.isNumber(obj[name]),
'{0}: expected a number, got {1}', name, obj[name])
util.assert(LatLon.isValidLon(obj[name]),
'{0}: expected a valid lon, got {1}', name, obj[name])
return obj[name]
}
if (util.isDef(obj.minlat)) {
const minlat = normalizeLat(obj, "minlat")
const minlon = normalizeLon(obj, "minlon")
const maxlat = normalizeLat(obj, "maxlat")
const maxlon = normalizeLon(obj, "maxlon")
return new Bounds(minlat, minlon, maxlat, maxlon)
} else if (util.isDef(obj.min)) {
const min = buildLatLon(obj.min)
const max = buildLatLon(obj.max)
return new Bounds(min,max)
} else {
util.assert(false,
'obj: expected an object {min:.., max:..} or '
+ '{minlat:, maxlat:, minlon:, maxlon:}, got {0}', obj)
}
}
/**
* Provides methods to open, close, get, update, etc. changesets on the OSM
* API server.
*
* <strong>Note:</strong> this class doesn't provide a constructor. Methods
* and properties are <code>static</code>.
*
* @example
* // load the changeset api
* import { ChangesetApi } from 'josm/api'
*
* // create a new changeset on the server
* const cs = ChangesetApi.open()
*
* @summary Provides methods to open, close, get, update, etc. changesets on the OSM
* API server.
*
*/
export class ChangesetApi {
/**
* Creates and opens a changeset
*
* @example
* import { ChangesetApi } from 'josm/api'
* const Changeset = Java.type('org.openstreetmap.josm.data.osm.Changeset')
*
* // open a new changeset with no tags
* const cs1 = ChangesetApi.open()
*
* // open a new changeset with the tags given by the supplied changeset
* const cs2 = new Changeset()
* cs2.put('comment', 'a test comment')
* cs2 = ChangesetApi.open(cs2)
*
* // open a new changeset with the tags given by the object
* const cs3 = ChangesetApi.open({comment: 'a test comment'})
*
* @returns {org.openstreetmap.josm.data.osm.Changeset} the changeset
* @param {org.openstreetmap.josm.data.osm.Changeset | object} [changeset] the changeset to open
*/
static open() {
let cs
switch (arguments.length) {
case 0:
cs = new Changeset()
break
case 1:
let o = arguments[0]
if (o instanceof Changeset) {
cs = o
} else if (typeof o === 'object') {
cs = new Changeset()
for (let p in o) {
if (!util.hasProp(o, p)) continue
let key = p
let value = o[p]
key = util.trim(key)
value = value + '' // convert to string
cs.put(key, value)
}
} else {
util.assert(false,
'Unexpected type of argument, expected Changeset or object, ' +
'got {0}', o)
}
break
default:
util.assert(false, 'Unexpected number of arguments, got {0}',
arguments.length)
}
const api = OsmApi.getOsmApi()
api.openChangeset(cs, NullProgressMonitor.INSTANCE)
return cs
}
/**
* Closes a changeset
*
* @example
* import { ChangesetApi } from 'josm/api'
* import * as util from 'josm/util'
* const Changeset = Java.type('org.openstreetmap.josm.data.osm.Changeset')
*
* // closs the changeset 12345
* ChangesetApi.close(12345)
*
* // open a new changeset with the tags given by the supplied changeset
* const cs2 = new Changeset(12345)
* cs2 = ChangesetApi.close(cs2)
* util.assert(cs2.closed) // the changeset is now closed
*
* @param {number | org.openstreetmap.josm.data.osm.Changeset} changeset the changeset to close
* @returns {org.openstreetmap.josm.data.osm.Changeset} the changeset
*/
static close() {
let cs
switch (arguments.length) {
case 0:
util.assert(false,
'Missing arguments. Expected a changeset it or a changeset')
break
case 1: {
const o = arguments[0]
if (o instanceof Changeset) {
cs = o
} else if (util.isNumber(o)) {
util.assert(o > 0, 'Expected a positive changeset id, got {0}', o)
cs = new Changeset(o)
} else {
util.assert(false,
'Unexpected type of argument, expected Changeset or number, ' +
'got {0}', o)
}
break
}
default:
util.assert(false, 'Unexpected number of arguments, got {0}',
arguments.length)
}
const api = OsmApi.getOsmApi()
api.closeChangeset(cs, NullProgressMonitor.INSTANCE)
return cs
}
/**
* Updates a changeset
*
* @example
* import { ChangesetApi } from 'josm/api'
* const Changeset = Java.type('org.openstreetmap.josm.data.osm.Changeset')
*
* // update the comment of a changeset
* const cs2 = new Changeset(12345)
* cs2.put('comment', 'an updated comment')
* cs2 = ChangesetApi.update(cs2)
*
* @param {org.openstreetmap.josm.data.osm.Changeset} changeset the changeset to update
* @returns {org.openstreetmap.josm.data.osm.Changeset} the changeset
*/
static update() {
let cs
switch (arguments.length) {
case 0:
util.assert(false, 'Missing arguments. Expected a changeset')
break
case 1: {
const o = arguments[0]
if (o instanceof Changeset) {
cs = o
} else {
util.assert(false,
'Unexpected type of argument, expected Changeset, got {0}', o)
}
break
}
default:
util.assert(false, 'Unexpected number of arguments, got {0}',
arguments.length)
}
const api = OsmApi.getOsmApi()
api.updateChangeset(cs, NullProgressMonitor.INSTANCE)
return cs
}
/**
* Get a changeset from the server
*
* @example
* import { ChangesetApi } from 'josm/api'
* const Changeset = Java.type('org.openstreetmap.josm.data.osm.Changeset')
*
* // get the changeset with id 12345
* const cs1 = ChangesetApi.get(12345)
*
* // get the changeset with id 12345
* lets cs2 = new Changeset(12345)
* cs2 = ChangesetApi.get(cs2)
*
* @param {number|org.openstreetmap.josm.data.osm.Changeset} changeset the changeset to get
* @returns {org.openstreetmap.josm.data.osm.Changeset} the changeset
*/
static get() {
let cs
switch (arguments.length) {
case 0:
util.assert(false,
'Missing arguments. Expected a changeset id or a changeset')
break
case 1: {
const o = arguments[0]
if (o instanceof Changeset) {
cs = o
} else if (util.isNumber(o)) {
util.assert(o > 0, 'Expected a positive changeset id, got {0}', o)
cs = new Changeset(o)
} else {
util.assert(false,
'Unexpected type of argument, expected Changeset or number, ' +
'got {0}', o)
}
break
}
default:
util.assert(false, 'Unexpected number of arguments, got {0}',
arguments.length)
}
const reader = new OsmServerChangesetReader()
cs = reader.readChangeset(cs.id, NullProgressMonitor.INSTANCE)
return cs
}
}
/**
* Collection of static methods to download objects from and upload objects
* to the OSM server.
*
* <strong>Note:</strong> this class doesn't provide a constructor.
* Methods and properties are 'static'.
*
* @example
* // load the api
* import { Api } from 'josm/api'
*
* // download node 12345
* const ds = Api.downloadObject(12345, 'node')
*
* @summary Collection of static methods to download objects from and upload objects
* to the OSM server
*/
export class Api {
static #normalizeType (type) {
util.assert(util.isSomething(type), 'type must not be null or undefined')
if (util.isString(type)) {
try {
type = OsmPrimitiveType.fromApiTypeName(type)
} catch (e) {
util.assert(false, 'Invalid primitive type, got \'\'{0}\'\'', type)
}
} else if (type instanceof OsmPrimitiveType) {
if (![OsmPrimitiveType.NODE, OsmPrimitiveType.WAY, OsmPrimitiveType.RELATION].includes(type)) {
util.assert(false, 'Invalid primitive type, got {0}', type)
}
} else {
util.assert(false, 'Invalid primitive type, got {0}', type)
}
return type
}
static #normalizeId (id) {
util.assert(util.isSomething(id), 'id must not be null or nothing')
util.assert(util.isNumber(id), 'Expected a number as id, got {0}', id)
util.assert(id > 0, 'Expected a positive number as id, got {0}', id)
return id
}
static #primitiveIdFromObject(o) {
util.assert(util.hasProp(o, 'id'),
'Mandatory property \'\'id\'\' is missing in object {0}', o)
util.assert(util.hasProp(o, 'type'),
'Mandatory property \'\'type\'\' is missing in object {0}', o)
return new SimplePrimitiveId(Api.#normalizeId(o.id), Api.#normalizeType(o.type))
}
static #downloadObject1() {
let id
const o = arguments[0]
util.assert(util.isSomething(o),
'Argument 0: must not be null or undefined')
if (o instanceof PrimitiveId) {
id = o
} else if (typeof o === 'object') {
id = Api.#primitiveIdFromObject(o)
} else {
util.assert(false, 'Argument 0: unexpected type, got {0}', o)
}
var reader = new OsmServerObjectReader(id, false)
var ds = reader.parseOsm(null /* null progress monitor */)
return ds
}
static #optionFull(options) {
if (!util.hasProp(options, 'full')) return undefined
var o = options.full
if (typeof o === 'boolean') return o
util.assert('Expected a boolean value for option \'\'full\'\', got {0}', o)
}
static #optionVersion(options) {
if (!util.hasProp(options, 'version')) return undefined
var o = options.version
util.assert(util.isNumber(o),
'Expected a number for option \'\'version\'\', got {0}', o)
util.assert(o > 0,
'Expected a number > 0 for option \'\'version\'\', got {0}', o)
return o
}
static #downloadObject2 () {
function parseOptions (arg) {
const options = { full: undefined, version: undefined }
if (!(typeof arg === 'object')) {
return options
}
options.full = Api.#optionFull(arg)
options.version = Api.#optionVersion(arg)
return options
}
let id
let options = { full: undefined, version: undefined }
if (util.isNumber(arguments[0])) {
id = Api.#normalizeId(arguments[0])
const type = Api.#normalizeType(arguments[1])
id = new SimplePrimitiveId(id, type)
} else if (arguments[0] instanceof PrimitiveId) {
id = arguments[0]
options = parseOptions(arguments[1])
} else if (typeof arguments[0] === 'object') {
id = Api.#primitiveIdFromObject(arguments[0])
options = parseOptions(arguments[1])
} else {
util.assert(false, 'Unsupported types of arguments')
}
let reader
if (util.isDef(options.version)) {
reader = new OsmServerObjectReader(id, options.version)
} else {
reader = new OsmServerObjectReader(id, !!options.full)
}
const ds = reader.parseOsm(null /* null progress monitor */)
return ds
}
static #downloadObject3 () {
const options = { full: undefined, version: undefined }
let n = Api.#normalizeId(arguments[0])
let type = Api.#normalizeType(arguments[1])
let id = new SimplePrimitiveId(n, type)
util.assert(typeof arguments[2] === 'object',
'Expected an object with named parameters, got {0}', arguments[2])
options.full = Api.#optionFull(arguments[2])
options.version = Api.#optionVersion(arguments[2])
let reader
if (util.isDef(options.version)) {
reader = new OsmServerObjectReader(id, options.version)
} else {
reader = new OsmServerObjectReader(id, !!options.full)
}
var ds = reader.parseOsm(null /* null progress monitor */)
return ds
}
/**
* Options for the method downloadObject()
*
* @typedef DownloadObjectOptions
* @property {boolean} [full=false] if <code>true</code>, the object and its immediate children are
* downloaded, i.e. the nodes of a way and the relation members of a relation. Default:
* <code>false</code>.
* @property {number} [version] if present, the specified version of the object is downloaded.
* If missing, the current version is downloaded. If present, the
* option <code>full</code> is ignored.
*/
/**
* Downloads an object from the server.
*
* There are multiple options to specify what object to download.
* In addition, the function accepts a set of optional named parameters
* as last argument.
*
* <dl>
* <dt><code class='signature'>downloadObject(id, type, ?options)</code></dt>
* <dd class="param-desc"><code>id</code> is the global numeric id.
* <code>type</code> is either one of the strings 'node', 'way',
* or 'relation', or one of the enumeration OsmPrimitiveType.NODE,
* OsmPrimitiveType.WAY, or OsmPrimitiveType.RELATION
* </dd>
*
* <dt><code class='signature'>downloadObject(id, ?options)</code></dt>
* <dd class="param-desc"><code>id</code> is a <code>PrimitiveId</code> or an object
* with the (mandatory) properties <code>id</code> and <code>type</code>,
* i.e. an object <code>{id: ..., type: ...}</code>.
* <code>id</code> is again a number, <code>type</code> is again either one
* of the strings 'node', 'way', or 'relation', or one of the
* enumeration OsmPrimitiveType.NODE, OsmPrimitiveType.WAY,
* or OsmPrimitiveType.RELATION.
* </dd>
* </dl>
*
* @example
* import { Api } from 'josm/api'
* const SimplePrimitiveId = Java.type('org.openstreetmap.josm.data.osm.SimplePrimitiveId')
* const OsmPrimitiveType = Java.type('org.openstreetmap.josm.data.osm.OsmPrimitiveType')
*
* // download the node with id 12345
* const ds1 = Api.downloadObject(12345, 'node')
*
* // download the node with id 12345
* const ds2 = Api.downloadObject({id: 12345, type: 'node'})
*
* // download the full relation (including its members) with id 12345
* const id = new SimplePrimitiveId(12345, OsmPrimitiveType.RELATION)
* const ds3 = Api.downloadObject(id, {full: true})
*
* // download version 5 of the full way 12345 (including its nodes)
* const ds4 = Api.downloadObject(12345, OsmPrimitiveType.WAY, {full: true, version: 5})
*
* @returns {org.openstreetmap.josm.data.osm.DataSet} the downloaded primitives
* @param {number|org.openstreetmap.josm.data.osm.PrimitiveId} id the id of the object
* @param {string|org.openstreetmap.josm.data.osm.OsmPrimitiveType} [type] the type of the object
* @param {module:josm/api~DownloadObjectOptions} [options] named options
* @static
*/
static downloadObject() {
switch (arguments.length) {
case 0:
util.assert(false, 'Unexpected number of arguments, got {0}',
arguments.length)
break
case 1:
return Api.#downloadObject1(...arguments)
case 2:
return Api.#downloadObject2(...arguments)
case 3:
return Api.#downloadObject3(...arguments)
default:
util.assert(false, 'Unexpected number of arguments, got {0}',
arguments.length)
}
}
static #downloadReferrer1 () {
let id
const o = arguments[0]
util.assert(util.isSomething(o),
'Argument 0: must not be null or undefined')
if (o instanceof PrimitiveId) {
id = o
} else if (typeof o === 'object') {
id = Api.#primitiveIdFromObject(o)
} else {
util.assert(false, 'Argument 0: unexpected type, got {0}', o)
}
const reader = new OsmServerBackreferenceReader(id.getUniqueId(),id.getType())
const ds = reader.parseOsm(NullProgressMonitor.INSTANCE)
return ds
}
static #downloadReferrer2 () {
let id
let type
const options = { full: undefined }
if (util.isNumber(arguments[0])) {
id = Api.#normalizeId(arguments[0])
type = Api.#normalizeType(arguments[1])
id = new SimplePrimitiveId(id, type)
} else if (arguments[0] instanceof PrimitiveId) {
id = arguments[0]
const o = arguments[1]
if (util.isSomething(o)) {
util.assert(typeof o === 'object',
'Expected an object with named parameters, got {0}', o)
options.full = Api.#optionFull(o)
}
} else if (typeof arguments[0] === 'object') {
id = Api.#primitiveIdFromObject(arguments[0])
const o = arguments[1]
if (util.isSomething(o)) {
util.assert(typeof o === 'object',
'Expected an object with named parameters, got {0}', o)
options.full = Api.#optionFull(o)
}
} else {
util.assert(false, 'Unsupported types of arguments')
}
const reader = new OsmServerBackreferenceReader(id.getUniqueId(),
id.getType())
if (options.full) {
reader.setReadFull(true)
}
const ds = reader.parseOsm(NullProgressMonitor.INSTANCE)
return ds
}
static #downloadReferrer3 () {
const options = { full: undefined }
const n = Api.#normalizeId(arguments[0])
const type = Api.#normalizeType(arguments[1])
const id = new SimplePrimitiveId(n, type)
util.assert(typeof arguments[2] === 'object',
'Expected an object with named parameters, got {0}', arguments[2])
options.full = Api.#optionFull(arguments[2])
const reader = new OsmServerBackreferenceReader(id.getUniqueId(),
id.getType())
if (options.full) {
reader.setReadFull(true)
}
const ds = reader.parseOsm(NullProgressMonitor.INSTANCE)
return ds
}
/**
* Options for the method donwloadReferrers()
*
* @typedef DownloadReferrerOptions
* @property {boolean} [full=false] If <code>true</code>, the <strong>full</strong> objects are
* retrieved using multi-gets. If missing or <code>false</code>,
* only proxy objects are downloaded. Default: false
*/
/**
* Downloads the objects <em>referring</em> to another object from
* the server.
*
* Downloads primitives from the OSM server which
* refer to a specific primitive. Given a node, the referring ways and
* relations are downloaded. Given a way or a relation, only referring
* relations are downloaded.
*
* The default behaviour is to reply proxy objects only.
*
* If you set the option <code>{full: true}</code>, every referring object
* is downloaded in full.
*
* There are multiple options to specify what referrers to download.
* In addition, the function accepts a set of optional named parameters as
* last argument.
*
* <dl>
* <dt><code class='signature'>downloadReferrer(id, type, ?options)
* </code></dt>
* <dd class="param-desc"><code>id</code> is the global numeric id.
* <code>type</code> is either one of the strings 'node', 'way', or
* 'relation', or one of the enumeration
* {@class org.openstreetmap.josm.data.osm.OsmPrimitiveType}.NODE,
* {@class org.openstreetmap.josm.data.osm.OsmPrimitiveType}.WAY,
* or {@class org.openstreetmap.josm.data.osm.OsmPrimitiveType}.RELATION.
* </dd>
*
* <dt><code class='signature'>downloadReferrer(id, ?options)</code></dt>
* <dd class="param-desc"><code>id</code> is a <code>PrimitiveId</code> or an object
* with the (mandatory) properties <code>id</code> and <code>type</code>,
* i.e. an object <code>{id: ..., type: ...}</code>.
* <code>id</code> is again a number, <code>type</code> is again either one
* of the strings 'node', 'way', or 'relation', or one of the
* enumeration
* {@class org.openstreetmap.josm.data.osm.OsmPrimitiveType}.NODE,
* {@class org.openstreetmap.josm.data.osm.OsmPrimitiveType}.WAY,
* or {@class org.openstreetmap.josm.data.osm.OsmPrimitiveType}.RELATION.
* </dd>
* </dl>
*
* @example
* import { Api } from 'josm/api'
* import { NodeBuilder } from 'josm/builder'
* const SimplePrimitiveId = Java.type('org.openstreetmap.josm.data.osm.SimplePrimitiveId')
* const OsmPrimitiveType = Java.type('org.openstreetmap.josm.data.osm.OsmPrimitiveType')
*
* // download the objects referring to the node with id 12345
* const ds1 = Api.downloadReferrer(12345, 'node')
*
* // download the objects referring to the node with id 12345
* const ds2 = Api.downloadReferrer({id: 12345, type: 'node'})
*
* // download the relations referring to the relation with id 12345.
* // Referring relations are downloaded in full.
* const id = new SimplePrimitiveId(12345, OsmPrimitiveType.RELATION)
* const ds3 = Api.downloadReferrer(id, { full: true })
*
* // create the global node 12345 ...
* const node = NodeBuilder.create(12345)
* // ... and downloads its referrers in full
* const ds = Api.downloadReferrer(node, { full: true })
*
* @returns {org.openstreetmap.josm.data.osm.DataSet} the downloaded primitives
* @param {number|org.openstreetmap.josm.data.osm.PrimitiveId} id the id of the object
* @param {string|org.openstreetmap.josm.data.osm.OsmPrimitiveType} [type] the type of the object
* @param {module:josm/api~DownloadReferrerOptions} [options] named options
*/
static downloadReferrer() {
switch (arguments.length) {
case 0:
util.assert(false, 'Unexpected number of arguments, got {0}',
arguments.length)
break
case 1:
return Api.#downloadReferrer1(...arguments)
case 2:
return Api.#downloadReferrer2(...arguments)
case 3:
return Api.#downloadReferrer3(...arguments)
default:
util.assert(false, 'Unexpected number of arguments, got {0}',
arguments.length)
}
}
/**
* Downloads the objects within a bounding box.
*
* @example
* import { Api } from 'josm/api'
* const Bounds = Java.type('org.openstreetmap.josm.data.Bounds')
* const LatLon = Java.type('org.openstreetmap.josm.data.coor.LatLon')
* const ds1 = Api.downloadArea(new Bounds(
* new LatLon(46.9479186,7.4619484), // min
* new LatLon(46.9497642, 7.4660683) // max
* ))
*
* const ds2 = Api.downloadArea({
* min: {lat: 46.9479186, lon: 7.4619484},
* max: {lat: 46.9497642, lon: 7.4660683}
* })
*
* @returns {org.openstreetmap.josm.data.osm.DataSet} the downloaded primitives
* @param {org.openstreetmap.josm.data.Bounds|module:josm/api~BoundsSpec1|module:josm/api~BoundsSpec2} bounds the bounding box
*/
static downloadArea() {
util.assert(arguments.length === 1, 'Expected 1 argument, got {0}',
arguments.length)
let bounds = arguments[0]
util.assert(util.isSomething(bounds),'bounds: must not be null or undefined')
if (bounds instanceof Bounds) {
// do nothing
} else if (typeof bounds === 'object') {
bounds = buildBounds(bounds) // convert to bounds
} else {
util.assert(false,
'expected an instance of Bounds or an object, got {0}', bounds)
}
const downloader = new BoundingBoxDownloader(bounds)
return downloader.parseOsm(NullProgressMonitor.INSTANCE)
}
/**
* Options for the method upload()
*
* @typedef UploadOptions
* @property {string|org.openstreetmap.josm.io.UploadStrategy}[strategy] Indicates how the data is uploaded.
* Either one of the strings
* <ul>
* <li>individualobjects</li>
* <li>chunked</li>
* <li>singlerequest</li>
* </ul>
* or one of the enumeration values in
* {@class org.openstreetmap.josm.io.UploadStrategy}.
* Default value: UploadStrategy.DEFAULT_UPLOAD_STRATEGY
* @property {number| org.openstreetmap.josm.data.osm.Changeset} [changeset] The changeset to which the data is uploaded.
* Default: creates a new changeset
*
* @property {number} [chunkSize] the size of an upload chunk if the data is uploaded with the
* upload strategy {@class org.openstreetmap.josm.io.UploadStrategy}.CHUNKED_DATASET_STRATEGY.
*
* @property {boolean} [closeChangeset=true] if true, closes the changeset after the upload
*/
/**
* Uploads objects to the server.
*
* You can submit data either as
* {@class org.openstreetmap.josm.data.osm.DataSet},
* {@class org.openstreetmap.josm.data.APIDataSet}, javascript array of
* {@class org.openstreetmap.josm.data.osm.OsmPrimitive}s or
* a {@class java.util.Collection} of
* {@class org.openstreetmap.josm.data.osm.OsmPrimitive}s.
*
* This method supports the same upload strategy as the JOSM upload dialog.
* Supply the named parameter <code>{strategy: ...}</code> to choose the
* strategy.
*
* <p class='documentation-warning'>
* Be careful when uploading data to the OSM server! Do not upload copyright-
* protected or test data.
* </p>
*
*
* The method takes care to update the primitives in the uploaded data when
* the upload succeeds. For instance, uploaded new primitives become global
* objects and get assigned their new id and version, successfully deleted
* objects become invisible, etc.
*
* Even if the entire upload of a dataset fails, a subset therefore may
* have been uploaded successfully. In order to keep track, which pritives
* have been uploaded successfully in case of an error, the method replies a
* collection of the successfully uploaded objects.
*
* @example
* const DataSet = Java.type('org.openstreetmap.josm.data.osm.DataSet')
* import { WayBuilder, NodeBuilder } from 'josm/builder'
* import { Api } from 'josm/api'
* const ds = new DataSet()
* const nb = NodeBuilder.forDataSet(ds)
* WayBuilder
* .forDataSet(ds)
* .withNodes(
* nb.withTags({name: 'node1'}).create(),
* nb.withTags({name: 'node2'}).create()
* )
* .withTags({name: 'way1'})
* .create()
*
* // uploads the data in a new changeset in one chunk
* const processed = Api.upload(ds, 'just testing')
*
* @param {org.openstreetmap.josm.data.osm.DataSet|
* org.openstreetmap.josm.data.APIDataSet|array|java.util.Collection} data the data to upload
* @param {string} comment the upload comment
* @param {module:josm/api~UploadOptions} [options] named options
* @returns {java.util.Collection}
*/
static upload(data, comment, options) {
const UploadStrategy = Java.type('org.openstreetmap.josm.io.UploadStrategy')
const Changeset = Java.type('org.openstreetmap.josm.data.osm.Changeset')
const APIDataSet = Java.type('org.openstreetmap.josm.data.APIDataSet')
const DataSet = Java.type('org.openstreetmap.josm.data.osm.DataSet')
const UploadStrategySpecification =
Java.type('org.openstreetmap.josm.io.UploadStrategySpecification')
const Collection = Java.type('java.util.Collection')
const OsmServerWriter = Java.type('org.openstreetmap.josm.io.OsmServerWriter')
comment = comment || ''
comment = String(comment)
util.assertSomething(data, 'data: must not be null or undefined')
options = options || {}
util.assert(typeof options === 'object',
'options: expected an object with named arguments, got {0}', options)
function normalizeChunkSize (size) {
util.assert(util.isNumber(size),
'chunksize: expected a number, got {0}', size)
util.assert(size >= -1,
'chunksize: expected -1 or a number > 0, got {0}', size)
return size
}
function normalizeChangeset (changeset) {
if (util.isNothing(changeset)) {
return new Changeset()
} else if (util.isNumber(changeset)) {
util.assert(changeset > 0,
'changeset: expected a changeset id > 0, got {0}', changeset)
return new Changeset(changeset)
} else if (changeset instanceof Changeset) {
return changeset
} else {
util.assert(false, 'changeset: unexpected value, got {0}',
changeset)
}
}
function uploadSpecFromOptions (options) {
let strategy = options.strategy || UploadStrategy.DEFAULT_UPLOAD_STRATEGY
if (strategy instanceof String) {
strategy = UploadStrategy.valueOf(strategy)
util.assert(strategy, "invalid upload strategy ''{0}''", strategy)
}
let chunkSize = options.chunkSize ||
UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE
chunkSize = normalizeChunkSize(chunkSize)
let closeChangeset = util.isDef(options.closeChangeset)
? options.closeChangeset
: true
closeChangeset = Boolean(closeChangeset)
const spec = new UploadStrategySpecification()
spec.setStrategy(strategy)
spec.setChunkSize(chunkSize)
spec.setCloseChangesetAfterUpload(closeChangeset)
return spec
}
let apiDataSet
if (data instanceof DataSet) {
apiDataSet = new APIDataSet(data)
} else if (data instanceof APIDataSet) {
apiDataSet = data
} else if (util.isArray(data)) {
apiDataSet = new APIDataSet(data)
} else if (data instanceof Collection) {
apiDataSet = new APIDataSet(data)
} else {
util.assert(false, 'data: unexpected type of value, got {0}', data)
}
if (apiDataSet.isEmpty()) return undefined
apiDataSet.adjustRelationUploadOrder()
const toUpload = apiDataSet.getPrimitives()
let changeset = options.changeset || new Changeset()
changeset = normalizeChangeset(changeset)
changeset.put('comment', comment)
const spec = uploadSpecFromOptions(options)
const writer = new OsmServerWriter()
writer.uploadOsm(spec, toUpload, changeset, null /* progress monitor */)
if (spec.isCloseChangesetAfterUpload()) {
ChangesetApi.close(changeset)
}
return writer.getProcessedPrimitives()
}
}
/* -------------------------------------------------------------------------- */
/* ApiConfig */
/* -------------------------------------------------------------------------- */
/**
* ApiConfig provides methods and properties for configuring API parameters.
*
*
* @class
* @summary ApiConfig provides methods and properties for configuring API parameters
* @name ApiConfig
*/
export const ApiConfig = {}
const DEFAULT_URL = 'https://www.openstreetmap.org/api'
/**
* Get or set the API server URL.
*
* <dl>
* <dt><code class='signature'>get</code></dt>
* <dd class="param-desc">Replies the currently configured server URL or undefinend, if no
* server URL is configured.</dd>
*
* <dt><code class='signature'>set</code></dt>
* <dd class="param-desc">Sets the current server URL. If null or undefined, removes the
* current configuration. Accepts either a string or a {@class java.net.URL}.
* Only accepts http or https URLs.
* </dd>
* </dl>
*
* @example
* import { ApiConfig } from 'josm/api'
* ApiConfig.serverUrl // -> the current server url
*
* // set a new API url
* ApiConfig.serverUrl = 'http://api06.dev.openstreetmap.org'
*
* @static
* @summary Get or set the API server URL.
* @property {string} serverUrl
* @name serverUrl
* @memberof module:josm/api~ApiConfig
*/
Object.defineProperty(ApiConfig, 'serverUrl', {
enumerable: true,
get: function () {
var url = Preferences.main().get('osm-server.url', null)
if (url == null) url = DEFAULT_URL
return url == null ? undefined : util.trim(url)
},
set: function (value) {
if (util.isNothing(value)) {
Preferences.main().put('osm-server.url', null)
} else if (value instanceof URL) {
util.assert(
['http', 'https'].includes(value.getProtocol()),
'url: expected a http or https URL, got {0}',
value)
Preferences.main().put('osm-server.url', value.toString())
} else if (util.isString(value)) {
value = util.trim(value)
try {
const url = new URL(value)
util.assert(
['http', 'https'].includes(url.getProtocol()),
'url: expected a http or https URL, got {0}',
url.toString())
Preferences.main().put('osm-server.url', url.toString())
} catch (e) {
util.assert(false,
'url: doesn\'\'t look like a valid URL, got {0}. Error: {1}',
value, e)
}
} else {
util.assert(false, 'Unexpected type of value, got {0}', value)
}
}
})
/**
* Get the default server URL.
*
* @example
* import { ApiConfig } from 'josm/api'
* ApiConfig.defaultServerUrl // -> the default server url
*
* @static
* @summary Get the default server URL
* @name defaultServerUrl
* @property {string} defaultServerUrl the default server URL
* @readOnly
* @memberof module:josm/api~ApiConfig
*/
Object.defineProperty(ApiConfig, 'defaultServerUrl', {
value: DEFAULT_URL,
writable: false,
enumerable: true
})
function normalizeAuthMethod (authMethod) {
util.assert(util.isString(authMethod),
'authMethod: expected a string, got {0}',
authMethod)
authMethod = util.trim(authMethod).toLowerCase()
util.assert(authMethod === 'basic' || authMethod === 'oauth',
'Unsupported value for authMethod, got {0}', authMethod)
return authMethod
}
/**
* Get or set the authentication method.
*
* JOSM uses two authentication methods:
* <dl>
* <dt><code class='signature'>basic</code></dt>
* <dd class="param-desc">Basic authentication with a username and a password</dd>
* <dt><code class='signature'>oauth</code></dt>
* <dd class="param-desc">Authentication with the <a href='http://oauth.net/'>OAuth</a>
* protocol.</dd>
* </dl>
*
* @example
* import { ApiConfig } from 'josm/api'
* ApiConfig.authMethod // -> the current authentication method
*
* // set OAuth as authentication method
* ApiConfig.authMethod = 'oauth'
*
* @static
* @summary Get or set the authentication method.
* @type string
* @name authMethod
* @property {string} authMethod the authentication method
* @memberof module:josm/api~ApiConfig
*/
Object.defineProperty(ApiConfig, 'authMethod', {
enumerate: true,
get: function () {
let authMethod = Preferences.main().get('osm-server.auth-method', 'basic')
authMethod = util.trim(authMethod).toLowerCase()
if (authMethod === 'basic' || authMethod === 'oauth') return authMethod
// unsupported value for authMethod in the preferences. Returning
// 'basic' as default.
return 'basic'
},
set: function (value) {
value = normalizeAuthMethod(value)
Preferences.main().put('osm-server.auth-method', value)
}
})
/**
* Options for the method setCredentials
*
* @typedef SetOrGetCredentialOptions
* @param {string} [host] the host name of the API server for which credentials are set.
* If missing, the host name of the currently configured OSM API server
* is used.
*/
/**
* Basic credentials replied by getCredentials
*
* @typedef BasicCredentials
* @property {string} host the host name
* @property {string} user the user name
* @property {string} password the password
*/
/**
* OAuth credentials replied by getCredentials
*
* @typedef OAuthCredentials
* @property {string} key the OAuth key
* @property {string} secret the OAuth secret
*/
/**
* Gets the credentials, i.e. username and password for the basic
* authentication method.
*
* @example
* import { ApiConfig } from 'josm/api'
*
* // get username/password for the current OSM API server
* const credentials = ApiConfig.getCredentials('basic')
*
* @param {string} authMethod the authentication method. Either <code>basic</code> or <code>oauth</code>
* @param {module:josm/api~SetOrGetCredentialOptions} [options] additional options
* @static
* @returns {BasicCredentials | OAuthCredentials} the credentials
* @memberof module:josm/api~ApiConfig
*/
function getCredentials(authMethod, options) {
options = options || {}
util.assert(typeof options === 'object',
'options: expected an object with named options, got {0}', options)
// a hack to convert a Java 'char[]' into a JavaScript string
function charArrayToString(chars) {
let result = ""
for (let i=0; i < chars.length; i++) {
const c = chars[i].toString()
result += c
}
return result
}
function getBasicCredentials () {
const cm = CredentialsManager.getInstance()
if (options.host) options.host = util.trim(String(options.host))
const host = options.host ? options.host : OsmApi.getOsmApi().getHost()
const pa = cm.lookup(RequestorType.SERVER, host)
if (pa) {
return {
host: host,
user: pa.getUserName(),
password: charArrayToString(pa.getPassword())
}
} else {
return {
host: host,
user: undefined,
password: undefined
}
}
}
function getOAuthCredentials () {
const cm = CredentialsManager.getInstance()
const token = cm.lookupOAuthAccessToken()
if (token == null) return undefined
return { key: token.getKey(), secret: token.getSecret() }
}
authMethod = normalizeAuthMethod(authMethod)
if (authMethod === 'basic') return getBasicCredentials()
if (authMethod === 'oauth') return getOAuthCredentials()
util.assert(false, 'Unsupported authentication method, got {0}',
authMethod)
}
ApiConfig.getCredentials = getCredentials
function normalizeBasicCredentials (credentials) {
if (util.isNothing(credentials)) return null
util.assert(credentials instanceof PasswordAuthentication || typeof credentials === 'object',
'basic credentials: expected an object or an instance of ' +
'PasswordAuthentication , got {0}', credentials)
if (credentials instanceof PasswordAuthentication) {
return credentials
} else {
const user = String(credentials.user || '')
let password = credentials.password || null
if (password) {
// convert to char array
password = [...password]
}
return new PasswordAuthentication(user, password)
}
}
function normalizeOAuthCredentials (credentials) {
const OAuthToken = Java.type('org.openstreetmap.josm.data.oauth.OAuthToken')
if (util.isNothing(credentials)) return null
util.assert(credentials instanceof OAuthToken || typeof credentials === 'object',
'oauth credentials: expected an object or an instance of OAuthToken, ' +
'got {0}', credentials)
if (credentials instanceof OAuthToken) {
return credentials
} else {
const key = String(credentials.key || '')
const secret = String(credentials.secret || '')
return new OAuthToken(key, secret)
}
}
/**
* Userid and password for basic authentication.
*
* @typdef BasicAuthParameters
* @param {string} user the user id
* @param {string} password the password
*/
/**
* Parameters for OAuth authentication
*
* @typdef OAuthParameters
* @param {string} key the key
* @param {string} secret the secret
*/
/**
* Set the credentials, i.e. username and password for the basic
* authentication method.
*
* Basic authentication credentials are either an instance of
* {@class java.net.PasswordAuthentication} or
* an object <code>{user: string, password: string}</code>.
*
* OAuth authentication credentials are either an instance of
* {@class org.openstreetmap.josm.data.oauth.OAuthToken} or
* an object <code>{key: string, secret: string}</code>.
*
* @example
* import { ApiConfig } from 'josm/api'
*
* // set the credentials
* ApiConfig.setCredentials('basic', { user:'test', password:'my-password' })
*
* @param {string} authMethod the authentication method. Either 'basic' or 'oauth'.
* @param {(
* module:josm/api~BasicAuthParameters
* | module:josm/api~OAuthParameters
* | org.openstreetmap.josm.data.oauth.OAuthToken
* | java.net.PasswordAuthentication)} credentials the credentials
* @param {module:josm/api~SetOrGetCredentialOptions} [options] additional options
* @static
* @returns {object} the credentials
* @memberof module:josm/api~ApiConfig
*/
function setCredentials (authMethod, credentials, options) {
options = options || {}
util.assert(typeof options === 'object',
'options: expected an object with named options, got {0}', options)
authMethod = normalizeAuthMethod(authMethod)
if (authMethod === 'basic') {
credentials = normalizeBasicCredentials(credentials)
util.assert(credentials != null,
'credentials: can\'\'t store null credentials')
let host = options.host ? String(options.host) : null
host = host || OsmApi.getOsmApi().getHost()
const cm = CredentialsManager.getInstance()
cm.store(RequestorType.SERVER, host, credentials)
} else if (authMethod === 'oauth') {
credentials = normalizeOAuthCredentials(credentials)
util.assert(credentials != null,
'credentials: can\'\'t store null credentials')
const cm = CredentialsManager.getInstance()
cm.storeOAuthAccessToken(credentials)
} else {
util.assert(false, 'Unsupported authentication method, got {0}',
authMethod)
}
}
ApiConfig.setCredentials = setCredentials