@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +144 -0
  3. package/dist/blobs-to-blocks.d.ts +5 -0
  4. package/dist/blobs-to-blocks.js +21 -0
  5. package/dist/blocks-to-pbf.d.ts +16 -0
  6. package/dist/blocks-to-pbf.js +73 -0
  7. package/dist/index.d.ts +8 -0
  8. package/dist/index.js +8 -0
  9. package/dist/pbf-to-blobs.d.ts +6 -0
  10. package/dist/pbf-to-blobs.js +48 -0
  11. package/dist/pbf-to-blocks.d.ts +20 -0
  12. package/dist/pbf-to-blocks.js +53 -0
  13. package/dist/proto/fileformat.d.ts +26 -0
  14. package/dist/proto/fileformat.js +56 -0
  15. package/dist/proto/osmformat.d.ts +91 -0
  16. package/dist/proto/osmformat.js +458 -0
  17. package/dist/spec.d.ts +5 -0
  18. package/dist/spec.js +9 -0
  19. package/dist/utils.d.ts +27 -0
  20. package/dist/utils.js +92 -0
  21. package/package.json +49 -0
  22. package/src/blobs-to-blocks.ts +28 -0
  23. package/src/blocks-to-pbf.ts +98 -0
  24. package/src/index.ts +8 -0
  25. package/src/pbf-to-blobs.ts +56 -0
  26. package/src/pbf-to-blocks.ts +77 -0
  27. package/src/proto/fileformat.proto +68 -0
  28. package/src/proto/fileformat.ts +70 -0
  29. package/src/proto/osmformat.proto +262 -0
  30. package/src/proto/osmformat.ts +488 -0
  31. package/src/spec.ts +10 -0
  32. package/src/utils.ts +90 -0
  33. package/test/blobs-to-blocks.test.ts +73 -0
  34. package/test/helpers.ts +66 -0
  35. package/test/pbf-to-blobs.test.ts +85 -0
  36. package/test/read.bench.ts +42 -0
  37. package/test/read.test.ts +45 -0
  38. package/test/streams.test.ts +92 -0
  39. package/test/utils.bun.test.ts +327 -0
  40. package/test/utils.test.ts +56 -0
  41. package/test/utils.ts +65 -0
  42. package/test/verify-pbf-reading.bun.test.ts +39 -0
  43. package/test/write.test.ts +86 -0
  44. package/tsconfig.json +9 -0
  45. 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,8 @@
1
+ export * from "./blobs-to-blocks"
2
+ export * from "./blocks-to-pbf"
3
+ export * from "./pbf-to-blobs"
4
+ export * from "./pbf-to-blocks"
5
+ export * from "./proto/fileformat"
6
+ export * from "./proto/osmformat"
7
+ export * from "./spec"
8
+ export * from "./utils"
@@ -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
+ }