lifo-advtrains_netmapper/geojson.lua

306 lines
7.5 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

-- advtrains track map generator
-- Usage: lua main.lua path/to/world
-- Viewport maximum coordinate in all directions
local maxc = 5000
-- embed an image called "world.png"
local wimg = false
-- image file resolution (not world resolution!)
local wimresx = 3000
local wimresy = 3000
-- one pixel is ... nodes
local wimscale = 4
-- y ranges and line colors
-- Minimum y level drawn
local drminy = -30
-- Color of min y level
local colminy = {r=0, g=0, b=255}
-- Intermediate color
local colmedy = {r=255, g=0, b=0}
-- Maximum y level drawn
local drmaxy = 80
-- Color of max y level
local colmaxy = {r=255, g=255, b=0}
datapath = (arg[1] or "").."/"
--Constant for maximum connection value/division of the circle
AT_CMAX = 16
advtrains = {}
minetest = {}
core = minetest
--table for track nodes/connections
trackconns = {}
-- math library seems to be missing this function
math.hypot = function(a,b) return math.sqrt(a*a + b*b) end
-- need to declare this for trackdefs
function attrans(str) return str end
-- pos to string
local function pts(pos)
return pos.x .. "," .. pos.y .. "," .. pos.z
end
--Advtrains dump (special treatment of pos and sigd)
function atdump(t, intend)
local str
if type(t)=="table" then
if t.x and t.y and t.z then
str=minetest.pos_to_string(t)
elseif t.p and t.s then -- interlocking sigd
str="S["..minetest.pos_to_string(t.p).."/"..t.s.."]"
else
str="{"
local intd = (intend or "") .. " "
for k,v in pairs(t) do
if type(k)~="string" or not string.match(k, "^path[_]?") then
-- do not print anything path-related
str = str .. "\n" .. intd .. atdump(k, intd) .. " = " ..atdump(v, intd)
end
end
str = str .. "\n" .. (intend or "") .. "}"
end
elseif type(t)=="boolean" then
if t then
str="true"
else
str="false"
end
elseif type(t)=="function" then
str="<function>"
elseif type(t)=="userdata" then
str="<userdata>"
else
str=""..t
end
return str
end
dofile("vector.lua")
dofile("serialize.lua")
dofile("helpers.lua")
dofile("tracks.lua")
dofile("track_defs.lua")
dofile("nodedb.lua")
function parse_args(argv)
local i = 1
local no_trains = false
local datapath, mappath, worldimage
while i <= #argv do
local a = argv[i]
if (a == "-m") or (a == "--map-file") then
-- only draw trains requires specifying an already drawn file
i = i+1
if not argv[i] then
error(("missing filename after `%s'"):format(a))
end
mappath = argv[i]
elseif (a == "-t") or (a == "--no-trains") then
-- do not draw trains
no_trains = true
elseif (a == "-w") or (a == "--world-image") then
-- overlay over world image
i = i+1
if not argv[i] then
error(("missing filename after `%s'"):format(a))
end
worldimage = argv[i]
else
datapath = a
end
i = i + 1
end
return datapath, mappath, no_trains, worldimage
end
datapath, mappath, no_trains, worldimage = parse_args(arg)
-- Load saves
local file, err = io.open(datapath.."advtrains_trains", "r")
local tbl = minetest.deserialize(file:read("*a"))
if type(tbl) ~= "table" then
error("Trains file: not a table")
end
advtrains.trains = tbl
file:close()
--ndb contains the defs, while ndb2 is the actual contents
file, err = io.open(datapath.."advtrains_ndb", "r")
tbl = minetest.deserialize(file:read("*a"))
if type(tbl) ~= "table" then
error("Node database file: not a table")
end
advtrains.ndb.load_data(tbl)
file:close()
-- open svg file
local svgfile = io.open(datapath.."trains.json", "w")
if mappath then
mapfile = io.open(mappath, "r")
cont = mapfile:read("*a"):sub(1, -7) -- remove </svg> end tag
svgfile:write(cont)
else
svgfile:write([[
[
]])
end
-- everything set up. Start generating an SVG
-- All nodes that have been fit into a polyline are removed from the NDB, in order to not draw them again.
-- "Restart points" for the breadth-first traverser (set when more than 2 conns present)
-- {pos = <position>, connid = <int>, conn = <conndef>}
-- Note that the node at "pos" is already deleted from the NDB at the time of recall, therefore "conn" is specified
local bfs_rsp = {}
local ndb_nodes_handled = 0
-- Points of the current polyline. Inserted as xyz vectors, maybe we'll use the y value one day
local current_polyline = {}
-- Traverser function from interlocking, highly modified
local function gen_rsp_polyline(rsp)
-- trick a bit
local pos, connid, conns = rsp.pos, 1, {rsp.conn}
current_polyline[#current_polyline+1] = pos
while true do
local adj_pos, adj_connid, conn_idx, nextrail_y, next_conns = advtrains.get_adjacent_rail(pos, conns, connid)
if not adj_pos then
-- proceed one more node, for seamless turnout transitions
current_polyline[#current_polyline+1] = advtrains.pos_add_dir(pos, conns[connid].c)
return
end
-- continue traversing
local conn_mainbranch
for nconnid, nconn in ipairs(next_conns) do
if adj_connid ~= nconnid then
if not conn_mainbranch then
--use the first one found to continue
conn_mainbranch = nconnid
--writec(nconnid.." nconn mainbranch")
else
-- insert bfs reminders for other conns
table.insert(bfs_rsp, {pos = adj_pos, connid = nconnid, conn = nconn})
--writec(nconnid.." nconn bfs")
end
end
end
-- save in polyline and delete from ndb
--writec("Saved pos: "..pts(adj_pos).." mainbranch cont "..conn_mainbranch.." nextconns "..atdump(next_conns))
current_polyline[#current_polyline+1] = adj_pos
advtrains.ndb.clear(adj_pos)
ndb_nodes_handled = ndb_nodes_handled + 1
pos, connid, conns = adj_pos, conn_mainbranch, next_conns
end
end
plcnt = 0
local function poly_angle(l, e)
local dx = (e.x - l.x)
local dz = (e.z - l.z)
return math.deg(math.atan2(dz, dx))
end
local function polyline_write(pl)
local str = {}
table.insert(str, '{"type":"Feature","geometry":{"type":"LineString","coordinates":[')
local i
local e = nil
local l = nil
local last_angle = -1000
local lastcf = p1y
local lastldir = {x=0, y=0}
local consider = 0
--print("new polyline\n")
for i=1,#pl do
local first = true
consider = 0
e = pl[i]
-- print("point " .. e.x .. "x" .. e.z .. "x" .. e.y)
if (l ~= nil) then
local angle = poly_angle(l, e)
if (angle ~= last_angle) then
table.insert(str, "[".. l.x .. "," .. (l.z) .. "],")
last_angle = angle
end
end
l = e
end
if (e ~= nil) then
table.insert(str, "[".. l.x .. "," .. (l.z) .. "]")
end
table.insert(str, ']},"properties":{"name":""}},\n')
svgfile:write(table.concat(str))
plcnt = plcnt + 1
end
-- while there are entries in the nodedb
-- 1. find a starting point
if not mappath then
local stpos, conns = advtrains.ndb.mapper_find_starting_point()
while stpos do
--print("Restart at position "..pts(stpos))
for connid, conn in ipairs(conns) do
table.insert(bfs_rsp, {pos = stpos, connid = connid, conn = conn})
end
advtrains.ndb.clear(stpos)
-- 2. while there are BFS entries
while #bfs_rsp > 0 do
-- make polylines
local current_rsp = bfs_rsp[#bfs_rsp]
bfs_rsp[#bfs_rsp] = nil
--print("Starting polyline at "..pts(current_rsp.pos).."/"..current_rsp.connid)
current_polyline = {}
gen_rsp_polyline(current_rsp)
polyline_write(current_polyline)
--break
io.write("Progress ", ndb_nodes_handled, "+", ndb_nodes_notrack, "/", ndb_nodes_total, "=", math.floor(((ndb_nodes_handled+ndb_nodes_notrack)/ndb_nodes_total)*100), "%\r")
end
stpos, conns = advtrains.ndb.mapper_find_starting_point()
--break
end
end
svgfile:write("{}]")
svgfile:close()
print("\nWrote",plcnt,"polylines. Processed", ndb_nodes_handled, "track,",ndb_nodes_notrack, "non-track nodes out of", ndb_nodes_total)