login

<     >

2021-08-26 07:49:44 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

map: move cache code to shp.lua

diff --git a/map.lua b/map.lua
index 4d63ecd..7144cab 100644
--- a/map.lua
+++ b/map.lua
@@ -168,13 +168,17 @@ local sep = ":"
 local Frame = {}
 Frame.__index = Frame
 
-function Frame:map(lon, lat)
-    local x, y = self.proj:map(lon, lat)
+function Frame:fit(x, y)
     x = (x - self.bbox.x0) * self.s
     y = self.h - (y - self.bbox.y0) * self.s
     return x, y
 end
 
+function Frame:map(lon, lat)
+    local x, y = self.proj:map(lon, lat)
+    return self:fit(x, y)
+end
+
 function Frame:set_height(h)
     local mw = self.bbox.x1 - self.bbox.x0
     local mh = self.bbox.y1 - self.bbox.y0
@@ -183,15 +187,15 @@ function Frame:set_height(h)
     self.w = math.floor(mw * self.s + 0.5)
 end
 
-function Frame:mapped(polys)
+function Frame:fitted(polys)
     return function()
         local points = polys()
         if points then
             return function()
                 local point = points()
                 if point then
-                    local lat, lon = unpack(point)
-                    local x, y = self:map(lat, lon)
+                    local x, y = unpack(point)
+                    x, y = self:fit(x, y)
                     return {x, y}
                 end
             end
@@ -199,76 +203,20 @@ function Frame:mapped(polys)
     end
 end
 
