@osmix/pbf 0.0.1
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 +7 -0
- package/README.md +144 -0
- package/dist/blobs-to-blocks.d.ts +5 -0
- package/dist/blobs-to-blocks.js +21 -0
- package/dist/blocks-to-pbf.d.ts +16 -0
- package/dist/blocks-to-pbf.js +73 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/pbf-to-blobs.d.ts +6 -0
- package/dist/pbf-to-blobs.js +48 -0
- package/dist/pbf-to-blocks.d.ts +20 -0
- package/dist/pbf-to-blocks.js +53 -0
- package/dist/proto/fileformat.d.ts +26 -0
- package/dist/proto/fileformat.js +56 -0
- package/dist/proto/osmformat.d.ts +91 -0
- package/dist/proto/osmformat.js +458 -0
- package/dist/spec.d.ts +5 -0
- package/dist/spec.js +9 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.js +92 -0
- package/package.json +49 -0
- package/src/blobs-to-blocks.ts +28 -0
- package/src/blocks-to-pbf.ts +98 -0
- package/src/index.ts +8 -0
- package/src/pbf-to-blobs.ts +56 -0
- package/src/pbf-to-blocks.ts +77 -0
- package/src/proto/fileformat.proto +68 -0
- package/src/proto/fileformat.ts +70 -0
- package/src/proto/osmformat.proto +262 -0
- package/src/proto/osmformat.ts +488 -0
- package/src/spec.ts +10 -0
- package/src/utils.ts +90 -0
- package/test/blobs-to-blocks.test.ts +73 -0
- package/test/helpers.ts +66 -0
- package/test/pbf-to-blobs.test.ts +85 -0
- package/test/read.bench.ts +42 -0
- package/test/read.test.ts +45 -0
- package/test/streams.test.ts +92 -0
- package/test/utils.bun.test.ts +327 -0
- package/test/utils.test.ts +56 -0
- package/test/utils.ts +65 -0
- package/test/verify-pbf-reading.bun.test.ts +39 -0
- package/test/write.test.ts +86 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { assert, describe, expect, it } from "vitest"
|
|
2
|
+
import {
|
|
3
|
+
concatUint8,
|
|
4
|
+
toAsyncGenerator,
|
|
5
|
+
uint32BE,
|
|
6
|
+
webCompress,
|
|
7
|
+
webDecompress,
|
|
8
|
+
} from "../src/utils"
|
|
9
|
+
|
|
10
|
+
describe("utils", () => {
|
|
11
|
+
it("wraps values into an async generator", async () => {
|
|
12
|
+
const generator = toAsyncGenerator(3)
|
|
13
|
+
const first = await generator.next()
|
|
14
|
+
assert.deepEqual(first, { value: 3, done: false })
|
|
15
|
+
const done = await generator.next()
|
|
16
|
+
assert.deepEqual(done, { value: undefined, done: true })
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it("consumes readable streams", async () => {
|
|
20
|
+
const stream = new ReadableStream<number>({
|
|
21
|
+
start(controller) {
|
|
22
|
+
controller.enqueue(1)
|
|
23
|
+
controller.enqueue(2)
|
|
24
|
+
controller.close()
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
const values: number[] = []
|
|
28
|
+
for await (const value of toAsyncGenerator(stream)) values.push(value)
|
|
29
|
+
assert.deepEqual(values, [1, 2])
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("throws on nullish inputs", async () => {
|
|
33
|
+
const invalidInput = null as unknown as never
|
|
34
|
+
await expect(toAsyncGenerator(invalidInput).next()).rejects.toThrow(
|
|
35
|
+
"Value is null",
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it("concatenates Uint8Array segments", () => {
|
|
40
|
+
const a = Uint8Array.of(1, 2)
|
|
41
|
+
const b = Uint8Array.of(3)
|
|
42
|
+
assert.deepEqual(concatUint8(a, b), Uint8Array.of(1, 2, 3))
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it("encodes big-endian 32-bit integers", () => {
|
|
46
|
+
assert.deepEqual(uint32BE(0x01020304), Uint8Array.of(1, 2, 3, 4))
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("compresses and decompresses data", async () => {
|
|
50
|
+
const input = new TextEncoder().encode("osmix") as Uint8Array<ArrayBuffer>
|
|
51
|
+
const compressed = await webCompress(input)
|
|
52
|
+
assert.notDeepEqual(compressed, input)
|
|
53
|
+
const decompressed = await webDecompress(compressed)
|
|
54
|
+
assert.deepEqual(decompressed, input)
|
|
55
|
+
})
|
|
56
|
+
})
|
package/test/utils.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { PbfFixture } from "@osmix/shared/test/fixtures"
|
|
2
|
+
import { assert } from "vitest"
|
|
3
|
+
import type {
|
|
4
|
+
OsmPbfBlock,
|
|
5
|
+
OsmPbfGroup,
|
|
6
|
+
OsmPbfHeaderBlock,
|
|
7
|
+
} from "../src/proto/osmformat"
|
|
8
|
+
|
|
9
|
+
export async function testOsmPbfReader(
|
|
10
|
+
osm: {
|
|
11
|
+
header: OsmPbfHeaderBlock
|
|
12
|
+
blocks: AsyncGenerator<OsmPbfBlock>
|
|
13
|
+
},
|
|
14
|
+
pbf: PbfFixture,
|
|
15
|
+
) {
|
|
16
|
+
assert.deepEqual(osm.header.bbox, pbf.bbox)
|
|
17
|
+
|
|
18
|
+
const { onGroup, count } = createOsmEntityCounter()
|
|
19
|
+
for await (const block of osm.blocks)
|
|
20
|
+
for (const group of block.primitivegroup) onGroup(group)
|
|
21
|
+
|
|
22
|
+
assert.equal(count.nodes, pbf.nodes)
|
|
23
|
+
assert.equal(count.ways, pbf.ways)
|
|
24
|
+
assert.equal(count.relations, pbf.relations)
|
|
25
|
+
assert.equal(count.node0, pbf.node0.id)
|
|
26
|
+
assert.equal(count.way0, pbf.way0)
|
|
27
|
+
assert.equal(count.relation0, pbf.relation0)
|
|
28
|
+
|
|
29
|
+
return count
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createOsmEntityCounter() {
|
|
33
|
+
const count = {
|
|
34
|
+
nodes: 0,
|
|
35
|
+
ways: 0,
|
|
36
|
+
relations: 0,
|
|
37
|
+
node0: -1,
|
|
38
|
+
way0: -1,
|
|
39
|
+
relation0: -1,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const onGroup = (group: OsmPbfGroup) => {
|
|
43
|
+
if (count.node0 === -1 && group.dense?.id?.[0] != null) {
|
|
44
|
+
count.node0 = group.dense.id[0]
|
|
45
|
+
}
|
|
46
|
+
if (count.way0 === -1 && group.ways?.[0]?.id != null) {
|
|
47
|
+
count.way0 = group.ways[0].id
|
|
48
|
+
}
|
|
49
|
+
if (count.relation0 === -1 && group.relations?.[0]?.id != null) {
|
|
50
|
+
count.relation0 = group.relations[0].id
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
count.nodes += group.nodes?.length ?? 0
|
|
54
|
+
if (group.dense) {
|
|
55
|
+
count.nodes += group.dense.id.length
|
|
56
|
+
}
|
|
57
|
+
count.ways += group.ways?.length ?? 0
|
|
58
|
+
count.relations += group.relations?.length ?? 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
onGroup,
|
|
63
|
+
count,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { getFixtureFile } from "@osmix/shared/test/fixtures"
|
|
2
|
+
import { describe, expect, test } from "vitest"
|
|
3
|
+
import { readOsmPbf } from "../src/pbf-to-blocks"
|
|
4
|
+
import { isBun } from "../src/utils"
|
|
5
|
+
|
|
6
|
+
describe.runIf(isBun())("PBF Reading with Bun", () => {
|
|
7
|
+
test("can read an actual OSM PBF file", async () => {
|
|
8
|
+
// Load a real PBF fixture
|
|
9
|
+
const pbfData = await getFixtureFile("monaco.pbf")
|
|
10
|
+
|
|
11
|
+
// Try to read it
|
|
12
|
+
const result = await readOsmPbf(pbfData)
|
|
13
|
+
|
|
14
|
+
// Basic sanity checks
|
|
15
|
+
expect(result.header).toBeDefined()
|
|
16
|
+
expect(result.header.required_features).toBeDefined()
|
|
17
|
+
expect(result.blocks).toBeDefined()
|
|
18
|
+
|
|
19
|
+
// Try to read at least one block
|
|
20
|
+
const firstBlock = await result.blocks.next()
|
|
21
|
+
expect(firstBlock.done).toBe(false)
|
|
22
|
+
expect(firstBlock.value).toBeDefined()
|
|
23
|
+
|
|
24
|
+
// If it's a primitive block, it should have primitivegroup
|
|
25
|
+
if ("primitivegroup" in firstBlock.value) {
|
|
26
|
+
expect(Array.isArray(firstBlock.value.primitivegroup)).toBe(true)
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("header contains expected OSM data", async () => {
|
|
31
|
+
const pbfData = await getFixtureFile("monaco.pbf")
|
|
32
|
+
|
|
33
|
+
const result = await readOsmPbf(pbfData)
|
|
34
|
+
|
|
35
|
+
// Check that header has the required OSM features
|
|
36
|
+
expect(result.header.required_features).toBeDefined()
|
|
37
|
+
expect(result.header.required_features.length).toBeGreaterThan(0)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { unlink } from "node:fs/promises"
|
|
2
|
+
import {
|
|
3
|
+
getFixtureFile,
|
|
4
|
+
getFixtureFileReadStream,
|
|
5
|
+
getFixtureFileWriteStream,
|
|
6
|
+
getFixturePath,
|
|
7
|
+
PBFs,
|
|
8
|
+
} from "@osmix/shared/test/fixtures"
|
|
9
|
+
import { assert, describe, it } from "vitest"
|
|
10
|
+
import {
|
|
11
|
+
OsmBlocksToPbfBytesTransformStream,
|
|
12
|
+
osmBlockToPbfBlobBytes,
|
|
13
|
+
} from "../src/blocks-to-pbf"
|
|
14
|
+
import {
|
|
15
|
+
OsmPbfBytesToBlocksTransformStream,
|
|
16
|
+
readOsmPbf,
|
|
17
|
+
} from "../src/pbf-to-blocks"
|
|
18
|
+
import { testOsmPbfReader } from "./utils"
|
|
19
|
+
|
|
20
|
+
describe("write", () => {
|
|
21
|
+
describe.each(Object.entries(PBFs))("%s", (name, pbf) => {
|
|
22
|
+
it("to buffer", async () => {
|
|
23
|
+
const fileData = await getFixtureFile(pbf.url)
|
|
24
|
+
const osm = await readOsmPbf(fileData)
|
|
25
|
+
|
|
26
|
+
let node0: number | null = null
|
|
27
|
+
let way0: number | null = null
|
|
28
|
+
let relation0: number | null = null
|
|
29
|
+
|
|
30
|
+
// Write the PBF to an array buffer
|
|
31
|
+
let data = new Uint8Array(0)
|
|
32
|
+
const write = (chunk: Uint8Array) => {
|
|
33
|
+
const newData = new Uint8Array(data.length + chunk.length)
|
|
34
|
+
newData.set(data)
|
|
35
|
+
newData.set(chunk, data.length)
|
|
36
|
+
data = newData
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
write(await osmBlockToPbfBlobBytes(osm.header))
|
|
40
|
+
for await (const block of osm.blocks) {
|
|
41
|
+
for (const group of block.primitivegroup) {
|
|
42
|
+
if (node0 == null && group.dense?.id?.[0] != null) {
|
|
43
|
+
node0 = group.dense.id[0]
|
|
44
|
+
}
|
|
45
|
+
if (way0 == null && group.ways?.[0]?.id != null) {
|
|
46
|
+
way0 = group.ways[0].id
|
|
47
|
+
}
|
|
48
|
+
if (relation0 == null && group.relations?.[0]?.id != null) {
|
|
49
|
+
relation0 = group.relations[0].id
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
write(await osmBlockToPbfBlobBytes(block))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Re-parse the new PBF and test
|
|
56
|
+
assert.exists(data.buffer)
|
|
57
|
+
// TODO: assert.equal(stream.buffer.byteLength, fileData.byteLength)
|
|
58
|
+
const osm2 = await readOsmPbf(data.buffer)
|
|
59
|
+
|
|
60
|
+
assert.deepEqual(osm.header, osm2.header)
|
|
61
|
+
const entities = await testOsmPbfReader(osm2, pbf)
|
|
62
|
+
assert.equal(entities.node0, node0)
|
|
63
|
+
assert.equal(entities.way0, way0)
|
|
64
|
+
assert.equal(entities.relation0, relation0)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it("to file", async () => {
|
|
68
|
+
const testFileName = `${name}-write-test.pbf`
|
|
69
|
+
const fileStream = getFixtureFileReadStream(pbf.url)
|
|
70
|
+
|
|
71
|
+
const fileWriteStream = getFixtureFileWriteStream(testFileName)
|
|
72
|
+
await fileStream
|
|
73
|
+
.pipeThrough(new OsmPbfBytesToBlocksTransformStream())
|
|
74
|
+
.pipeThrough(new OsmBlocksToPbfBytesTransformStream())
|
|
75
|
+
.pipeTo(fileWriteStream)
|
|
76
|
+
|
|
77
|
+
const testFileData = await getFixtureFile(pbf.url)
|
|
78
|
+
const testOsm = await readOsmPbf(testFileData)
|
|
79
|
+
|
|
80
|
+
assert.deepEqual(testOsm.header.bbox, pbf.bbox)
|
|
81
|
+
await testOsmPbfReader(testOsm, pbf)
|
|
82
|
+
|
|
83
|
+
await unlink(getFixturePath(testFileName))
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
})
|
package/tsconfig.json
ADDED