Something went wrong. Try again.
Zig parser for the OSM PBF file format. jeff.jcollie.pages/osm.zig
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie// SPDX-License-Identifier: MIT
const DenseNodeIterator = @This();
const std = @import("std");
const OSMPBF = @import("proto/OSMPBF.pb.zig");
const Node = @import("Node.zig");
block: *const OSMPBF.PrimitiveBlock,group: *const OSMPBF.PrimitiveGroup,index: usize,kv_index: usize,delta: struct { id: i64, lat: i64, lon: i64, version: i32, timestamp: i64, changeset: i64, uid: i32, user_sid: i32,
pub inline fn acc( self: *@This(), comptime field: std.meta.FieldEnum(@This()), delta: @FieldType(@This(), @tagName(field)), ) @FieldType(@This(), @tagName(field)) { @field(self, @tagName(field)) += delta; return @field(self, @tagName(field)); }},
pub fn init(block: *const OSMPBF.PrimitiveBlock, group: *const OSMPBF.PrimitiveGroup) DenseNodeIterator { return .{ .block = block, .group = group, .index = 0, .kv_index = 0, .delta = .{ .id = 0, .lat = 0, .lon = 0, .version = 0, .timestamp = 0, .changeset = 0, .uid = 0, .user_sid = 0, }, };}
pub fn next(self: *DenseNodeIterator, alloc: std.mem.Allocator) !?*Node { const dense = &(self.group.dense orelse return null); if (self.index >= dense.id.items.len) return null; const tmp = try alloc.create(Node); tmp.* = .{ .id = self.delta.acc(.id, dense.id.items[self.index]), .latitude = .init( self.delta.acc(.lat, dense.lat.items[self.index]), self.block.lat_offset orelse 0, self.block.granularity orelse 100, ), .longitude = .init( self.delta.acc(.lon, dense.lon.items[self.index]), self.block.lon_offset orelse 0, self.block.granularity orelse 100, ), .version = if (dense.denseinfo) |*di| self.delta.acc(.version, di.version.items[self.index]) else null, .timestamp = .init( if (dense.denseinfo) |*di| self.delta.acc(.timestamp, di.timestamp.items[self.index]) else null, self.block.date_granularity orelse 1000, ), .changeset = if (dense.denseinfo) |*di| self.delta.acc(.changeset, di.changeset.items[self.index]) else null, .uid = if (dense.denseinfo) |*di| self.delta.acc(.uid, di.uid.items[self.index]) else null, .user_sid = user_sid: { const di = &(dense.denseinfo orelse break :user_sid null); const st = &(self.block.stringtable orelse break :user_sid null); const user_sid_ = self.delta.acc(.user_sid, di.user_sid.items[self.index]); if (user_sid_ <= 0) return error.StringIndexOutOfRange; break :user_sid try alloc.dupe(u8, st.s.items[@intCast(user_sid_)]); }, .visible = visible: { const di = &(dense.denseinfo orelse break :visible null); if (self.index >= di.visible.items.len) break :visible null; break :visible di.visible.items[self.index]; }, .kv = kv: { var kv: std.StringArrayHashMapUnmanaged([]const u8) = .empty; const st = &(self.block.stringtable orelse break :kv kv); const kvl = dense.keys_vals.items; while (self.kv_index < kvl.len) { const key_index_ = kvl[self.kv_index]; if (key_index_ < 0) return error.InvalidStringIndex; const key_index: usize = @intCast(key_index_); self.kv_index += 1; if (key_index == 0) break; if (key_index >= st.s.items.len) return error.StringIndexOutOfRange; const key = st.s.items[key_index];
const value_index_ = kvl[self.kv_index]; if (value_index_ < 0) return error.StringIndexOutOfRange; const value_index: usize = @intCast(value_index_); self.kv_index += 1; if (value_index >= st.s.items.len) return error.StringIndexOutOfRange; const value = st.s.items[value_index];
const gop = try kv.getOrPut(alloc, key); if (gop.found_existing) { alloc.free(gop.value_ptr.*); } else { gop.key_ptr.* = try alloc.dupe(u8, key); } gop.value_ptr.* = try alloc.dupe(u8, value); } break :kv kv; }, }; self.index += 1;
return tmp;}