@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,98 @@
|
|
|
1
|
+
import Pbf from "pbf"
|
|
2
|
+
import { writeBlob, writeBlobHeader } from "./proto/fileformat"
|
|
3
|
+
import type { OsmPbfBlock, OsmPbfHeaderBlock } from "./proto/osmformat"
|
|
4
|
+
import { writeHeaderBlock, writePrimitiveBlock } from "./proto/osmformat"
|
|
5
|
+
import {
|
|
6
|
+
MAX_BLOB_SIZE_BYTES,
|
|
7
|
+
MAX_HEADER_SIZE_BYTES,
|
|
8
|
+
RECOMMENDED_BLOB_SIZE_BYTES,
|
|
9
|
+
RECOMMENDED_HEADER_SIZE_BYTES,
|
|
10
|
+
} from "./spec"
|
|
11
|
+
import { concatUint8, uint32BE, webCompress } from "./utils"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Serializes a header or primitive block into a spec-compliant compressed blob.
|
|
15
|
+
* Automatically sets the blob type, compresses the payload, and enforces size guardrails.
|
|
16
|
+
* @param block - Parsed header or primitive block to encode.
|
|
17
|
+
* @returns BlobHeader length prefix + Blob bytes as a single Uint8Array.
|
|
18
|
+
*/
|
|
19
|
+
export async function osmBlockToPbfBlobBytes(
|
|
20
|
+
block: OsmPbfBlock | OsmPbfHeaderBlock,
|
|
21
|
+
compress: (
|
|
22
|
+
data: Uint8Array<ArrayBuffer>,
|
|
23
|
+
) => Promise<Uint8Array<ArrayBuffer>> = webCompress,
|
|
24
|
+
) {
|
|
25
|
+
const contentPbf = new Pbf()
|
|
26
|
+
let type: "OSMHeader" | "OSMData"
|
|
27
|
+
if ("primitivegroup" in block) {
|
|
28
|
+
type = "OSMData"
|
|
29
|
+
writePrimitiveBlock(block, contentPbf)
|
|
30
|
+
} else {
|
|
31
|
+
type = "OSMHeader"
|
|
32
|
+
writeHeaderBlock(block, contentPbf)
|
|
33
|
+
}
|
|
34
|
+
const contentData = contentPbf.finish() as Uint8Array<ArrayBuffer>
|
|
35
|
+
const raw_size = contentData.length
|
|
36
|
+
const compressedBuffer = await compress(contentData)
|
|
37
|
+
|
|
38
|
+
const blobPbf = new Pbf()
|
|
39
|
+
writeBlob(
|
|
40
|
+
{
|
|
41
|
+
raw_size,
|
|
42
|
+
zlib_data: compressedBuffer,
|
|
43
|
+
},
|
|
44
|
+
blobPbf,
|
|
45
|
+
)
|
|
46
|
+
const blob = blobPbf.finish()
|
|
47
|
+
|
|
48
|
+
const blobHeaderPbf = new Pbf()
|
|
49
|
+
writeBlobHeader(
|
|
50
|
+
{
|
|
51
|
+
type,
|
|
52
|
+
datasize: blob.length,
|
|
53
|
+
},
|
|
54
|
+
blobHeaderPbf,
|
|
55
|
+
)
|
|
56
|
+
const blobHeader = blobHeaderPbf.finish()
|
|
57
|
+
const blobHeaderSize = uint32BE(blobHeader.byteLength)
|
|
58
|
+
|
|
59
|
+
// Check the BlobHeader and Blob sizes, log error if over the recommended size, throw error if over the maximum size
|
|
60
|
+
if (blobHeader.byteLength > RECOMMENDED_HEADER_SIZE_BYTES) {
|
|
61
|
+
const sizeKiB = (blobHeader.byteLength / 1024).toFixed(2)
|
|
62
|
+
if (blobHeader.byteLength > MAX_HEADER_SIZE_BYTES) {
|
|
63
|
+
throw new Error(`BlobHeader is ${sizeKiB} KiB, the maximum size is 64KiB`)
|
|
64
|
+
}
|
|
65
|
+
console.warn(`BlobHeader is ${sizeKiB} KiB, the recommended size is 32KiB`)
|
|
66
|
+
}
|
|
67
|
+
if (blob.byteLength > RECOMMENDED_BLOB_SIZE_BYTES) {
|
|
68
|
+
const sizeMiB = (blob.byteLength / 1024 / 1024).toFixed(2)
|
|
69
|
+
if (blob.byteLength > MAX_BLOB_SIZE_BYTES) {
|
|
70
|
+
throw new Error(`Blob is ${sizeMiB} MiB, the maximum size is 32MiB`)
|
|
71
|
+
}
|
|
72
|
+
console.warn(`Blob is ${sizeMiB} MiB, the recommended size is 16MiB`)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return concatUint8(blobHeaderSize, blobHeader, blob)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Web `TransformStream` that converts OSM header/data blocks into PBF byte chunks.
|
|
80
|
+
* Throws if the header is not the first block and reuses `osmBlockToPbfBlobBytes` for encoding.
|
|
81
|
+
*/
|
|
82
|
+
export class OsmBlocksToPbfBytesTransformStream extends TransformStream<
|
|
83
|
+
OsmPbfHeaderBlock | OsmPbfBlock,
|
|
84
|
+
Uint8Array
|
|
85
|
+
> {
|
|
86
|
+
headerEnqueued = false
|
|
87
|
+
constructor() {
|
|
88
|
+
super({
|
|
89
|
+
transform: async (block, controller) => {
|
|
90
|
+
if ("primitivegroup" in block && !this.headerEnqueued) {
|
|
91
|
+
throw Error("Header first in ReadableStream of blocks.")
|
|
92
|
+
}
|
|
93
|
+
this.headerEnqueued = true
|
|
94
|
+
controller.enqueue(await osmBlockToPbfBlobBytes(block))
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import Pbf from "pbf"
|
|
2
|
+
import { HEADER_LENGTH_BYTES } from "./pbf-to-blocks"
|
|
3
|
+
import {
|
|
4
|
+
type OsmPbfBlobHeader,
|
|
5
|
+
readBlob,
|
|
6
|
+
readBlobHeader,
|
|
7
|
+
} from "./proto/fileformat"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates a stateful parser that slices incoming bytes into compressed OSM PBF blobs.
|
|
11
|
+
* Works with buffers, iterables, or streams and yields `Uint8Array` payloads ready to decompress.
|
|
12
|
+
* The first blob represents the file header; subsequent blobs hold primitive data.
|
|
13
|
+
*/
|
|
14
|
+
export function createOsmPbfBlobGenerator() {
|
|
15
|
+
let pbf: Pbf = new Pbf(new Uint8Array(0))
|
|
16
|
+
let state: "header-length" | "header" | "blob" = "header-length"
|
|
17
|
+
let bytesNeeded: number = HEADER_LENGTH_BYTES
|
|
18
|
+
let blobHeader: OsmPbfBlobHeader | null = null
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Feed the parser with the next chunk of bytes and yield any complete compressed blobs.
|
|
22
|
+
*/
|
|
23
|
+
return function* nextChunk(chunk: Uint8Array) {
|
|
24
|
+
const currentBuffer: Uint8Array = pbf.buf.slice(pbf.pos)
|
|
25
|
+
const tmpBuffer = new Uint8Array(
|
|
26
|
+
currentBuffer.buffer.byteLength + chunk.byteLength,
|
|
27
|
+
)
|
|
28
|
+
tmpBuffer.set(currentBuffer.subarray(0))
|
|
29
|
+
tmpBuffer.set(new Uint8Array(chunk), currentBuffer.byteLength)
|
|
30
|
+
pbf = new Pbf(tmpBuffer)
|
|
31
|
+
|
|
32
|
+
while (pbf.pos + bytesNeeded <= pbf.length) {
|
|
33
|
+
if (state === "header-length") {
|
|
34
|
+
const dataView = new DataView(pbf.buf.buffer)
|
|
35
|
+
bytesNeeded = dataView.getInt32(pbf.pos, false) // network byte order
|
|
36
|
+
pbf.pos += HEADER_LENGTH_BYTES
|
|
37
|
+
state = "header"
|
|
38
|
+
} else if (state === "header") {
|
|
39
|
+
blobHeader = readBlobHeader(pbf, pbf.pos + bytesNeeded)
|
|
40
|
+
bytesNeeded = blobHeader.datasize
|
|
41
|
+
state = "blob"
|
|
42
|
+
} else if (state === "blob") {
|
|
43
|
+
if (blobHeader == null) throw Error("Blob header has not been read")
|
|
44
|
+
const blob = readBlob(pbf, pbf.pos + bytesNeeded)
|
|
45
|
+
if (blob.zlib_data === undefined || blob.zlib_data.length === 0)
|
|
46
|
+
throw Error("Blob has no zlib data. Format is unsupported.")
|
|
47
|
+
|
|
48
|
+
yield blob.zlib_data as Uint8Array<ArrayBuffer>
|
|
49
|
+
|
|
50
|
+
state = "header-length"
|
|
51
|
+
bytesNeeded = HEADER_LENGTH_BYTES
|
|
52
|
+
blobHeader = null
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import Pbf from "pbf"
|
|
2
|
+
import { osmPbfBlobsToBlocksGenerator } from "./blobs-to-blocks"
|
|
3
|
+
import { createOsmPbfBlobGenerator } from "./pbf-to-blobs"
|
|
4
|
+
import {
|
|
5
|
+
type OsmPbfBlock,
|
|
6
|
+
type OsmPbfHeaderBlock,
|
|
7
|
+
readHeaderBlock,
|
|
8
|
+
readPrimitiveBlock,
|
|
9
|
+
} from "./proto/osmformat"
|
|
10
|
+
import {
|
|
11
|
+
type AsyncGeneratorValue,
|
|
12
|
+
toAsyncGenerator,
|
|
13
|
+
webDecompress,
|
|
14
|
+
} from "./utils"
|
|
15
|
+
|
|
16
|
+
export const HEADER_LENGTH_BYTES = 4
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parses OSM PBF bytes from buffers, streams, or generators into header + block iterators.
|
|
20
|
+
* Returns the decoded header and a lazy async generator of primitive blocks.
|
|
21
|
+
*/
|
|
22
|
+
export async function readOsmPbf(data: AsyncGeneratorValue<ArrayBufferLike>) {
|
|
23
|
+
const generateBlobsFromChunk = createOsmPbfBlobGenerator()
|
|
24
|
+
const blocks = osmPbfBlobsToBlocksGenerator(
|
|
25
|
+
(async function* () {
|
|
26
|
+
for await (const chunk of toAsyncGenerator(data)) {
|
|
27
|
+
for await (const blob of generateBlobsFromChunk(
|
|
28
|
+
new Uint8Array(chunk),
|
|
29
|
+
)) {
|
|
30
|
+
yield blob
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
})(),
|
|
34
|
+
)
|
|
35
|
+
const header = (await blocks.next()).value
|
|
36
|
+
if (header == null || !("required_features" in header)) {
|
|
37
|
+
throw Error("OSM PBF header block not found")
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
header,
|
|
41
|
+
blocks: blocks as AsyncGenerator<OsmPbfBlock>,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Web `TransformStream` that turns raw PBF byte chunks into OSM header/data blocks.
|
|
47
|
+
* Assumes the first decoded blob carries the header and emits it before any primitive blocks.
|
|
48
|
+
*/
|
|
49
|
+
export class OsmPbfBytesToBlocksTransformStream extends TransformStream<
|
|
50
|
+
ArrayBufferLike,
|
|
51
|
+
OsmPbfHeaderBlock | OsmPbfBlock
|
|
52
|
+
> {
|
|
53
|
+
generateBlobsFromChunk = createOsmPbfBlobGenerator()
|
|
54
|
+
header: OsmPbfHeaderBlock | null = null
|
|
55
|
+
constructor(
|
|
56
|
+
decompress: (
|
|
57
|
+
data: Uint8Array<ArrayBuffer>,
|
|
58
|
+
) => Promise<Uint8Array<ArrayBuffer>> = webDecompress,
|
|
59
|
+
) {
|
|
60
|
+
super({
|
|
61
|
+
transform: async (bytesChunk, controller) => {
|
|
62
|
+
for await (const rawBlobs of this.generateBlobsFromChunk(
|
|
63
|
+
new Uint8Array(bytesChunk),
|
|
64
|
+
)) {
|
|
65
|
+
const decompressed = await decompress(rawBlobs)
|
|
66
|
+
const pbf = new Pbf(decompressed)
|
|
67
|
+
if (this.header == null) {
|
|
68
|
+
this.header = readHeaderBlock(pbf)
|
|
69
|
+
controller.enqueue(this.header)
|
|
70
|
+
} else {
|
|
71
|
+
controller.enqueue(readPrimitiveBlock(pbf))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** Copyright (c) 2010 Scott A. Crosby. <scott@sacrosby.com>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
20
|
+
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
syntax = "proto2";
|
|
24
|
+
|
|
25
|
+
option java_package = "crosby.binary";
|
|
26
|
+
package OSMPBF;
|
|
27
|
+
|
|
28
|
+
//protoc --java_out=../.. fileformat.proto
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
//
|
|
32
|
+
// STORAGE LAYER: Storing primitives.
|
|
33
|
+
//
|
|
34
|
+
|
|
35
|
+
message Blob {
|
|
36
|
+
optional int32 raw_size = 2; // When compressed, the uncompressed size
|
|
37
|
+
|
|
38
|
+
oneof data {
|
|
39
|
+
bytes raw = 1; // No compression
|
|
40
|
+
|
|
41
|
+
// Possible compressed versions of the data.
|
|
42
|
+
bytes zlib_data = 3;
|
|
43
|
+
|
|
44
|
+
// For LZMA compressed data (optional)
|
|
45
|
+
bytes lzma_data = 4;
|
|
46
|
+
|
|
47
|
+
// Formerly used for bzip2 compressed data. Deprecated in 2010.
|
|
48
|
+
bytes OBSOLETE_bzip2_data = 5 [deprecated=true]; // Don't reuse this tag number.
|
|
49
|
+
|
|
50
|
+
// For LZ4 compressed data (optional)
|
|
51
|
+
bytes lz4_data = 6;
|
|
52
|
+
|
|
53
|
+
// For ZSTD compressed data (optional)
|
|
54
|
+
bytes zstd_data = 7;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* A file contains an sequence of fileblock headers, each prefixed by
|
|
59
|
+
their length in network byte order, followed by a data block
|
|
60
|
+
containing the actual data. Types starting with a "_" are reserved.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
message BlobHeader {
|
|
64
|
+
required string type = 1;
|
|
65
|
+
optional bytes indexdata = 2;
|
|
66
|
+
required int32 datasize = 3;
|
|
67
|
+
}
|
|
68
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// code generated by @mapbox/pbf v4.0.1 and types merged in by hand
|
|
2
|
+
import type Pbf from "pbf"
|
|
3
|
+
|
|
4
|
+
export type OsmPbfBlob = {
|
|
5
|
+
raw_size?: number
|
|
6
|
+
raw?: Uint8Array
|
|
7
|
+
zlib_data?: Uint8Array
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type OsmPbfBlobHeader = {
|
|
11
|
+
type: "OSMHeader" | "OSMData"
|
|
12
|
+
datasize: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Reads an `OsmPbfBlob` message from the current position in the Pbf reader.
|
|
17
|
+
*/
|
|
18
|
+
export function readBlob(pbf: Pbf, end?: number): OsmPbfBlob {
|
|
19
|
+
return pbf.readFields(
|
|
20
|
+
readBlobField,
|
|
21
|
+
{
|
|
22
|
+
raw_size: 0,
|
|
23
|
+
},
|
|
24
|
+
end,
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Dispatches individual Blob fields based on their protobuf tag.
|
|
29
|
+
*/
|
|
30
|
+
function readBlobField(tag: number, obj: OsmPbfBlob, pbf: Pbf) {
|
|
31
|
+
if (tag === 2) obj.raw_size = pbf.readVarint(true)
|
|
32
|
+
else if (tag === 1) {
|
|
33
|
+
obj.raw = pbf.readBytes()
|
|
34
|
+
} else if (tag === 3) {
|
|
35
|
+
obj.zlib_data = pbf.readBytes()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Writes an `OsmPbfBlob` message to the provided Pbf writer.
|
|
40
|
+
*/
|
|
41
|
+
export function writeBlob(obj: OsmPbfBlob, pbf: Pbf) {
|
|
42
|
+
if (obj.raw_size) pbf.writeVarintField(2, obj.raw_size)
|
|
43
|
+
if (obj.raw != null) pbf.writeBytesField(1, obj.raw)
|
|
44
|
+
if (obj.zlib_data != null) pbf.writeBytesField(3, obj.zlib_data)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Reads an `OsmPbfBlobHeader` from the current position in the Pbf reader.
|
|
49
|
+
*/
|
|
50
|
+
export function readBlobHeader(pbf: Pbf, end?: number): OsmPbfBlobHeader {
|
|
51
|
+
return pbf.readFields(
|
|
52
|
+
readBlobHeaderField,
|
|
53
|
+
{ type: "OSMData", datasize: 0 },
|
|
54
|
+
end,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Dispatches individual BlobHeader fields based on their protobuf tag.
|
|
59
|
+
*/
|
|
60
|
+
function readBlobHeaderField(tag: number, obj: OsmPbfBlobHeader, pbf: Pbf) {
|
|
61
|
+
if (tag === 1) obj.type = pbf.readString() as OsmPbfBlobHeader["type"]
|
|
62
|
+
else if (tag === 3) obj.datasize = pbf.readVarint(true)
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Writes an `OsmPbfBlobHeader` message to the provided Pbf writer.
|
|
66
|
+
*/
|
|
67
|
+
export function writeBlobHeader(obj: OsmPbfBlobHeader, pbf: Pbf) {
|
|
68
|
+
if (obj.type) pbf.writeStringField(1, obj.type)
|
|
69
|
+
if (obj.datasize) pbf.writeVarintField(3, obj.datasize)
|
|
70
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/** Copyright (c) 2010 Scott A. Crosby. <scott@sacrosby.com>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
20
|
+
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
syntax = "proto2";
|
|
24
|
+
|
|
25
|
+
option java_package = "crosby.binary";
|
|
26
|
+
package OSMPBF;
|
|
27
|
+
|
|
28
|
+
/* OSM Binary file format
|
|
29
|
+
|
|
30
|
+
This is the master schema file of the OSM binary file format. This
|
|
31
|
+
file is designed to support limited random-access and future
|
|
32
|
+
extendability.
|
|
33
|
+
|
|
34
|
+
A binary OSM file consists of a sequence of FileBlocks (please see
|
|
35
|
+
fileformat.proto). The first fileblock contains a serialized instance
|
|
36
|
+
of HeaderBlock, followed by a sequence of PrimitiveBlock blocks that
|
|
37
|
+
contain the primitives.
|
|
38
|
+
|
|
39
|
+
Each primitiveblock is designed to be independently parsable. It
|
|
40
|
+
contains a string table storing all strings in that block (keys and
|
|
41
|
+
values in tags, roles in relations, usernames, etc.) as well as
|
|
42
|
+
metadata containing the precision of coordinates or timestamps in that
|
|
43
|
+
block.
|
|
44
|
+
|
|
45
|
+
A primitiveblock contains a sequence of primitive groups, each
|
|
46
|
+
containing primitives of the same type (nodes, densenodes, ways,
|
|
47
|
+
relations). Coordinates are stored in signed 64-bit integers. Lat&lon
|
|
48
|
+
are measured in units <granularity> nanodegrees. The default of
|
|
49
|
+
granularity of 100 nanodegrees corresponds to about 1cm on the ground,
|
|
50
|
+
and a full lat or lon fits into 32 bits.
|
|
51
|
+
|
|
52
|
+
Converting an integer to a latitude or longitude uses the formula:
|
|
53
|
+
$OUT = IN * granularity / 10**9$. Many encoding schemes use delta
|
|
54
|
+
coding when representing nodes and relations.
|
|
55
|
+
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
//////////////////////////////////////////////////////////////////////////
|
|
59
|
+
//////////////////////////////////////////////////////////////////////////
|
|
60
|
+
|
|
61
|
+
/* Contains the file header. */
|
|
62
|
+
|
|
63
|
+
message HeaderBlock {
|
|
64
|
+
optional HeaderBBox bbox = 1;
|
|
65
|
+
|
|
66
|
+
/* Additional tags to aid in parsing this dataset */
|
|
67
|
+
repeated string required_features = 4;
|
|
68
|
+
repeated string optional_features = 5;
|
|
69
|
+
|
|
70
|
+
optional string writingprogram = 16;
|
|
71
|
+
optional string source = 17; // From the bbox field.
|
|
72
|
+
|
|
73
|
+
/* Tags that allow continuing an Osmosis replication */
|
|
74
|
+
|
|
75
|
+
// Replication timestamp, expressed in seconds since the epoch,
|
|
76
|
+
// otherwise the same value as in the "timestamp=..." field
|
|
77
|
+
// in the state.txt file used by Osmosis.
|
|
78
|
+
optional int64 osmosis_replication_timestamp = 32;
|
|
79
|
+
|
|
80
|
+
// Replication sequence number (sequenceNumber in state.txt).
|
|
81
|
+
optional int64 osmosis_replication_sequence_number = 33;
|
|
82
|
+
|
|
83
|
+
// Replication base URL (from Osmosis' configuration.txt file).
|
|
84
|
+
optional string osmosis_replication_base_url = 34;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
/** The bounding box field in the OSM header. BBOX, as used in the OSM
|
|
89
|
+
header. Units are always in nanodegrees -- they do not obey
|
|
90
|
+
granularity rules. */
|
|
91
|
+
|
|
92
|
+
message HeaderBBox {
|
|
93
|
+
required sint64 left = 1;
|
|
94
|
+
required sint64 right = 2;
|
|
95
|
+
required sint64 top = 3;
|
|
96
|
+
required sint64 bottom = 4;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
///////////////////////////////////////////////////////////////////////
|
|
101
|
+
///////////////////////////////////////////////////////////////////////
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
message PrimitiveBlock {
|
|
105
|
+
required StringTable stringtable = 1;
|
|
106
|
+
repeated PrimitiveGroup primitivegroup = 2;
|
|
107
|
+
|
|
108
|
+
// Granularity, units of nanodegrees, used to store coordinates in this block.
|
|
109
|
+
optional int32 granularity = 17 [default=100];
|
|
110
|
+
|
|
111
|
+
// Offset value between the output coordinates and the granularity grid in units of nanodegrees.
|
|
112
|
+
optional int64 lat_offset = 19 [default=0];
|
|
113
|
+
optional int64 lon_offset = 20 [default=0];
|
|
114
|
+
|
|
115
|
+
// Granularity of dates, normally represented in units of milliseconds since the 1970 epoch.
|
|
116
|
+
optional int32 date_granularity = 18 [default=1000];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Group of OSMPrimitives. All primitives in a group must be the same type.
|
|
120
|
+
message PrimitiveGroup {
|
|
121
|
+
repeated Node nodes = 1;
|
|
122
|
+
optional DenseNodes dense = 2;
|
|
123
|
+
repeated Way ways = 3;
|
|
124
|
+
repeated Relation relations = 4;
|
|
125
|
+
repeated ChangeSet changesets = 5;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
/** String table, contains the common strings in each block.
|
|
130
|
+
|
|
131
|
+
Note that we reserve index '0' as a delimiter, so the entry at that
|
|
132
|
+
index in the table is ALWAYS blank and unused.
|
|
133
|
+
|
|
134
|
+
*/
|
|
135
|
+
message StringTable {
|
|
136
|
+
repeated bytes s = 1;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Optional metadata that may be included into each primitive. */
|
|
140
|
+
message Info {
|
|
141
|
+
optional int32 version = 1 [default = -1];
|
|
142
|
+
optional int64 timestamp = 2;
|
|
143
|
+
optional int64 changeset = 3;
|
|
144
|
+
optional int32 uid = 4;
|
|
145
|
+
optional uint32 user_sid = 5; // String IDs
|
|
146
|
+
|
|
147
|
+
// The visible flag is used to store history information. It indicates that
|
|
148
|
+
// the current object version has been created by a delete operation on the
|
|
149
|
+
// OSM API.
|
|
150
|
+
// When a writer sets this flag, it MUST add a required_features tag with
|
|
151
|
+
// value "HistoricalInformation" to the HeaderBlock.
|
|
152
|
+
// If this flag is not available for some object it MUST be assumed to be
|
|
153
|
+
// true if the file has the required_features tag "HistoricalInformation"
|
|
154
|
+
// set.
|
|
155
|
+
optional bool visible = 6;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Optional metadata that may be included into each primitive. Special dense format used in DenseNodes. */
|
|
159
|
+
message DenseInfo {
|
|
160
|
+
repeated int32 version = 1 [packed = true];
|
|
161
|
+
repeated sint64 timestamp = 2 [packed = true]; // DELTA coded
|
|
162
|
+
repeated sint64 changeset = 3 [packed = true]; // DELTA coded
|
|
163
|
+
repeated sint32 uid = 4 [packed = true]; // DELTA coded
|
|
164
|
+
repeated sint32 user_sid = 5 [packed = true]; // String IDs for usernames. DELTA coded
|
|
165
|
+
|
|
166
|
+
// The visible flag is used to store history information. It indicates that
|
|
167
|
+
// the current object version has been created by a delete operation on the
|
|
168
|
+
// OSM API.
|
|
169
|
+
// When a writer sets this flag, it MUST add a required_features tag with
|
|
170
|
+
// value "HistoricalInformation" to the HeaderBlock.
|
|
171
|
+
// If this flag is not available for some object it MUST be assumed to be
|
|
172
|
+
// true if the file has the required_features tag "HistoricalInformation"
|
|
173
|
+
// set.
|
|
174
|
+
repeated bool visible = 6 [packed = true];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
// This is kept for backwards compatibility but not used anywhere.
|
|
179
|
+
message ChangeSet {
|
|
180
|
+
required int64 id = 1;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
message Node {
|
|
185
|
+
required sint64 id = 1;
|
|
186
|
+
|
|
187
|
+
// Parallel arrays.
|
|
188
|
+
repeated uint32 keys = 2 [packed = true]; // String IDs.
|
|
189
|
+
repeated uint32 vals = 3 [packed = true]; // String IDs.
|
|
190
|
+
|
|
191
|
+
optional Info info = 4; // May be omitted in omitmeta
|
|
192
|
+
|
|
193
|
+
required sint64 lat = 8;
|
|
194
|
+
required sint64 lon = 9;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Used to densly represent a sequence of nodes that do not have any tags.
|
|
198
|
+
|
|
199
|
+
We represent these nodes columnwise as five columns: ID's, lats, and
|
|
200
|
+
lons, all delta coded. When metadata is not omitted,
|
|
201
|
+
|
|
202
|
+
We encode keys & vals for all nodes as a single array of integers
|
|
203
|
+
containing key-stringid and val-stringid, using a stringid of 0 as a
|
|
204
|
+
delimiter between nodes.
|
|
205
|
+
|
|
206
|
+
( (<keyid> <valid>)* '0' )*
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
message DenseNodes {
|
|
210
|
+
repeated sint64 id = 1 [packed = true]; // DELTA coded
|
|
211
|
+
|
|
212
|
+
optional DenseInfo denseinfo = 5;
|
|
213
|
+
|
|
214
|
+
repeated sint64 lat = 8 [packed = true]; // DELTA coded
|
|
215
|
+
repeated sint64 lon = 9 [packed = true]; // DELTA coded
|
|
216
|
+
|
|
217
|
+
// Special packing of keys and vals into one array. May be empty if all nodes in this block are tagless.
|
|
218
|
+
repeated int32 keys_vals = 10 [packed = true];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
message Way {
|
|
223
|
+
required int64 id = 1;
|
|
224
|
+
|
|
225
|
+
// Parallel arrays.
|
|
226
|
+
repeated uint32 keys = 2 [packed = true];
|
|
227
|
+
repeated uint32 vals = 3 [packed = true];
|
|
228
|
+
|
|
229
|
+
optional Info info = 4;
|
|
230
|
+
|
|
231
|
+
repeated sint64 refs = 8 [packed = true]; // DELTA coded
|
|
232
|
+
|
|
233
|
+
// The following two fields are optional. They are only used in a special
|
|
234
|
+
// format where node locations are also added to the ways. This makes the
|
|
235
|
+
// files larger, but allows creating way geometries directly.
|
|
236
|
+
//
|
|
237
|
+
// If this is used, you MUST set the optional_features tag "LocationsOnWays"
|
|
238
|
+
// and the number of values in refs, lat, and lon MUST be the same.
|
|
239
|
+
repeated sint64 lat = 9 [packed = true]; // DELTA coded, optional
|
|
240
|
+
repeated sint64 lon = 10 [packed = true]; // DELTA coded, optional
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
message Relation {
|
|
244
|
+
enum MemberType {
|
|
245
|
+
NODE = 0;
|
|
246
|
+
WAY = 1;
|
|
247
|
+
RELATION = 2;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
required int64 id = 1;
|
|
251
|
+
|
|
252
|
+
// Parallel arrays.
|
|
253
|
+
repeated uint32 keys = 2 [packed = true];
|
|
254
|
+
repeated uint32 vals = 3 [packed = true];
|
|
255
|
+
|
|
256
|
+
optional Info info = 4;
|
|
257
|
+
|
|
258
|
+
// Parallel arrays
|
|
259
|
+
repeated int32 roles_sid = 8 [packed = true]; // This should have been defined as uint32 for consistency, but it is now too late to change it
|
|
260
|
+
repeated sint64 memids = 9 [packed = true]; // DELTA encoded
|
|
261
|
+
repeated MemberType types = 10 [packed = true];
|
|
262
|
+
}
|