From f8b8256962f2d4b31b66297873b1a47a000591b5 Mon Sep 17 00:00:00 2001 From: Nekkowe Date: Tue, 6 Oct 2026 01:20:08 +0200 Subject: [PATCH 1/6] precise locations of bridges, down from world tiles to midmap tiles --- gui/export-world-map.lua | 1 - internal/export-world-map/export-roads.lua | 50 +++++++++++++++++----- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/gui/export-world-map.lua b/gui/export-world-map.lua index abe7de5d2..73bd92e58 100644 --- a/gui/export-world-map.lua +++ b/gui/export-world-map.lua @@ -118,7 +118,6 @@ local SELECTED_ICON = dfhack.pen.parse{ch=string.char(251), fg=COLOR_LIGHTGREEN} local DISABLED_ICON = dfhack.pen.parse{ch='x', fg=COLOR_RED} function ExportMap:getChoices() - print("getChoices") local choices = {} for _, export in ipairs(exports) do table.insert(choices, { diff --git a/internal/export-world-map/export-roads.lua b/internal/export-world-map/export-roads.lua index ac738eb15..37858fde4 100644 --- a/internal/export-world-map/export-roads.lua +++ b/internal/export-world-map/export-roads.lua @@ -20,26 +20,54 @@ local function getConstructionAndGeometryType(construction) end end +local function listBridgeSquaresById() + local squares = {} + + local tile_array_width = df.global.world.world_data.constructions.width + local tile_array_height = df.global.world.world_data.constructions.height + + for i = 1, tile_array_width do + for j = 1, tile_array_height do + for _, square in ipairs(df.global.world.world_data.constructions.map[i - 1]:_displace(j - 1).square) do + if df.world_construction_square_bridgest:is_instance(square) then + squares[square.construction_id] = square + end + end + end + end + + return squares +end + local function gatherFeatures() local features = {} + + local bridge_squares_by_id = listBridgeSquaresById() + for _, construction in ipairs(df.global.world.world_data.constructions.list) do local type, geometry_type, subtype = getConstructionAndGeometryType(construction) local coordinates if geometry_type == "Point" then - -- only world tile - not sure where to get region tile coordinates from - -- perhaps it gets them from neighbouring roads? + -- dwarf fortress doesn't populate square_obj for bridges, only for roads and tunnels. + -- but all world tiles are listed in df.global.world.world_data.constructions.map (not list), + -- a 2D array that has to be iterated over with _displace() + local region_x = construction.square_pos["x"][0] + local region_y = construction.square_pos["y"][0] + local midmap_x = bridge_squares_by_id[construction.id].embark_x[0] + local midmap_y = bridge_squares_by_id[construction.id].embark_y[0] + coordinates = { - construction.square_pos["x"][0]*768, - -(construction.square_pos["y"][0]*768) + region_x * 768 + midmap_x * 48 + 24, + -(region_y * 768 + midmap_y * 48 + 24) } else coordinates = {} - for _,square_obj in ipairs(construction.square_obj) do - for i=0,#square_obj.embark_x-1 do + for _, square_obj in ipairs(construction.square_obj) do + for i = 0, #square_obj.embark_x - 1 do table.insert(coordinates, { - square_obj.region_pos["x"]*768+square_obj.embark_x[i]*48+24, - -(square_obj.region_pos["y"]*768+square_obj.embark_y[i]*48+24) + square_obj.region_pos["x"] * 768 + square_obj.embark_x[i] * 48 + 24, + -(square_obj.region_pos["y"] * 768 + square_obj.embark_y[i] * 48 + 24) }) end end @@ -49,8 +77,8 @@ local function gatherFeatures() type = "Feature", properties = { id = construction.id, - name_df = dfhack.df2utf(dfhack.translation.translateName(construction["name"],false)), - name_en = dfhack.df2utf(dfhack.translation.translateName(construction["name"],true)), + name_df = dfhack.df2utf(dfhack.translation.translateName(construction["name"], false)), + name_en = dfhack.df2utf(dfhack.translation.translateName(construction["name"], true)), construction_type = type, construction_subtype = subtype, }, @@ -68,7 +96,7 @@ function export(by_world, by_date) return function() json.encode_file( { type = "FeatureCollection", features = gatherFeatures() }, - util.getOutputFolder(by_world, by_date).."roads.geojson" + util.getOutputFolder(by_world, by_date) .. "roads.geojson" ) end end From f2be9422e20f2ab4abbd670725e34ec239c9ec41 Mon Sep 17 00:00:00 2001 From: Nekkowe Date: Tue, 6 Oct 2026 01:24:47 +0200 Subject: [PATCH 2/6] changelog entry --- changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.txt b/changelog.txt index 622273520..e31af8acf 100644 --- a/changelog.txt +++ b/changelog.txt @@ -19,6 +19,8 @@ Template for new versions: ## Fixes ## Misc Improvements +export-world-map: narrowing the coordinates of bridges down more precisely + ## Removed From 39583b0df944a0b52f6b9eebe8c589ec20f240ac Mon Sep 17 00:00:00 2001 From: Nekkowe Date: Tue, 6 Oct 2026 02:13:44 +0200 Subject: [PATCH 3/6] specify that it's the gui version of the script --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index e31af8acf..fb078d2bb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -19,7 +19,7 @@ Template for new versions: ## Fixes ## Misc Improvements -export-world-map: narrowing the coordinates of bridges down more precisely +gui/export-world-map: narrowing the coordinates of bridges down more precisely ## Removed From b9aab8dec1332ea67182731ea592cf547dd0bbc9 Mon Sep 17 00:00:00 2001 From: Nekkowe Date: Tue, 6 Oct 2026 12:53:13 +0200 Subject: [PATCH 4/6] export bridge and road materials --- changelog.txt | 3 +- internal/export-world-map/export-roads.lua | 34 +++++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/changelog.txt b/changelog.txt index fb078d2bb..a4e6539ec 100644 --- a/changelog.txt +++ b/changelog.txt @@ -19,7 +19,8 @@ Template for new versions: ## Fixes ## Misc Improvements -gui/export-world-map: narrowing the coordinates of bridges down more precisely +gui/export-world-map: more precise bridge coordinates +gui/export-world-map: export of bridge and road materials ## Removed diff --git a/internal/export-world-map/export-roads.lua b/internal/export-world-map/export-roads.lua index 37858fde4..eac066bcc 100644 --- a/internal/export-world-map/export-roads.lua +++ b/internal/export-world-map/export-roads.lua @@ -2,19 +2,38 @@ local json = require('json') local util = reqscript('internal/export-world-map/util') -local function getConstructionAndGeometryType(construction) +local function getConstructionAndGeometryType(construction, bridge_squares_by_id) if df.world_construction_roadst:is_instance(construction) then + local square = construction.square_obj[0] -- df populates square_obj for roads and tunnels (only) local subtype = "" - if df.item_type[construction.square_obj[0]["item_type"]] == "BLOCKS" then + local material = "" + + if df.item_type[square.item_type] == "BLOCKS" then subtype = "paved" - else + material = dfhack.matinfo.decode(square.mat_type, square.mat_index):toString() + elseif df.item_type[square.item_type] == "NONE" then subtype = "dirt" end - return "road", "LineString", subtype + + return "road", "LineString", subtype, material elseif df.world_construction_bridgest:is_instance(construction) then - return "bridge", "Point", "" + local square = bridge_squares_by_id[construction.id] -- df does NOT populate square_obj for bridges + local subtype = "" + local material = "" + + if df.item_type[square.item_type] == "WOOD" then + subtype = "wooden" + material = dfhack.matinfo.decode(square.mat_type, square.mat_index):toString() + elseif df.item_type[square.item_type] == "BLOCKS" then + subtype = "stone" + material = dfhack.matinfo.decode(square.mat_type, square.mat_index):toString() + end + + return "bridge", "Point", subtype, material elseif df.world_construction_tunnelst:is_instance(construction) then - return "tunnel", "LineString", "" + -- df assigns world construction squares to tunnels like it does to roads, + -- but tunnel squares have nothing besides positional data: no material info, no nothing. + return "tunnel", "LineString", "", "" else qerror("unknown construction type") end @@ -45,7 +64,7 @@ local function gatherFeatures() local bridge_squares_by_id = listBridgeSquaresById() for _, construction in ipairs(df.global.world.world_data.constructions.list) do - local type, geometry_type, subtype = getConstructionAndGeometryType(construction) + local type, geometry_type, subtype, material = getConstructionAndGeometryType(construction, bridge_squares_by_id) local coordinates if geometry_type == "Point" then @@ -81,6 +100,7 @@ local function gatherFeatures() name_en = dfhack.df2utf(dfhack.translation.translateName(construction["name"], true)), construction_type = type, construction_subtype = subtype, + construction_material = material, }, geometry = { type = geometry_type, From 4844cf10f302d255ce7c29da9071071393356229 Mon Sep 17 00:00:00 2001 From: Nekkowe Date: Tue, 6 Oct 2026 22:12:41 +0200 Subject: [PATCH 5/6] addressing the first of the review comments --- internal/export-world-map/export-roads.lua | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/internal/export-world-map/export-roads.lua b/internal/export-world-map/export-roads.lua index eac066bcc..0a47abd75 100644 --- a/internal/export-world-map/export-roads.lua +++ b/internal/export-world-map/export-roads.lua @@ -2,6 +2,8 @@ local json = require('json') local util = reqscript('internal/export-world-map/util') +local constructions = df.global.world.world_data.constructions + local function getConstructionAndGeometryType(construction, bridge_squares_by_id) if df.world_construction_roadst:is_instance(construction) then local square = construction.square_obj[0] -- df populates square_obj for roads and tunnels (only) @@ -42,12 +44,12 @@ end local function listBridgeSquaresById() local squares = {} - local tile_array_width = df.global.world.world_data.constructions.width - local tile_array_height = df.global.world.world_data.constructions.height + local tile_array_width = constructions.width + local tile_array_height = constructions.height - for i = 1, tile_array_width do - for j = 1, tile_array_height do - for _, square in ipairs(df.global.world.world_data.constructions.map[i - 1]:_displace(j - 1).square) do + for i = 0, (tile_array_width - 1) do + for j = 0, (tile_array_height - 1) do + for _, square in ipairs(constructions.map[i]:_displace(j).square) do if df.world_construction_square_bridgest:is_instance(square) then squares[square.construction_id] = square end @@ -63,7 +65,7 @@ local function gatherFeatures() local bridge_squares_by_id = listBridgeSquaresById() - for _, construction in ipairs(df.global.world.world_data.constructions.list) do + for _, construction in ipairs(constructions.list) do local type, geometry_type, subtype, material = getConstructionAndGeometryType(construction, bridge_squares_by_id) local coordinates @@ -83,10 +85,10 @@ local function gatherFeatures() else coordinates = {} for _, square_obj in ipairs(construction.square_obj) do - for i = 0, #square_obj.embark_x - 1 do + for i = 0, (#square_obj.embark_x - 1) do table.insert(coordinates, { - square_obj.region_pos["x"] * 768 + square_obj.embark_x[i] * 48 + 24, - -(square_obj.region_pos["y"] * 768 + square_obj.embark_y[i] * 48 + 24) + square_obj.region_pos.x * 768 + square_obj.embark_x[i] * 48 + 24, + -(square_obj.region_pos.y * 768 + square_obj.embark_y[i] * 48 + 24) }) end end From 2e11e66c2bc12f286d71a91820491d86532a37b8 Mon Sep 17 00:00:00 2001 From: Nekkowe Date: Tue, 6 Oct 2026 22:29:18 +0200 Subject: [PATCH 6/6] different method for type check --- internal/export-world-map/export-roads.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/export-world-map/export-roads.lua b/internal/export-world-map/export-roads.lua index 0a47abd75..ac6f1c8f5 100644 --- a/internal/export-world-map/export-roads.lua +++ b/internal/export-world-map/export-roads.lua @@ -5,7 +5,7 @@ local util = reqscript('internal/export-world-map/util') local constructions = df.global.world.world_data.constructions local function getConstructionAndGeometryType(construction, bridge_squares_by_id) - if df.world_construction_roadst:is_instance(construction) then + if construction:getType() == df.world_construction_type.ROAD then local square = construction.square_obj[0] -- df populates square_obj for roads and tunnels (only) local subtype = "" local material = "" @@ -18,7 +18,7 @@ local function getConstructionAndGeometryType(construction, bridge_squares_by_id end return "road", "LineString", subtype, material - elseif df.world_construction_bridgest:is_instance(construction) then + elseif construction:getType() == df.world_construction_type.BRIDGE then local square = bridge_squares_by_id[construction.id] -- df does NOT populate square_obj for bridges local subtype = "" local material = "" @@ -32,7 +32,7 @@ local function getConstructionAndGeometryType(construction, bridge_squares_by_id end return "bridge", "Point", subtype, material - elseif df.world_construction_tunnelst:is_instance(construction) then + elseif construction:getType() == df.world_construction_type.TUNNEL then -- df assigns world construction squares to tunnels like it does to roads, -- but tunnel squares have nothing besides positional data: no material info, no nothing. return "tunnel", "LineString", "", "" @@ -85,7 +85,7 @@ local function gatherFeatures() else coordinates = {} for _, square_obj in ipairs(construction.square_obj) do - for i = 0, (#square_obj.embark_x - 1) do + for i = 0, #square_obj.embark_x - 1 do table.insert(coordinates, { square_obj.region_pos.x * 768 + square_obj.embark_x[i] * 48 + 24, -(square_obj.region_pos.y * 768 + square_obj.embark_y[i] * 48 + 24)