@osmix/core 0.1.10 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/LICENSE +21 -0
- package/README.md +136 -72
- package/dist/contracts.d.ts +27 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +1 -0
- package/dist/contracts.js.map +1 -0
- package/dist/entities.d.ts +22 -2
- package/dist/entities.d.ts.map +1 -1
- package/dist/entities.js +54 -10
- package/dist/entities.js.map +1 -1
- package/dist/ids.d.ts +20 -5
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +87 -29
- package/dist/ids.js.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/indirect-kd-index.d.ts +28 -0
- package/dist/indirect-kd-index.d.ts.map +1 -0
- package/dist/indirect-kd-index.js +163 -0
- package/dist/indirect-kd-index.js.map +1 -0
- package/dist/limits.d.ts +30 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +51 -0
- package/dist/limits.js.map +1 -0
- package/dist/mocks.d.ts.map +1 -1
- package/dist/mocks.js.map +1 -1
- package/dist/nodes.d.ts +36 -15
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +185 -62
- package/dist/nodes.js.map +1 -1
- package/dist/osm.d.ts +60 -4
- package/dist/osm.d.ts.map +1 -1
- package/dist/osm.js +32 -6
- package/dist/osm.js.map +1 -1
- package/dist/relations.d.ts +11 -4
- package/dist/relations.d.ts.map +1 -1
- package/dist/relations.js +73 -29
- package/dist/relations.js.map +1 -1
- package/dist/renumber.d.ts +23 -0
- package/dist/renumber.d.ts.map +1 -0
- package/dist/renumber.js +64 -0
- package/dist/renumber.js.map +1 -0
- package/dist/stringtable.d.ts +2 -1
- package/dist/stringtable.d.ts.map +1 -1
- package/dist/stringtable.js +9 -14
- package/dist/stringtable.js.map +1 -1
- package/dist/tags.d.ts +45 -12
- package/dist/tags.d.ts.map +1 -1
- package/dist/tags.js +153 -27
- package/dist/tags.js.map +1 -1
- package/dist/typed-arrays.d.ts +63 -48
- package/dist/typed-arrays.d.ts.map +1 -1
- package/dist/typed-arrays.js +184 -107
- package/dist/typed-arrays.js.map +1 -1
- package/dist/ways.d.ts +52 -3
- package/dist/ways.d.ts.map +1 -1
- package/dist/ways.js +223 -46
- package/dist/ways.js.map +1 -1
- package/package.json +45 -31
- package/src/contracts.ts +29 -0
- package/src/entities.ts +242 -194
- package/src/ids.ts +278 -235
- package/src/index.ts +22 -13
- package/src/indirect-kd-index.ts +212 -0
- package/src/limits.ts +73 -0
- package/src/mocks.ts +114 -113
- package/src/nodes.ts +538 -397
- package/src/osm.ts +282 -207
- package/src/relations.ts +580 -558
- package/src/renumber.ts +80 -0
- package/src/stringtable.ts +189 -184
- package/src/tags.ts +406 -272
- package/src/typed-arrays.ts +307 -205
- package/src/types/geoflatbush.d.ts +28 -33
- package/src/ways.ts +577 -406
- package/test/entity-index-errors.test.ts +41 -0
- package/test/ids.bench.ts +33 -0
- package/test/ids.test.ts +122 -0
- package/test/limits.test.ts +83 -0
- package/test/memory.test.ts +29 -30
- package/test/no-shared-array-buffer.test.ts +41 -0
- package/test/nodes-spatial-index.test.ts +261 -0
- package/test/osm-bbox.test.ts +30 -0
- package/test/relations.bench.ts +32 -0
- package/test/relations.test.ts +124 -0
- package/test/renumber.test.ts +54 -0
- package/test/spatial-index-rebuild.test.ts +33 -0
- package/test/storage-compaction.test.ts +269 -0
- package/test/tags-search.test.ts +89 -56
- package/test/typed-arrays.test.ts +252 -132
- package/tsconfig.build.json +6 -6
- package/tsconfig.json +7 -7
- package/src/types/geokdbush.d.ts +0 -37
- package/src/types/kdbush.d.ts +0 -47
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A compact static KD index over coordinate columns owned by another object.
|
|
3
|
+
*
|
|
4
|
+
* The selection and traversal algorithms are adapted from KDBush 4.1.0,
|
|
5
|
+
* Copyright (c) 2018 Vladimir Agafonkin, distributed under the ISC License.
|
|
6
|
+
* Unlike KDBush, this index stores only a Uint32 permutation. Coordinates are
|
|
7
|
+
* dereferenced from the existing Int32 microdegree columns.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { BufferType } from "./typed-arrays.ts";
|
|
11
|
+
import { BufferConstructor } from "./typed-arrays.ts";
|
|
12
|
+
|
|
13
|
+
const DEFAULT_NODE_SIZE = 64;
|
|
14
|
+
|
|
15
|
+
export class IndirectKdIndex {
|
|
16
|
+
readonly indexes: Uint32Array;
|
|
17
|
+
private readonly lons: Int32Array;
|
|
18
|
+
private readonly lats: Int32Array;
|
|
19
|
+
private readonly nodeSize: number;
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
lons: Int32Array,
|
|
23
|
+
lats: Int32Array,
|
|
24
|
+
indexes: Uint32Array,
|
|
25
|
+
nodeSize = DEFAULT_NODE_SIZE,
|
|
26
|
+
) {
|
|
27
|
+
this.lons = lons;
|
|
28
|
+
this.lats = lats;
|
|
29
|
+
this.indexes = indexes;
|
|
30
|
+
this.nodeSize = nodeSize;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static build(
|
|
34
|
+
lons: Int32Array,
|
|
35
|
+
lats: Int32Array,
|
|
36
|
+
count: number,
|
|
37
|
+
fill: (indexes: Uint32Array) => void,
|
|
38
|
+
): IndirectKdIndex {
|
|
39
|
+
const buffer = new BufferConstructor(count * Uint32Array.BYTES_PER_ELEMENT);
|
|
40
|
+
const indexes = new Uint32Array(buffer);
|
|
41
|
+
fill(indexes);
|
|
42
|
+
sortKd(indexes, lons, lats, DEFAULT_NODE_SIZE, 0, indexes.length - 1, 0);
|
|
43
|
+
return new IndirectKdIndex(lons, lats, indexes);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static from(lons: Int32Array, lats: Int32Array, buffer: BufferType): IndirectKdIndex {
|
|
47
|
+
if (buffer.byteLength % Uint32Array.BYTES_PER_ELEMENT !== 0) {
|
|
48
|
+
throw new Error("Indirect KD index buffer length must be divisible by four");
|
|
49
|
+
}
|
|
50
|
+
return new IndirectKdIndex(lons, lats, new Uint32Array(buffer));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get buffer(): BufferType {
|
|
54
|
+
return this.indexes.buffer as BufferType;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Find entity indexes within an inclusive bounding box.
|
|
59
|
+
*
|
|
60
|
+
* Bounds are **integer microdegrees** (the units of the underlying Int32
|
|
61
|
+
* coordinate columns), not degrees. `Nodes.findIndexesWithinBbox` performs
|
|
62
|
+
* the degree → microdegree conversion.
|
|
63
|
+
*/
|
|
64
|
+
range(minLon: number, minLat: number, maxLon: number, maxLat: number): number[] {
|
|
65
|
+
const stack = [0, this.indexes.length - 1, 0];
|
|
66
|
+
const result: number[] = [];
|
|
67
|
+
|
|
68
|
+
while (stack.length > 0) {
|
|
69
|
+
const axis = stack.pop();
|
|
70
|
+
const right = stack.pop();
|
|
71
|
+
const left = stack.pop();
|
|
72
|
+
if (axis === undefined || right === undefined || left === undefined) {
|
|
73
|
+
throw new Error("Indirect KD index traversal stack is corrupt");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (right - left <= this.nodeSize) {
|
|
77
|
+
for (let i = left; i <= right; i++) {
|
|
78
|
+
const entityIndex = entityAt(this.indexes, i);
|
|
79
|
+
const lon = coordinate(this.lons, this.lats, entityIndex, 0);
|
|
80
|
+
const lat = coordinate(this.lons, this.lats, entityIndex, 1);
|
|
81
|
+
if (lon >= minLon && lon <= maxLon && lat >= minLat && lat <= maxLat) {
|
|
82
|
+
result.push(entityIndex);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const middle = (left + right) >> 1;
|
|
89
|
+
const entityIndex = entityAt(this.indexes, middle);
|
|
90
|
+
const lon = coordinate(this.lons, this.lats, entityIndex, 0);
|
|
91
|
+
const lat = coordinate(this.lons, this.lats, entityIndex, 1);
|
|
92
|
+
|
|
93
|
+
if (lon >= minLon && lon <= maxLon && lat >= minLat && lat <= maxLat) {
|
|
94
|
+
result.push(entityIndex);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const splitCoordinate = axis === 0 ? lon : lat;
|
|
98
|
+
const minimum = axis === 0 ? minLon : minLat;
|
|
99
|
+
const maximum = axis === 0 ? maxLon : maxLat;
|
|
100
|
+
if (minimum <= splitCoordinate) stack.push(left, middle - 1, 1 - axis);
|
|
101
|
+
if (maximum >= splitCoordinate) stack.push(middle + 1, right, 1 - axis);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function sortKd(
|
|
109
|
+
indexes: Uint32Array,
|
|
110
|
+
lons: Int32Array,
|
|
111
|
+
lats: Int32Array,
|
|
112
|
+
nodeSize: number,
|
|
113
|
+
left: number,
|
|
114
|
+
right: number,
|
|
115
|
+
axis: number,
|
|
116
|
+
) {
|
|
117
|
+
if (right - left <= nodeSize) return;
|
|
118
|
+
const middle = (left + right) >> 1;
|
|
119
|
+
select(indexes, lons, lats, middle, left, right, axis);
|
|
120
|
+
sortKd(indexes, lons, lats, nodeSize, left, middle - 1, 1 - axis);
|
|
121
|
+
sortKd(indexes, lons, lats, nodeSize, middle + 1, right, 1 - axis);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Floyd-Rivest selection adapted to dereference coordinates indirectly. */
|
|
125
|
+
function select(
|
|
126
|
+
indexes: Uint32Array,
|
|
127
|
+
lons: Int32Array,
|
|
128
|
+
lats: Int32Array,
|
|
129
|
+
k: number,
|
|
130
|
+
initialLeft: number,
|
|
131
|
+
initialRight: number,
|
|
132
|
+
axis: number,
|
|
133
|
+
) {
|
|
134
|
+
let left = initialLeft;
|
|
135
|
+
let right = initialRight;
|
|
136
|
+
|
|
137
|
+
while (right > left) {
|
|
138
|
+
if (right - left > 600) {
|
|
139
|
+
const n = right - left + 1;
|
|
140
|
+
const m = k - left + 1;
|
|
141
|
+
const z = Math.log(n);
|
|
142
|
+
const s = 0.5 * Math.exp((2 * z) / 3);
|
|
143
|
+
const sd = 0.5 * Math.sqrt((z * s * (n - s)) / n) * (m - n / 2 < 0 ? -1 : 1);
|
|
144
|
+
const newLeft = Math.max(left, Math.floor(k - (m * s) / n + sd));
|
|
145
|
+
const newRight = Math.min(right, Math.floor(k + ((n - m) * s) / n + sd));
|
|
146
|
+
select(indexes, lons, lats, k, newLeft, newRight, axis);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const pivotIndex = entityAt(indexes, k);
|
|
150
|
+
const pivot = coordinate(lons, lats, pivotIndex, axis);
|
|
151
|
+
let i = left;
|
|
152
|
+
let j = right;
|
|
153
|
+
|
|
154
|
+
swap(indexes, left, k);
|
|
155
|
+
if (compareCoordinate(lons, lats, entityAt(indexes, right), pivotIndex, axis) > 0) {
|
|
156
|
+
swap(indexes, left, right);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
while (i < j) {
|
|
160
|
+
swap(indexes, i, j);
|
|
161
|
+
i++;
|
|
162
|
+
j--;
|
|
163
|
+
while (i <= right && coordinate(lons, lats, entityAt(indexes, i), axis) < pivot) i++;
|
|
164
|
+
while (j >= left && coordinate(lons, lats, entityAt(indexes, j), axis) > pivot) j--;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (coordinate(lons, lats, entityAt(indexes, left), axis) === pivot) {
|
|
168
|
+
swap(indexes, left, j);
|
|
169
|
+
} else {
|
|
170
|
+
j++;
|
|
171
|
+
swap(indexes, j, right);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (j <= k) left = j + 1;
|
|
175
|
+
if (k <= j) right = j - 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Read the permutation entry at `position`, throwing on an out-of-bounds read.
|
|
181
|
+
* A silent skip here would leave the permutation subtly unsorted and produce
|
|
182
|
+
* wrong query results, so fail loudly instead.
|
|
183
|
+
*/
|
|
184
|
+
function entityAt(indexes: Uint32Array, position: number): number {
|
|
185
|
+
const value = indexes[position];
|
|
186
|
+
if (value === undefined) {
|
|
187
|
+
throw new Error(`Indirect KD index read out of bounds at position ${position}`);
|
|
188
|
+
}
|
|
189
|
+
return value;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function coordinate(lons: Int32Array, lats: Int32Array, entityIndex: number, axis: number): number {
|
|
193
|
+
const value = axis === 0 ? lons[entityIndex] : lats[entityIndex];
|
|
194
|
+
if (value === undefined) throw new Error(`Missing coordinate for node index ${entityIndex}`);
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function compareCoordinate(
|
|
199
|
+
lons: Int32Array,
|
|
200
|
+
lats: Int32Array,
|
|
201
|
+
a: number,
|
|
202
|
+
b: number,
|
|
203
|
+
axis: number,
|
|
204
|
+
): number {
|
|
205
|
+
return coordinate(lons, lats, a, axis) - coordinate(lons, lats, b, axis);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function swap(indexes: Uint32Array, a: number, b: number) {
|
|
209
|
+
const valueA = entityAt(indexes, a);
|
|
210
|
+
indexes[a] = entityAt(indexes, b);
|
|
211
|
+
indexes[b] = valueA;
|
|
212
|
+
}
|
package/src/limits.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hard capacity limits set by the typed-array layout of `Osm` storage.
|
|
3
|
+
*
|
|
4
|
+
* See `docs/limits.md` for the full list, including limits that come from the
|
|
5
|
+
* JavaScript engine rather than from this layout.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Largest number of node refs one way can hold (`Ways.refCount` is a `Uint16Array`). */
|
|
9
|
+
export const MAX_WAY_REFS = 0xffff;
|
|
10
|
+
|
|
11
|
+
/** Largest number of members one relation can hold (`Relations.memberCount` is a `Uint16Array`). */
|
|
12
|
+
export const MAX_RELATION_MEMBERS = 0xffff;
|
|
13
|
+
|
|
14
|
+
/** Largest UTF-8 byte length of one string (`StringTable.count` is a `Uint16Array`). */
|
|
15
|
+
export const MAX_STRING_BYTES = 0xffff;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Largest element offset a `Uint32Array` offset column can store. Bounds the total
|
|
19
|
+
* way refs, relation members and string-table bytes in one dataset.
|
|
20
|
+
*/
|
|
21
|
+
export const MAX_UINT32_OFFSET = 0xffffffff;
|
|
22
|
+
|
|
23
|
+
/** The storage limit an {@link OsmCapacityError} reports. */
|
|
24
|
+
export type OsmCapacityLimit =
|
|
25
|
+
| "way-refs"
|
|
26
|
+
| "relation-members"
|
|
27
|
+
| "string-bytes"
|
|
28
|
+
| "total-way-refs"
|
|
29
|
+
| "total-relation-members"
|
|
30
|
+
| "total-string-bytes";
|
|
31
|
+
|
|
32
|
+
/** Thrown when an entity or dataset is larger than the storage layout can hold. */
|
|
33
|
+
export class OsmCapacityError extends Error {
|
|
34
|
+
readonly code = "OSM_CAPACITY_EXCEEDED";
|
|
35
|
+
readonly limit: OsmCapacityLimit;
|
|
36
|
+
readonly value: number;
|
|
37
|
+
readonly max: number;
|
|
38
|
+
|
|
39
|
+
constructor(limit: OsmCapacityLimit, value: number, max: number, subject: string) {
|
|
40
|
+
super(
|
|
41
|
+
`${subject} has ${value.toLocaleString("en-US")} ${capacityUnit(limit)}; ` +
|
|
42
|
+
`Osmix stores at most ${max.toLocaleString("en-US")}.`,
|
|
43
|
+
);
|
|
44
|
+
this.name = "OsmCapacityError";
|
|
45
|
+
this.limit = limit;
|
|
46
|
+
this.value = value;
|
|
47
|
+
this.max = max;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function capacityUnit(limit: OsmCapacityLimit): string {
|
|
52
|
+
switch (limit) {
|
|
53
|
+
case "way-refs":
|
|
54
|
+
case "total-way-refs":
|
|
55
|
+
return "node refs";
|
|
56
|
+
case "relation-members":
|
|
57
|
+
case "total-relation-members":
|
|
58
|
+
return "members";
|
|
59
|
+
case "string-bytes":
|
|
60
|
+
case "total-string-bytes":
|
|
61
|
+
return "UTF-8 bytes";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Throw {@link OsmCapacityError} when `value` is above `max`. */
|
|
66
|
+
export function assertCapacity(
|
|
67
|
+
limit: OsmCapacityLimit,
|
|
68
|
+
value: number,
|
|
69
|
+
max: number,
|
|
70
|
+
subject: () => string,
|
|
71
|
+
): void {
|
|
72
|
+
if (value > max) throw new OsmCapacityError(limit, value, max, subject());
|
|
73
|
+
}
|
package/src/mocks.ts
CHANGED
|
@@ -1,49 +1,50 @@
|
|
|
1
|
-
import type { OsmNode } from "@osmix/
|
|
2
|
-
import { Osm } from "./osm.ts"
|
|
1
|
+
import type { OsmNode } from "@osmix/types";
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
const YAKIM_LON = -120.505898
|
|
3
|
+
import { Osm } from "./osm.ts";
|
|
6
4
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
5
|
+
const YAKIM_LAT = 46.60207;
|
|
6
|
+
const YAKIM_LON = -120.505898;
|
|
7
|
+
|
|
8
|
+
const ONE_KM_LON = 0.0131; // approximately
|
|
9
|
+
const ONE_KM_LAT = 0.009;
|
|
9
10
|
|
|
10
11
|
const node0: OsmNode = {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
12
|
+
id: 0,
|
|
13
|
+
lat: YAKIM_LAT,
|
|
14
|
+
lon: YAKIM_LON,
|
|
15
|
+
};
|
|
15
16
|
|
|
16
17
|
const node1: OsmNode = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
18
|
+
id: 1,
|
|
19
|
+
lat: YAKIM_LAT,
|
|
20
|
+
lon: YAKIM_LON - ONE_KM_LON, // approximately 1km west
|
|
21
|
+
};
|
|
21
22
|
|
|
22
23
|
function addBaseNodes(osm: Osm) {
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
osm.nodes.addNode(node0);
|
|
25
|
+
osm.nodes.addNode(node1);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
function addBaseWays(osm: Osm) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
osm.ways.addWay({
|
|
30
|
+
id: 1,
|
|
31
|
+
refs: [0, 1],
|
|
32
|
+
tags: {
|
|
33
|
+
highway: "primary",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* Create a base OSM with one way and two nodes.
|
|
39
40
|
*/
|
|
40
41
|
export function createMockBaseOsm(): Osm {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
const base = new Osm();
|
|
43
|
+
addBaseNodes(base);
|
|
44
|
+
base.nodes.buildIndex();
|
|
45
|
+
addBaseWays(base);
|
|
46
|
+
base.buildIndexes();
|
|
47
|
+
return base;
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
/**
|
|
@@ -53,88 +54,88 @@ export function createMockBaseOsm(): Osm {
|
|
|
53
54
|
* - A way that crosses over the first way, but does not share a node and so starts disconnected
|
|
54
55
|
*/
|
|
55
56
|
export function createMockPatchOsm(): Osm {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
57
|
+
const osm = new Osm();
|
|
58
|
+
|
|
59
|
+
addBaseNodes(osm);
|
|
60
|
+
|
|
61
|
+
// Add way the connects with base way node 0 (will replace it)
|
|
62
|
+
osm.nodes.addNode({
|
|
63
|
+
...node0,
|
|
64
|
+
id: 2,
|
|
65
|
+
tags: {
|
|
66
|
+
crossing: "yes",
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
osm.nodes.addNode({
|
|
70
|
+
...node1,
|
|
71
|
+
id: 3,
|
|
72
|
+
lon: YAKIM_LON + ONE_KM_LON, // ends 1km east of the center point
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Add nodes for disconnected way
|
|
76
|
+
osm.nodes.addNode({
|
|
77
|
+
id: 4,
|
|
78
|
+
lat: YAKIM_LAT - ONE_KM_LAT, // 1km south
|
|
79
|
+
lon: YAKIM_LON + ONE_KM_LON / 4, // 250m east
|
|
80
|
+
});
|
|
81
|
+
osm.nodes.addNode({
|
|
82
|
+
id: 5,
|
|
83
|
+
lat: YAKIM_LAT + ONE_KM_LAT, // 1km north
|
|
84
|
+
lon: YAKIM_LON + ONE_KM_LON / 4, // 250m east
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Add nodes for crossing way
|
|
88
|
+
osm.nodes.addNode({
|
|
89
|
+
id: 6,
|
|
90
|
+
lat: YAKIM_LAT - ONE_KM_LAT, // 1km south
|
|
91
|
+
lon: YAKIM_LON + ONE_KM_LON / 2, // 500m east
|
|
92
|
+
});
|
|
93
|
+
osm.nodes.addNode({
|
|
94
|
+
id: 7,
|
|
95
|
+
lat: YAKIM_LAT + ONE_KM_LAT, // 1km north
|
|
96
|
+
lon: YAKIM_LON + ONE_KM_LON / 2, // 500m east
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
osm.nodes.buildIndex();
|
|
100
|
+
|
|
101
|
+
// Add same base way with new tags
|
|
102
|
+
osm.ways.addWay({
|
|
103
|
+
id: 1,
|
|
104
|
+
refs: [0, 1],
|
|
105
|
+
tags: {
|
|
106
|
+
highway: "primary",
|
|
107
|
+
version: "2",
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Add way that overlaps with base way node 1. Node should be de-duplicated.
|
|
112
|
+
osm.ways.addWay({
|
|
113
|
+
id: 2,
|
|
114
|
+
refs: [2, 3],
|
|
115
|
+
tags: {
|
|
116
|
+
highway: "secondary",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Add way that crosses way 2, and should generate 1 intersection.
|
|
121
|
+
osm.ways.addWay({
|
|
122
|
+
id: 3,
|
|
123
|
+
refs: [4, 5],
|
|
124
|
+
tags: {
|
|
125
|
+
highway: "primary",
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Add a way that crosses way 2, but has a tag indicating it is an underpass and should be left alone
|
|
130
|
+
osm.ways.addWay({
|
|
131
|
+
id: 4,
|
|
132
|
+
refs: [6, 7],
|
|
133
|
+
tags: {
|
|
134
|
+
highway: "underpass",
|
|
135
|
+
tunnel: "yes",
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
osm.buildIndexes();
|
|
140
|
+
return osm;
|
|
140
141
|
}
|