/** * @module josm/builder/node */ /* global Java */ // -- imports const Node = Java.type('org.openstreetmap.josm.data.osm.Node') const Way = Java.type('org.openstreetmap.josm.data.osm.Way') const Relation = Java.type('org.openstreetmap.josm.data.osm.Relation') const RelationMember = Java.type('org.openstreetmap.josm.data.osm.RelationMember') const DataSet = Java.type('org.openstreetmap.josm.data.osm.DataSet') const OsmPrimitive = Java.type('org.openstreetmap.josm.data.osm.OsmPrimitive') const LatLon = Java.type('org.openstreetmap.josm.data.coor.LatLon') const List = Java.type('java.util.List') import * as util from 'josm/util' import { assertGlobalId, rememberId, rememberTags, assignTags, rememberIdFromObject, rememberVersionFromObject, checkLat, checkLon, rememberPosFromObject, rememberTagsFromObject } from './common' /** * NodeBuilder helps to create OSM nodes. * * Methods of NodeBuilder can be used in a static and in an instance context. * It isn't necessary to create an instance of NodeBuilder, unless it is * configured with a {@class org.openstreetmap.josm.data.osm.DataSet}, * to which created nodes are added. * * @example * import {NodeBuilder} from 'josm/builder' * const DataSet = Java.type('org.openstreetmap.josm.data.osm.DataSet') * * const ds = new DataSet() * // create a node builder without and underlying dataset ... * let nbuilder = new NodeBuilder() * // ... with an underlying dataset .... * nbuilder = new NodeBuilder(ds) * // ... or using this factory method * nbuilder = NodeBuilder.forDataSet(ds) * * // create a new local node at position (0,0) without tags * const n1 = NodeBuilder.create() * * // create a new global node at a specific position with tags * const n2 = NodeBuilder.withPosition(1,1).withTags({name: 'test'}).create(1) * * // create a new proxy for a global node * // (an 'incomplete' node in JOSM terminology) * const n3 = NodeBuilder.createProxy(2) * * @class * @summary NodeBuilder helps to create OSM nodes * @name NodeBuilder * @param {org.openstreetmap.josm.data.osm.DataSet} [ds] the dataset * which created objects are added to */ /** * Creates a new node builder. * * @param {org.openstreetmap.josm.data.osm.DataSet} [ds] the dataset * to which created objects are added * @constructor * @memberOf NodeBuilder */ export function NodeBuilder(ds) { if (util.isSomething(ds)) { util.assert(ds instanceof DataSet, 'Expected a JOSM dataset, got {0}', ds) this.ds = ds } } /** * Creates or configures a NodeBuilder which will add created nodes * to the dataset <code>ds</code>. * * @example * import { NodeBuilder } from 'josm/builder' * * // create a new node builder building to a data set * const DataSet = Java.type('org.openstreetmap.josm.data.osm.DataSet') * const ds = new DataSet() * * // ... using a static method ... * const nb1 = NodeBuilder.forDataSet(ds) * // ... or the instance method * const nb2 = new NodeBuilder.forDataSet(ds) * * @returns {module:josm/builder/node~NodeBuilder} the node builder * @param {org.openstreetmap.josm.data.osm.DataSet} ds the dataset which * created objects are added to * @summary Creates a new NodeBuilder for a specific * {@class org.openstreetmap.josm.data.osm.DataSet}. * @memberof module:josm/builder/node~NodeBuilder * @instance */ function forDataSet (ds) { const builder = receiver(this) util.assert(util.isSomething(ds), 'Expected a non-null defined object, got {0}', ds) util.assert(ds instanceof DataSet, 'Expected a JOSM dataset, got {0}', ds) builder.ds = ds return builder } NodeBuilder.prototype.forDataSet = forDataSet NodeBuilder.forDataSet = forDataSet function receiver (that) { return typeof that === 'object' ? that : new NodeBuilder() } function initFromObject (builder, args) { rememberIdFromObject(builder, args) rememberVersionFromObject(builder, args) rememberPosFromObject(builder, args) rememberTagsFromObject(builder, args) } /** * Named options for {@link module:josm/builder/node~NodeBuilder#create create} * * @typedef NodeBuilderOptions * @property {number} [version=1] the version (> 0) of the node. Default: 1. * @property {number} [lat=0.0] a valide latitude (number in the range [-90,90]. Default: 0.0 * @property {number} [lon=0.0] a valide longitude (number in the range[-180,180]. Default: 0.0 * @property {number[] | {lat: number, lon: number }} [pos=null] a position, either an array with * two numbers or as an object * @property {object} [tags] an object with tags. Null values and undefined * values are ignored. Any other value is converted to a string. * Leading and trailing white space in keys is removed. * @memberOf module:josm/builder/node~NodeBuilder * @example * // options to create a node at position (1.0, 2.0) with some tags * const options = { * lat: 1.0, * lon: 2.0, * tags: { * amenity: 'restaurant' * } * } */ /** * Creates a new {@class org.openstreetmap.josm.data.osm.Node}. * * Can be used in an instance or in a static context. * * @example * import { NodeBuilder } from 'josm/builder' * // create a new local node at position [0,0] * const n1 = NodeBuilder.create() * * // create a new global node with id 1111 at position [0,0] * const n2 = NodeBuilder.create(1111) * * // create a new global node with version 3 at a specific position * // and with some tags * const n3 = NodeBuilder.create(2222, { * version: 3, * lat: 23.45, * lon: 87.23, * tags: {amenity: 'restaurant'} * }) * * @param {number} [id] a global node id. If missing and * not set before using <code>withId(..)</code>, creates a new local id. * @param {module:josm/builder/node~NodeBuilder.NodeBuilderOptions} [options] additional options for creating the node * @returns {org.openstreetmap.josm.data.osm.Node} the created node * @summary Creates a new {@class org.openstreetmap.josm.data.osm.Node} * @memberof module:josm/builder/node~NodeBuilder * @instance */ function create () { const builder = receiver(this) let arg switch (arguments.length) { case 0: break case 1: arg = arguments[0] util.assert(util.isSomething(arg), 'Argument 0: must not be null or undefined') if (util.isNumber(arg)) { util.assert(arg > 0, 'Argument 0: expected an id > 0, got {0}', arg) builder.id = arg } else if (typeof arg === 'object') { initFromObject(builder, arg) } else { util.assert(false, "Argument 0: unexpected type, got ''{0}''", arg) } break case 2: arg = arguments[0] util.assert(util.isSomething(arg), 'Argument 0: must not be null or undefined') util.assert(util.isNumber(arg), 'Argument 0: must be a number') util.assert(arg > 0, 'Expected an id > 0, got {0}', arg) builder.id = arg arg = arguments[1] if (util.isSomething(arg)) { util.assert(typeof arg === 'object', 'Argument 1: must be an object') initFromObject(builder, arg) } break default: util.assert(false, 'Unexpected number of arguments, got {0}', arguments.length) } let node if (util.isNumber(builder.id)) { if (util.isNumber(builder.version)) { node = new Node(builder.id, builder.version) } else { node = new Node(builder.id, 1) } const coor = new LatLon(builder.lat || 0, builder.lon || 0) node.setCoor(coor) } else { node = new Node(new LatLon(builder.lat || 0, builder.lon || 0)) } assignTags(node, builder.tags || {}) if (builder.ds) { if (builder.ds.getPrimitiveById(node) == null) { builder.ds.addPrimitive(node) } else { throw new Error( 'Failed to add primitive, primitive already included ' + 'in dataset. \n' + 'primitive=' + node ) } } return node } NodeBuilder.create = create NodeBuilder.prototype.create = create /** * Creates a new <em>proxy</em> * {@class org.openstreetmap.josm.data.osm.Node}. A proxy node is a node, * for which we only know its global id. In order to know more details * (position, tags, etc.), we would have to download it from the OSM server. * * * The method can be used in a static and in an instance context. * * @example * import { NodeBuilder } from 'josm/builder' * * // a new proxy node for the global node with id 1111 * const n1 = NodeBuilder.createProxy(1111) * * @param {number} id the node id (not null, number > 0 expected) * @return {org.openstreetmap.josm.data.osm.Node} the new proxy node * @memberof module:josm/builder/node~NodeBuilder * @instance */ function createProxy (id) { const builder = receiver(this) util.assert(util.isSomething(id), 'Argument 0: must not be null or undefined') util.assert(util.isNumber(id), 'Argument 0: expected a number, got {0}', id) util.assert(id > 0, 'Argument 0: id > 0 expected, got {0}', id) const node = new Node(id) if (builder.ds) builder.ds.addPrimitive(node) return node } NodeBuilder.prototype.createProxy = NodeBuilder.createProxy = createProxy /** * Declares the node position. * * The method can be used in a static and in an instance context. * * @example * import { NodeBuilder } from 'josm/builder' * * // a new global node with the global id 1111 at position (34,45) * const n1 = NodeBuilder.withPosition(34,45).create(1111) * * // a new local node at position (23.2, 87.33) * const n2 = NodeBuilder.withPosition(23.3,87.33).create() * * @param {Number} lat the latitude. A number in the range [-90..90]. * @param {Number} lon the longitude. A number in the range [-180..180]. * @returns {module:josm/builder/node~NodeBuilder} a node builder (for method chaining) * @memberof module:josm/builder/node~NodeBuilder * @instance */ function withPosition (lat, lon) { const builder = receiver(this) util.assert(util.isNumber(lat), 'Expected a number for lat, got {0}', lat) util.assert(util.isNumber(lon), 'Expected a number for lon, got {0}', lon) util.assert(LatLon.isValidLat(lat), 'Invalid lat, got {0}', lat) util.assert(LatLon.isValidLon(lon), 'Invalid lon, got {0}', lon) builder.lat = lat builder.lon = lon return builder } NodeBuilder.prototype.withPosition = withPosition NodeBuilder.withPosition = withPosition /** * Declares the tags to be assigned to the new node. * * The method can be used in a static and in an instance context. * * @example * import { NodeBuilder } from 'josm/builder' * * // a new global node with the global id 1111 and tags name=test and * // highway=road * const n1 = NodeBuilder.withTags({'name':'test', 'highway':'road'}).create(1111) * * // a new local node with tags name=test and highway=road * const tags = { * 'name' : 'test', * 'highway' : 'road' * } * const n2 = NodeBuilder.withTags(tags).create() * * @param {object} [tags] the tags * @returns {module:josm/builder/node~NodeBuilder} the node builder (for method chaining) * @memberof module:josm/builder/node~NodeBuilder * @instance */ function withTags (tags) { const builder = receiver(this) rememberTags(builder, tags) return builder } NodeBuilder.prototype.withTags = withTags NodeBuilder.withTags = withTags /** * Declares the global node id and the global node version. * * The method can be used in a static and in an instance context. * * @param {number} id the global node id. A number > 0. * @param {number} [version=1] optional the global node version. If present, * a number > 0. * @returns {module:josm/builder/node~NodeBuilder} the node builder (for method chaining) * @memberof module:josm/builder/node~NodeBuilder * @instance */ function withId (id, version) { const builder = receiver(this) rememberId(builder, id, version) return builder } NodeBuilder.prototype.withId = withId NodeBuilder.withId = withId