Something went wrong. Try again.
htop for your network: a real-time IP traffic monitor for the terminal, ported to Zig from iptop
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154// SPDX-FileCopyrightText: © 2026 Francesc Muñoz// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! Writes `src/worldmap_data.zig`: the country polygons of a GeoJSON file//! rasterized into a land bitmap, one dot per braille dot of the map at its//! largest, each land dot tagged with the country it is in.//!//! zig build worldmap -- countries.geo.json all.json > src/worldmap_data.zig//!//! The polygons are https://github.com/johan/world.geo.json's//! `countries.geo.json`, which names each country by its ISO 3166-1 alpha-3//! code, and the alpha-3 to alpha-2 table is the `all/all.json` of//! https://github.com/lukes/ISO-3166-Countries-with-Regional-Codes. They are//! read with zig-geojson, and a dot is land when zig-geo puts it inside a//! polygon -- inside its outer ring and outside its holes, so that Lesotho is//! not counted as South Africa.
const std = @import("std");const geojson = @import("geojson");
const width = 180;const height = 72;const lat_top = 84;const lat_bottom = -60;
/// A country's outline, with the box around it to rule most dots out before/// the polygon test.const Area = struct { geometry: geojson.Geometry, box: geojson.geo.BoundingBox, /// Index into the country table, plus one; zero for a polygon whose /// country has no alpha-2 code. country: u8,};
pub fn main(init: std.process.Init) !void { const io = init.io; var arena: std.heap.ArenaAllocator = .init(init.gpa); defer arena.deinit(); const gpa = arena.allocator();
var args = try init.minimal.args.iterateAllocator(gpa); _ = args.skip(); const geo_path = args.next() orelse return error.MissingGeoJson; const iso_path = args.next() orelse return error.MissingIsoTable;
const cwd: std.Io.Dir = .cwd(); const geo_text = try cwd.readFileAlloc(io, geo_path, gpa, .limited(64 << 20)); const iso_text = try cwd.readFileAlloc(io, iso_path, gpa, .limited(64 << 20)); const options: std.json.ParseOptions = .{ .ignore_unknown_fields = true }; const collection = try std.json.parseFromSliceLeaky(geojson.FeatureCollection(std.json.Value), gpa, geo_text, options); const Iso = struct { @"alpha-2": []const u8, @"alpha-3": []const u8 }; const iso = try std.json.parseFromSliceLeaky([]const Iso, gpa, iso_text, options);
var alpha3to2: std.StringHashMapUnmanaged([]const u8) = .empty; for (iso) |c| try alpha3to2.put(gpa, c.@"alpha-3", c.@"alpha-2");
// The country table, in the order countries are first met. A dot goes // to the first feature that contains it, so where outlines overlap the // file's order decides, as it always has. var countries: std.ArrayList([]const u8) = .empty; var areas: std.ArrayList(Area) = .empty; for (collection.features) |feature| { const geometry = feature.geometry orelse continue; const box = geometry.boundingBox() orelse continue; const id = if (feature.id) |i| switch (i) { .string => |str| str, else => "", } else ""; const code = alpha3to2.get(id) orelse ""; const country: u8 = if (code.len == 0) 0 else blk: { for (countries.items, 0..) |c, i| { if (std.mem.eql(u8, c, code)) break :blk @intCast(i + 1); } try countries.append(gpa, code); break :blk @intCast(countries.items.len); }; try areas.append(gpa, .{ .geometry = geometry, .box = box, .country = country }); }
var land: [height][width]u8 = undefined; var owner: [height][width]u8 = undefined; for (0..height) |y| { const yf: f64 = @floatFromInt(y); const lat = lat_top - (yf + 0.5) / height * (lat_top - lat_bottom); for (0..width) |x| { const xf: f64 = @floatFromInt(x); const lon = -180 + (xf + 0.5) / width * 360; land[y][x] = '0'; owner[y][x] = 0; const here: geojson.Position = .{ .longitude = lon, .latitude = lat }; for (areas.items) |a| { if (a.box.contains(here) and a.geometry.contains(here)) { land[y][x] = '1'; owner[y][x] = a.country; break; } } } }
var buffer: [4096]u8 = undefined; var stdout = std.Io.File.stdout().writer(io, &buffer); const w = &stdout.interface; try w.print( \\// SPDX-FileCopyrightText: © 2026 Francesc Muñoz \\// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us> \\// SPDX-License-Identifier: MIT \\ \\//! Generated by `tools/worldmap.zig`; do not edit by hand. \\//! \\//! An equirectangular land bitmap, longitude -180 to 180 left to right and \\//! latitude {d} to {d} top to bottom. \\ \\pub const width = {d}; \\pub const height = {d}; \\pub const lat_top = {d}; \\pub const lat_bottom = {d}; \\ \\/// One row per line of latitude, `1` for land and `0` for water. \\pub const land: [height]*const [width]u8 = .{{ \\ , .{ lat_top, lat_bottom, width, height, lat_top, lat_bottom }); for (land) |row| try w.print(" \"{s}\",\n", .{row}); try w.writeAll( \\}; \\ \\/// ISO 3166-1 alpha-2 codes, in the order `owner` numbers them from one. \\pub const countries = [_]*const [2]u8{ \\ ); for (countries.items, 0..) |c, i| { try w.print("{s}\"{s}\",", .{ if (i % 16 == 0) " " else " ", c }); if (i % 16 == 15) try w.writeAll("\n"); } try w.writeAll( \\ \\}; \\ \\/// The country each land dot is in, as an index into `countries` plus one; \\/// zero for water and for land no country claims. \\pub const owner: [height][width]u8 = .{ \\ ); for (owner) |row| { try w.writeAll(" .{ "); for (row, 0..) |o, i| try w.print("{s}{d}", .{ if (i == 0) "" else ", ", o }); try w.writeAll(" },\n"); } try w.writeAll("};\n"); try w.flush();}