JOSM is a powerful tool to create maps, although the structure of the map data is quite simple. There are only three basic types of objects or OSM primitives, as they are called in JOSM:
Most of the scripts run by the Scripting Plugin will have to manipulate these primitives in one way or the other. Basically, a scripts manipulates the same java objects representing data primitives, as JOSM does. The public methods and fields of the respective Java classes are available for scripting. In addition, there are JavaScript properties and functions mixed into the Java classes. They don't replace the native fields and methods, but they complete them with properties and and functions which are more "natural" to a script in a JavaScript-environment.
The following table lists the names of the basic Java classes for data primitives and the names of the corresponding JavaScript mixins.
Kind of primitive | Java class | JavaScript mixin | JavaScript builder class |
---|---|---|---|
node | Node (extending OsmPrimitive) |
NodeMixin (extending OsmPrimitiveMixin) |
NodeBuilder |
way | Way (extending OsmPrimitive) |
WayMixin (extending OsmPrimitiveMixin) |
WayBuilder |
relation | Relation (extending OsmPrimitive) |
RelationMixin (extending OsmPrimitiveMixin) |
RelationBuilder |
var out = java.lang.System.out;
// Create a new node at position [12.45, 45.56]
var node = new org.openstreetmap.josm.data.osm.Node(
new org.openstreetmap.josm.data.coor.LatLon(12.45, 45.56)
);
out.println("Created a node - id=" + node.getNumericId());
// Create a new empty relation with global id 12345 and global version 6
var Relation = org.openstreetmap.josm.data.osm.Relation;
var relation = new Relation(12345, 6);
out.println("Created a relation - id=" + relation.getNumericId());
var out = java.lang.System.out;
var builder= require("josm/builder");
var nb = builder.NodeBuilder;
var rb = builder.RelationBuilder;
// Create a new node at position [12.45, 45.56]
var node;
node = nb.withPosition(12.45,45.56).create();
// ... or ...
node = nb.create({lat: 12.45, lon: 45.56});
out.println("Created a node - id=" + node.getNumericId());
// Create a new relation with global id 12345 and global version 6
var relation;
relation = rb.withId(12345,6).create();
// .. or ..
relation = rb.create(12345, {version: 6});
out.println("Created a relation - id=" + relation.getNumericId());
The native JOSM classes provide public setter and getter methods to set and get property values on OSM primitives. You can either invoke them or set and get JavaScript properties provided by the JavaScript mixins. As an example, the following table summarizes the various options for a nodes property position.
Native methods | Mixin properties | |
---|---|---|
set position |
|
|
get position |
|
|
JOSM manages interconnected primitives in datasets, a kind of container for nodes, ways, and relations. A primitive doesn't have to belong to a dataset, but if it does, it can belong to at most one dataset. The dataset is said to be its parent.
The following table summarizes the name of the native Java class and the JavaScript mixin representing data sets.
Java class | JavaScript mixin | |
---|---|---|
data set | DataSet | DataSetMixin |
There are two major differences between detached primitives and those attached to a dataset:
Data changes are notified to listeners listing on change events on the parent data set.
Consequence: even simple property assignements on primitives may result in costly event
propagation and UI refreshing.
Consider to group batches of updates on attached primitives in a batch which
notifies listeners only once about data change events for the entire batch:
var ds = ... // assume ds is an already initialized data set
// runs the updates on two primitives in a "batch"
ds.batch(function() {
ds.node(12345).lat = 12.34;
ds.relation(67890).tags.name = "a new name";
});
JOSM provides an UI to display primitives and manipulate them interactively in data layers. If primitives are modified interactively, the respective changes can be undone and redone.
If you manipulate primitives attached to a dataset which is itself attached to a data layer, you are better off to apply data commands to the primitives, instead of manipulating them directly. For this purpose, the Scripting Plugin provides a command API.
var command = require("josm/command");
var layer = josm.layers.get("my data layer");
var ds = layer.data;
// creates and applies two undoable/redoable commands
layer.apply(
command.change(ds.node(12345), {lat: 12.45}),
command.change(ds.relation(67890), {tags: {name: "a new name"}})
// to remove a tag, set its value to null
command.change(ds.way(87632), {tags: {width: null}})
);
The easiest way to get hold on a primitive in a dataset is to access it by its unique numeric id.
var ds = .... // a dataset
var node = ds.get("node", 12345);
// .. or
node = ds.node(12345);
var way = ds.get("way", 12345);
// ... or
way = ds.way(12345);
var relation = ds.get("relation", -27222); // this is a local id
// ... or
relation = ds.relation(-27222);
In addition, you can search in a dataset using the method query()
.
query()
accepts two types of search expressions:
var ds = .... // a dataset
// query the dataset with a predicate
var restaurants = ds.query(function(p) {
p.tags.amenity == "restaurant";
});
// query the dataset with a JOSM search expression
restaurants = ds.query("amenity=restaurant");