-function Frame:near(bb)
-    -- not strictly correct, since projected geobbox != 2D rectangle
-    -- just used to limit cache and avoid 16-bit overflows
-    local ns = {}
-    local add_ll = function(lon, lat)
-        local x, y = self:map(lon, lat)
-        table.insert(ns, abs(x-self.w/2))
-        table.insert(ns, abs(y-self.h/2))
-    end
-    add_ll(bb.xmin, bb.ymin)
-    add_ll(bb.xmax, bb.ymin)
-    add_ll(bb.xmax, bb.ymax)
-    add_ll(bb.xmin, bb.ymax)
-    return math.max(unpack(ns)) < math.max(self.w, self.h)*2
-end
-
-function Frame:save_cache(fname, k, sf, filter)
-    local indices = {}
-    for i = 1, #sf.tab do
-        local row = sf.tab[i]
-        local rec = {}
-        for j = 1, #sf.fields do
-            rec[sf.fields[j].name] = row[j]
-        end
-        local key = filter(rec)
-        if key ~= nil then
-            if self:near(sf:read_bbox(i)) then
-                table.insert(indices, {i, key:sub(1, 16)})
-            end
-        end
-    end
-    local cache = io.open(fname, "w")
-    bio.write_beu16(cache, self.w)
-    bio.write_beu16(cache, self.h)
-    bio.write_byte(cache, k)
-    for i = 1, #indices do
-        local index, key = unpack(indices[i])
-        cache:write(key, string.rep("\0", 20 - #key))
-    end
-    cache:write(string.rep("\0", 20)) -- end list of entries
-    for i = 1, #indices do
-        local index, key = unpack(indices[i])
-        local offset = cache:seek()
-        cache:seek("set", i * 20 + 1)
-        bio.write_beu32(cache, offset)
-        cache:seek("set", offset)
-        local bb, lens, polys = sf:read_polygons(index)
-        bio.write_beu16(cache, #lens)
-        for poly in self:mapped(polys) do
-            local ox, oy = unpack(poly())
-            ox, oy = math.floor(ox + 0.5), math.floor(oy + 0.5)
-            bio.write_bei16(cache, ox)
-            bio.write_bei16(cache, oy)
-            local rice = bio.rice_w(cache, k)
-            for point in poly do
-                local x, y = unpack(point)
-                x, y = math.floor(x + 0.5), math.floor(y + 0.5)
-                local dx, dy = x-ox, y-oy
-                if dx ~= 0 or dy ~= 0 then
-                    rice:put_signed(dx)
-                    rice:put_signed(dy)
-                    ox, oy = x, y
+function Frame:mapped(polys)
+    return function()
+        local points = polys()
+        if points then
+            return function()
+                local point = points()
+                if point then
+                    local lat, lon = unpack(point)
+                    local x, y = self:map(lat, lon)
+                    return {x, y}
                 end
             end
-            rice:put_signed(0)
-            rice:put_signed(0)
-            rice:flush()
         end
     end
-    cache:close()
 end
 
 function Frame:save(fname)
@@ -333,66 +281,7 @@ local function load_frame(fname)
     return new_frame(proj, bbox)
 end
 
-local Cache = {}
-Cache.__index = Cache
-
-function Cache:keys()
-    local cache = self.fp
-    local offset = 5
-    return function()
-        cache:seek("set", offset)
-        offset = offset + 20
-        local ckey = cache:read(16)
-        if ckey:byte() ~= 0 then
-            return ckey:sub(1, ckey:find("\0")-1)
-        end
-    end
-end
-
-function Cache:get_polys(key)
-    local cache = self.fp
-    cache:seek("set", 5)
-    local ckey = cache:read(16)
-    local offset = -1
-    while ckey:byte() ~= 0 do
-        if ckey:sub(1, ckey:find("\0")-1) == key then
-            offset = bio.read_beu32(cache)
-            break
-        end
-        cache:seek("cur", 4)
-        ckey = cache:read(16)
-    end
-    assert(offset > 0, ("key '%s' not found in cache"):format(key))
-    cache:seek("set", offset)
-    local npolys = bio.read_beu16(cache)
-    return function()
-        if npolys > 0 then
-            npolys = npolys - 1
-            local ox, oy = bio.read_bei16(cache), bio.read_bei16(cache)
-            local rice = bio.rice_r(cache, self.k)
-            local x, y = 0, 0
-            return function()
-                if x ~= ox or y ~= oy then
-                    x, y = ox, oy
-                    local dx, dy = rice:get_signed(), rice:get_signed()
-                    ox, oy = ox+dx, oy+dy
-                    return {x, y}
-                end
-            end
-        end
-    end
-end
-
-local function load_cache(fname)
-    local self = setmetatable({}, Cache)
-    self.fp = io.open(fname, "r")
-    self.w = bio.read_beu16(self.fp)
-    self.h = bio.read_beu16(self.fp)
-    self.k = bio.read_byte(self.fp)
-    return self
-end
-
 return {
     distance=distance, bbox=bbox, centroid=centroid, Proj=Proj,
-    new_frame=new_frame, load_frame=load_frame, load_cache=load_cache
+    new_frame=new_frame, load_frame=load_frame
 }

diff --git a/shp.lua b/shp.lua
index d6b3f10..956481c 100644
--- a/shp.lua
+++ b/shp.lua
@@ -1,7 +1,19 @@
+local ffi = require "ffi"
 local bit = require "bit"
 
 local bio = require "bio"
 
+ffi.cdef[[
+double hypot(double x, double y);
+double copysign(double x, double y);
+]]
+local copysign = ffi.C.copysign
+
+local function round(x)
+    local i, f = math.modf(x + copysign(0.5, x))
+    return i
+end
+
 local function rtrim(s, c)
     local i = #s
     while s:sub(i, i) == c do
@@ -225,6 +237,61 @@ function SF:close()
     io.close(self.fp)
 end
 
+function SF:save_cache(fname, k, proj, scale, filter)
+    local indices = {}
+    for i = 1, #self.tab do
+        local row = self.tab[i]
+        local rec = {}
+        for j = 1, #self.fields do
+            rec[self.fields[j].name] = row[j]
+        end
+        local key = filter(rec)
+        if key ~= nil then
+            table.insert(indices, {i, key:sub(1, 16)})
+        end
+    end
+    local cache = io.open(fname, "w")
+    bio.write_beu32(cache, round(1000 / scale))
+    bio.write_byte(cache, k)
+    for i = 1, #indices do
+        local index, key = unpack(indices[i])
+        cache:write(key, string.rep("\0", 20 - #key))
+    end
+    cache:write(string.rep("\0", 20)) -- end list of entries
+    for i = 1, #indices do
+        local index, key = unpack(indices[i])
+        local offset = cache:seek()
+        cache:seek("set", i * 20 + 1)
+        bio.write_beu32(cache, offset)
+        cache:seek("set", offset)
+        local bb, lens, polys = self:read_polygons(index)
+        bio.write_beu16(cache, #lens)
+        for poly in polys do
+            local ox, oy = unpack(poly())
+            ox, oy = proj:map(ox, oy)
+            ox, oy = round(ox * scale), round(oy * scale)
+            bio.write_bei16(cache, ox)
+            bio.write_bei16(cache, oy)
+            local rice = bio.rice_w(cache, k)
+            for point in poly do
+                local x, y = unpack(point)
+                x, y = proj:map(x, y)
+                x, y = round(x * scale), round(y * scale)
+                local dx, dy = x-ox, y-oy
+                if dx ~= 0 or dy ~= 0 then
+                    rice:put_signed(dx)
+                    rice:put_signed(dy)
+                    ox, oy = x, y
+                end
+            end
+            rice:put_signed(0)
+            rice:put_signed(0)
+            rice:flush()
+        end
+    end
+    cache:close()
+end
+
 local function open_shapefile(path)
     local self = setmetatable({path=path}, SF)
     self:read_dbf()
@@ -233,4 +300,62 @@ local function open_shapefile(path)
     return self
 end
 
-return {open_shapefile=open_shapefile}
+local Cache = {}
+Cache.__index = Cache
+
+function Cache:keys()
+    local cache = self.fp
+    local offset = 5
+    return function()
+        cache:seek("set", offset)
+        offset = offset + 20
+        local ckey = cache:read(16)
+        if ckey:byte() ~= 0 then
+            return ckey:sub(1, ckey:find("\0")-1)
+        end
+    end
+end
+
+function Cache:get_polys(key)
+    local cache = self.fp
+    cache:seek("set", 5)
+    local ckey = cache:read(16)
+    local offset = -1
+    while ckey:byte() ~= 0 do
+        if ckey:sub(1, ckey:find("\0")-1) == key then
+            offset = bio.read_beu32(cache)
+            break
+        end
+        cache:seek("cur", 4)
+        ckey = cache:read(16)
+    end
+    assert(offset > 0, ("key '%s' not found in cache"):format(key))
+    cache:seek("set", offset)
+    local npolys = bio.read_beu16(cache)
+    return function()
+        if npolys > 0 then
+            npolys = npolys - 1
+            local ox, oy = bio.read_bei16(cache), bio.read_bei16(cache)
+            local rice = bio.rice_r(cache, self.k)
+            local x, y = 0, 0
+            return function()
+                if x ~= ox or y ~= oy then
+                    x, y = ox, oy
+                    local dx, dy = rice:get_signed(), rice:get_signed()
+                    ox, oy = ox+dx, oy+dy
+                    return {x * self.s, y * self.s}
+                end
+            end
+        end
+    end
+end
+
+local function load_cache(fname)
+    local self = setmetatable({}, Cache)
+    self.fp = io.open(fname, "r")
+    self.s = bio.read_beu32(self.fp) / 1000
+    self.k = bio.read_byte(self.fp)
+    return self
+end
+
+return {open_shapefile=open_shapefile, load_cache=load_cache}