Compare commits

..

15 Commits

Author SHA1 Message Date
0f64758e77 off-white background colour 2025-06-02 23:43:29 +01:00
66d1a585fb improve text placement 2025-06-02 23:43:14 +01:00
2906360c2e render road names (badly) 2025-06-02 21:53:26 +01:00
052cd4b578 parse way name as well as points 2025-06-02 21:35:55 +01:00
35f0f3c71e pass zoom level to canvas 2025-06-02 21:15:56 +01:00
05401941b4 draw roads fatter and with edging 2025-06-02 21:15:18 +01:00
8ee10214c8 async tile fetcher
we use cqueues, which is the async framework that lua-http is built
on. we integrate it into the glib event loop rather hackily by calling
the cqueues event stepper ever 20ms from a glib timeout function

overpass has very low rate limits so we handle a 429 response by
sleeping for a random length of time and retrying. This is, also,
a bit of a hack
2025-06-01 20:50:26 +01:00
195e028e22 clobber map-surface when bounds change 2025-05-30 23:34:17 +01:00
acbe27e6e2 draw map once only and copy it to screen in on_draw
This massively reduces cpu usage, however it doesn't yet work
if we've moved far enough that we'd need to fetch new tiles.
2025-05-30 21:24:28 +01:00
2a86a2bfde use register-widget more, fewer arrow widget repaints 2025-05-29 21:13:19 +01:00
f64bfeb7fd invalidate map only when app-state changes 2025-05-29 21:09:24 +01:00
cb0314d1d6 invalidate the map display each time we repaint it
really we should only need to do this when the app-state changes
2025-05-29 21:03:02 +01:00
6e61113366 replace in-memory cache with a persistent json cache
we just store the raw response from overpass
2025-05-29 18:43:48 +01:00
86682a2ad6 fetch enough tiles to cover the display 2025-05-29 18:13:47 +01:00
995880e5a3 wip 2025-05-29 12:48:21 +01:00
5 changed files with 234 additions and 396 deletions

116
README
View File

@ -37,24 +37,6 @@ note that IPS LCD requires as much battery for dark pixels
as light ones, so we get no power saving by colouring only part
of the screen
------
we need to not rotate the text labels
which means we need to redraw whenever the angle changes
which is much more often than when we cross a tile boundary
so caching the bitmap isn't going to work
put map features in a table in app-state keyed on tile key
use the fetch on-complete callback to update app-state directly.
side issue: we could move widget updates from update-app-state
to the timer
maybe we could put the polylines in app-state. if we store
------
@ -65,6 +47,104 @@ random notes follow ...
https://git.syndicate-lang.org/tonyg/squeak-phone/raw/commit/474960ddc665ed445a1f5afb0164fe39057720f9/devices/pine64-pinephone/modem-docs/80545ST10798A_LM940_QMI_Command_Reference_Guide_r3.pdf
1) [done] connect Satellite to qmi-nmea
https://codeberg.org/tpikonen/satellite/src/branch/main/satellite/nmeasource.py#L106
2) [nope] do we need modemmanager? might be required if we want lte data,
but that's up to the "site admin" - we don't need it for biscuit itself
3) we should make saturn give visual feedback while it's loading an
app. How does it know when the app has finished loading? Maybe it
could get told when its window is obscured
4) rename Maps to soemthing that describes its role better. Cockpit?
HUD? (but it's actually H Down D) Head Unit? Navigate? Dashboard? Cluster?
Codriver? Satnav? Binnacle? Launch? Ride? Embark?
5) [all done]
Maps also needs to open the nmea port and parse data from it.
5a) Maps needs to open a fake nmea source for testing
5b) we need a generator of fake nmea data
6) Cockpit
- show [done] speed and [done] direction
- show speed limit
- [done] scroll and [not done] rotate map
- overlay planned route on map
- show next turn
- voice prompts
- when off-course, calculate route to nearest point on course
- fetch planned route from nominated url
(don't want to type the url in, is there a better way to have it check in
with a mothership?)
- save trail (maybe this could be a separate task entirely)
elapsed time: what should it actually show? moving time, I guess
- moving time since when?
should we rename bearing as course in nmea?
perhaps we need a server-side component for route planning
7) think about how to use nfc tags or something for profiles so that
it can recognise when it's attached to bicycle or motorbike
8) use OSM directly?
we can't rotate the map using OsmGpsMap widget because the labels will
be sideways or upside down, so we need something with vectors that we
can rotate
[done] a) we can get data from overpass api as json
[done badly] b) we would like to cache the results, which means some kind of
chunking or tiling so that the json for position a is the same as the
json for position b.
c) at coarser zoom levels, we need to query for fewer objects: either
do it by hand -
- houses
- minor roads
- major roads
or do something smart but complicated like "only return ways that
cover more than 1/16th the length of the tile"
d) render ways according to their type (road/cycleway/path/etc)
e) label the ways
f) async tile fetching
we don't want everything to stop when it's time to fetch a new
row of tiles, what are our options? lua-http is built on cqueues
which is async enough to make my head hurt, but we also need
to make it coexist with the gtk event loop
assumptions:
1) gtk stuff has to happen in the main thread (whatever that is...)
so we can't control it from cqueues because that has its own
threading stuff
2) there will be lots of fds from lua-http, do we really want the
housekeeping of making GLib.io_add_watch for each of them? it looks
like adding a glib source from lgi is not currently practical
https://github.com/lgi-devs/lgi/issues/111
3) if we put http calls inside cq:wrap, that make them background
provided that we call (cq:step 0)
periodically. we could do that in a glib idle function, perhaps.
- The tile fetcher would need to know where to write the data when
eventually it comes back
- need some say to not fetch the same tile 18 times if there's more than
one request for it while a previous request is in progress
----
https://git.syndicate-lang.org/tonyg/squeak-phone/raw/commit/474960ddc665ed445a1f5afb0164fe39057720f9/devices/pine64-pinephone/modem-docs/80545ST10798A_LM940_QMI_Command_Reference_Guide_r3.pdf

View File

@ -31,8 +31,8 @@ let
src = fetchFromGitHub {
owner = "lgi-devs";
repo = "lgi";
rev = "a412921fad445bcfc05a21148722a92ecb93ad06";
hash = "sha256-kZBpH5gcaCNU134Wn6JXAkFELzmiphc1PeCtmN9cagc=";
rev = "e06ad94c8a1c84e3cdb80cee293450a280dfcbc7";
hash = "sha256-VYr/DV1FAyzPe6p6Quc1nmsHup23IAMfz532rL167Q4=";
};
};
rxi-json = callPackage ../rxi-json { lua = lua5_3; };
@ -67,7 +67,6 @@ in stdenv.mkDerivation {
];
GIO_EXTRA_MODULES = [ "${glib-networking.out}/lib/gio/modules" ];
RXI_JSON="${rxi-json}/";
CAIRO_TRACE_SO = "${cairo.out}/lib/cairo/libcairo-trace.so";
makeFlags = [ "PREFIX=${placeholder "out"}" "NAME=${pname}" ];
@ -85,9 +84,4 @@ in stdenv.mkDerivation {
icon = "nix-snowflake"; # "${placeholder "out"}/share/icons/${pname}.svg";
})
];
passthru = {
inherit lua luaPackages;
};
}

View File

@ -1,6 +1,5 @@
; (local { : view } (require :fennel))
(local { : fdopen } (require :posix.stdio))
(local ptime (require :posix.time))
(local cqueues (require :cqueues))
(local nmea (require :nmea))
@ -8,21 +7,6 @@
(import-macros { : define-tests : expect : expect= } :assert)
(local profile
(and (os.getenv "IN_NIX_SHELL")
(require :libluaperf)))
(macro with-timing [label & body]
`(let [before# (ptime.clock_gettime ptime.CLOCK_PROCESS_CPUTIME_ID)
ret# (table.pack (do ,body))]
(let [after# (ptime.clock_gettime ptime.CLOCK_PROCESS_CPUTIME_ID)]
(print ,label (.. (- after#.tv_sec before#.tv_sec) "s "
(// (- after#.tv_nsec before#.tv_nsec) 1000) "us "))
(table.unpack ret#))))
; (with-timing :loop (for [i 1 100] (print i)))
(local {
: Gtk
: Gdk
@ -49,8 +33,8 @@ label.readout {
(os.difftime (os.time localt) (os.time utct))))
(local viewport-width 720)
(local viewport-height 800)
(local map-width 720)
(local map-height 800)
(local tile-size 256)
(fn styles []
@ -62,17 +46,13 @@ label.readout {
)
(style_provider:load_from_data CSS)))
(fn main-quit []
(when profile (profile:stop))
(Gtk.main_quit))
(local window (Gtk.Window {
:title "Map"
:name "toplevel"
:default_width viewport-width
:default_height viewport-height
:default_width map-width
:default_height map-height
:on_destroy main-quit
:on_destroy Gtk.main_quit
}))
(local state-widgets { })
@ -85,10 +65,7 @@ label.readout {
:lat 49
:lon 0
:zoom 17
:course 0 ; direction of travel
:orientation-target 0 ; map rotation angle from north
:orientation-actual 0 ; map rotation angle from north
:tiles {}
:course 22
}
)
@ -98,197 +75,119 @@ label.readout {
(fn map-bounds-tile [tile-x tile-y]
;; we fetch enough tiles around the current location that the screen
;; can be freely rotated without needing to fetch more.
;; when facing north, we have e.g.
;; 720 width is 2.8 * 256 pixel tiles
;; 800 height is 3.125 tiles
;;
;; however:
;; - when the map is rotated 90 degrees we instead have
;; 3.125 tiles horizontally and 2.8 vertically
;; - at e.g a 45 degree angle ... something else?
;;
;; the furthest points visible from the centre of the screen are the
;; corners. So, we draw a circle about the centre which goes
;; through those points. To ensure we have enough tiles to fill the
;; screen at any angle, we fetch every tile that's (partly
;; or entirely) inside that circle
(let [radius (/ (math.sqrt (+ (^ viewport-width 2) (^ viewport-height 2)))
tile-size 2)
min-tile-x (math.floor (- tile-x radius))
max-tile-x (math.floor (+ tile-x radius))
min-tile-y (math.floor (- tile-y radius))
max-tile-y (math.floor (+ tile-y radius))
num-tiles-x (+ 1 (- max-tile-x min-tile-x))
num-tiles-y (+ 1 (- max-tile-y min-tile-y))]
(fn map-bounds [lat lon zoom]
(let [num-tiles-x (+ 1 (math.ceil (/ map-width tile-size)))
num-tiles-y (+ 1 (math.ceil (/ map-height tile-size)))
(tile-x tile-y) (tiles.latlon->tile app-state.lat app-state.lon app-state.zoom)
min-tile-x (math.floor (- tile-x (/ num-tiles-x 2)))
max-tile-x (+ min-tile-x num-tiles-x 4)
min-tile-y (math.floor (- tile-y (/ num-tiles-y 2)))
max-tile-y (+ min-tile-y num-tiles-y 4)]
{
:min { :x min-tile-x :y min-tile-y }
:max { :x max-tile-x :y max-tile-y }
: num-tiles-x
: num-tiles-y
:pixels {
:x (* tile-size num-tiles-x)
:y (* tile-size num-tiles-y)
}
:centre {
:x (/ (+ min-tile-x max-tile-x 1) 2)
:y (/ (+ min-tile-y max-tile-y 1) 2)
}
: num-tiles-x : num-tiles-y
}))
;; diagonal radius is 538 pixels, 2.1 tiles
(let [bounds (map-bounds-tile 65539.5 45014.5)]
(expect= bounds.min {:x 65537 :y 45012})
(expect= bounds.max {:x 65541 :y 45016})
(expect= bounds.centre {:x 65539.5 :y 45014.5}))
(let [bounds (map-bounds-tile 65539.0 45014.0)]
(expect= bounds.min {:x 65536 :y 45011})
(expect= bounds.max {:x 65541 :y 45016})
(expect= bounds.centre {:x 65539 :y 45014})
)
(fn map-bounds [lat lon zoom]
(let [(tile-x tile-y) (tiles.latlon->tile app-state.lat app-state.lon app-state.zoom)]
(map-bounds-tile tile-x tile-y)))
(fn bounds= [a b]
(and (= a.min.x b.min.x)
(= a.min.y b.min.y)
(= a.max.x b.max.x)
(= a.max.y b.max.y)))
(local cq (cqueues.new))
(fn road-width-for [line]
(case (?. line :tags :highway)
:motorway 18
:trunk 17
:primary 16
:secondary 14
:cycleway 4
:footway 4
other 12))
(fn cairo-road-path [g [[sx sy] & points] bounds]
(let [min bounds.min
{ : line_to } g]
(g:move_to (* tile-size (- sx min.x))
(* tile-size (- sy min.y)))
(each [_ [x y] (ipairs points)]
(let [x1 (* tile-size (- x min.x))
y1 (* tile-size (- y min.y))]
(line_to g x1 y1)))))
(fn cairo-roads [g lines bounds]
(g:set_source_rgb 0 0 0)
(fn cairo-roads-path [g lines bounds]
(each [_ line (pairs lines)]
(g:set_line_width (road-width-for line))
(cairo-road-path g line.points bounds )
(g:stroke))
(g:set_source_rgb 1 1 1)
(each [_ line (pairs lines)]
(g:set_line_width (- (road-width-for line) 2))
(cairo-road-path g line.points bounds)
(g:stroke)))
(case line.points
[[sx sy] & more]
(do
(g:save)
(g:move_to (* tile-size (- sx bounds.min.x))
(* tile-size (- sy bounds.min.y)))
(each [_ [x y] (ipairs more)]
(let [x1 (* tile-size (- x bounds.min.x))
y1 (* tile-size (- y bounds.min.y))]
(g:line_to x1 y1)))
(g:stroke)
(g:restore)))))
(fn label-coords [{ : points } bounds]
(var biggest 0)
(var biggest-n 0)
(for [i 2 (# points)]
(let [[x1 y1] (. points (- i 1))
[x2 y2] (. points i)
dist
(+ (* (- x2 x1) (- x2 x1))
(* (- y2 y1) (- y2 y1)))]
(when (>= dist biggest)
(set biggest dist)
(set biggest-n (- i 1)))))
(let [[x y] (. points biggest-n)
[nx ny] (. points (+ 1 biggest-n))
angle (math.atan (- ny y) (- nx x))]
(values
(* tile-size (- x bounds.min.x))
(* tile-size (- y bounds.min.y))
angle)))
(var map-surface nil)
(fn fetch-tiles [bounds tbl zoom]
(for [x bounds.min.x bounds.max.x]
(for [y bounds.min.y bounds.max.y]
(let [k (tiles.name x y zoom)]
(when (not (. tbl k))
(tiles.fetch cq x y zoom #(tset tbl k $1)))))))
(fn draw-onto-map-surface [surface bounds zoom]
(let [{ : num-tiles-x : num-tiles-y } bounds
(fn cairo-the-map [window]
(let [{ : lat : lon : zoom } app-state
{ : num-tiles-x : num-tiles-y &as bounds } (map-bounds lat lon zoom)
road-width 14
lines []]
(for [x bounds.min.x bounds.max.x]
(for [y bounds.min.y bounds.max.y]
(merge lines (or (. app-state.tiles (tiles.name x y zoom)) {}))))
(merge lines (tiles.polylines cq x y zoom))))
(let [seen-road-names {}
g (cairo.Context.create surface)]
(let [map-surface
(window:create_similar_surface
cairo.Content.COLOR
(* tile-size (+ 4 num-tiles-x))
(* tile-size (+ 4 num-tiles-y)))
g (cairo.Context.create map-surface)]
(g:set_source_rgb 0.7 0.8 0.8)
(g:rectangle 0 0 bounds.pixels.x bounds.pixels.y)
(g:rectangle 0 0 (* tile-size num-tiles-x) (* tile-size num-tiles-y))
(g:fill)
(g:translate (+ (// bounds.pixels.x 2)) (+ (// bounds.pixels.y 2)))
(g:rotate (* (/ (- app-state.orientation-target) 180) math.pi))
(g:translate (- (// bounds.pixels.x 2)) (- (// bounds.pixels.y 2)))
(cairo-roads g lines bounds)
(g:set_source_rgb 0 0 0)
(g:set_line_width road-width)
(cairo-roads-path g lines bounds)
(g:set_source_rgb 1 1 1)
(g:set_line_width (- road-width 2))
(cairo-roads-path g lines bounds)
(g:set_source_rgb 0.2 0.2 0.2)
(g:set_font_size (+ road-width 1))
(g:set_font_size (- road-width 3))
(each [_ line (pairs lines)]
(case line.name
n (let [[tx ty angle] line.label-place
ext (g:text_extents n)
w ext.width
h ext.height]
(when (and tx ty (not (. seen-road-names n)))
(let [x (* tile-size (- tx bounds.min.x))
y (* tile-size (- ty bounds.min.y))]
(tset seen-road-names n true)
n (let [(x y angle) (label-coords line bounds)]
(when (and x y)
(g:save)
(g:move_to x y)
(g:rotate angle)
(g:rel_move_to 0 3)
(g:text_path n)
(g:fill)
(g:restore)))))
(g:save)
(g:move_to x y)
(g:rotate angle)
(g:rel_move_to (- (// w 2)) 3)
(g:text_path n)
(g:fill)
(g:restore))))))
surface)))
map-surface)))
(var map-surface nil)
(fn on-osm-draw [widget g]
(when (not map-surface)
(let [window (widget:get_window)]
(set map-surface (cairo-the-map window))))
(let [(tile-x tile-y) (tiles.latlon->tile app-state.lat app-state.lon app-state.zoom)
bounds (map-bounds-tile tile-x tile-y)
offset-x (/ (- viewport-width bounds.pixels.x) 2)
offset-y (/ (- viewport-height bounds.pixels.y) 2)
x-to-centre (- tile-x bounds.centre.x)
y-to-centre (- tile-y bounds.centre.y)
angle (- (/ (* math.pi app-state.orientation-target) 180))
x-to-centre-rot (- (* x-to-centre (math.cos angle))
(* y-to-centre (math.sin angle)))
y-to-centre-rot (+ (* x-to-centre (math.sin angle))
(* y-to-centre (math.cos angle)))
]
bounds (map-bounds tile-x tile-y)
offset-x (- (* tile-size (- tile-x bounds.min.x)) (/ map-width 2))
offset-y (- (* tile-size (- tile-y bounds.min.y)) (/ map-height 2))]
(when (not map-surface)
(let [window (widget:get_window)]
(set map-surface
(doto
(window:create_similar_surface
cairo.Content.COLOR
bounds.pixels.x
bounds.pixels.y)
(draw-onto-map-surface bounds app-state.zoom)))))
(when (not (= app-state.orientation-actual app-state.orientation-target))
(print (- app-state.orientation-actual app-state.orientation-target))
(g:translate (+ (/ viewport-width 2)) (+ (/ viewport-height 2)))
(g:rotate (* (/ (- 360 (- app-state.orientation-actual app-state.orientation-target)) 180) math.pi))
(g:translate (- (/ viewport-width 2)) (- (/ viewport-height 2))))
(g:set_source_surface map-surface
(- offset-x (* tile-size x-to-centre-rot))
(- offset-y (* tile-size y-to-centre-rot)))
(g:set_source_surface map-surface (- offset-x) (- offset-y))
(g:set_operator cairo.Operator.SOURCE)
(g:paint)))
(g:rectangle 0 0 map-width map-height)
(g:fill)))
@ -300,7 +199,7 @@ label.readout {
(register-widget
:osm
(Gtk.DrawingArea {
:width viewport-width :height viewport-height
:width map-width :height map-height
:on_draw on-osm-draw
})))
@ -324,45 +223,26 @@ label.readout {
(expect= (hhmmss (+ 45 (* 60 12) (* 60 60 3))) "3:12:45")
(fn turn-smoothly [from to]
(if (< (math.abs (- from to)) 10) to
(+ from (* 0.05 (- to from)))))
(fn update-app-state [new-vals]
(let [old-state (merge {} app-state)
old-bounds
(let [old-bounds
(map-bounds app-state.lat app-state.lon app-state.zoom)]
(merge app-state new-vals)
(let [bounds
(map-bounds app-state.lat app-state.lon app-state.zoom)]
(when (not (bounds= old-bounds bounds))
(fetch-tiles bounds app-state.tiles app-state.zoom)
(when (or
(not (= old-bounds.min.x bounds.min.x))
(not (= old-bounds.min.y bounds.min.y)))
(set map-surface nil)))
(when (> (math.abs (- app-state.orientation-target app-state.course)) 20)
(set app-state.orientation-target app-state.course)
; (-> state-widgets.rose (: :get_window) (: :invalidate_rect nil))
(set map-surface nil))
(when (not (= app-state.orientation-target app-state.orientation-actual))
(set app-state.orientation-actual
(turn-smoothly app-state.orientation-actual app-state.orientation-target)))
(each [name widget (pairs state-widgets)]
(case name
:speed (widget:set_label
(string.format "%.1f km/h" (* app-state.speed 3.6)))
:osm
(when (not (and ; false
(= old-state.lat app-state.lat)
(= old-state.lon app-state.lon)
(= old-state.orientation-actual
app-state.orientation-actual)
))
(: (widget:get_window) :invalidate_rect nil))
:osm (: (widget:get_window) :invalidate_rect nil)
:arrow (: (widget:get_window) :invalidate_rect nil)
:rose (: (widget:get_window) :invalidate_rect nil)
:time (widget:set_label
(hhmmss (+ utc-offset app-state.time-of-day)))
))))
@ -391,8 +271,7 @@ label.readout {
(fn [self g]
(g:set_source_rgb 0.4 0.0 0.1)
(g:translate (// height 2) (// height 2))
(g:rotate (* (/ (- app-state.course app-state.orientation-actual)
180) math.pi))
(g:rotate (/ (* -2 app-state.course math.pi) 360) )
(g:translate (// height -2) (// height -2))
(g:set_line_width 4)
(g:move_to 10 height)
@ -402,48 +281,6 @@ label.readout {
true)
}))))
(fn deg->rad [degrees]
(* math.pi (/ degrees 180)))
(fn rose []
(let [height 60]
(register-widget
:rose
(Gtk.Label {
:halign Gtk.Align.START
:valign Gtk.Align.START
:width height :height height
:on_draw
(fn [self g]
(g:save)
(g:set_line_width 1)
(g:set_source_rgb 0.4 0.0 0.1)
(g:arc (// height 2) (// height 2) 15
0 (* 2 math.pi))
(g:stroke)
(g:translate (// height 2) (// height 2))
(g:rotate (- (deg->rad app-state.orientation-actual)))
(g:translate (// height -2) (// height -2))
(g:set_line_width 2)
(g:move_to (// height 2) height)
(g:line_to (// height 2) 0)
(g:move_to 10 20)
(g:line_to (// height 2) 0)
(g:line_to (- height 10) 20)
(g:stroke)
(g:set_source_rgb 1 1 0)
(g:move_to (// height -2) (// height -2))
(g:text_path "N")
(g:fill)
(g:restore)
true)
}))))
(local socket-path (or (. arg 1) "/var/run/gnss-share.sock"))
@ -485,34 +322,19 @@ label.readout {
(GLib.timeout_add
GLib.PRIORITY_DEFAULT
100 ; ms
20 ; ms
(fn []
;; run cqueues scheduler
(cq:step 0)
;; for smoother rotation when course changes, repaint more often than
;; once per gnss message
(update-app-state {})
true)
nil nil)
(fn collect-profile []
(GLib.timeout_add
GLib.PRIORITY_DEFAULT
(* 60 1000) main-quit
nil nil)
(print "profiling for 60 seconds")
(profile.start 0))
(window:add
(doto (Gtk.Overlay {})
(: :add (osm-widget))
(: :add_overlay (readouts))
(: :add_overlay (arrow))
(: :add_overlay (rose))
))
(window:show_all)
(styles)
(when (os.getenv "MAP_PROFILE") (collect-profile))
(Gtk:main)

View File

@ -1,49 +1,9 @@
with import <nixpkgs> {};
let
package = pkgs.callPackage ./. {};
fennel-ls1 =
let inherit (pkgs) stdenv pandoc;
in stdenv.mkDerivation {
name = "fennel-ls";
buildInputs = [ package.lua ];
nativeBuildInputs = [ pandoc ];
makeFlags = [ "PREFIX=\\$out" ];
src = fetchFromSourcehut {
owner ="~xerool";
repo ="fennel-ls";
rev = "552b03b983c18d7db5053350711bef9088cc9110";
hash = "sha256-npR10hzPYgDPbKWB5ueq8cXAWYvUEbVVJ1R/EEdCnVY=";
};
};
fennel-ls = pkgs.fennel-ls.override { inherit (package) lua luaPackages; };
luaProfiler =
let
inherit (pkgs) stdenv cmake;
inherit (package) lua;
in stdenv.mkDerivation {
name = "LuaProfiler";
src = fetchFromGitHub {
owner = "Patrick08T";
repo = "LuaProfiler";
rev = "abb989337f6f46b820c9d2eb1e1d339e8f6f3760";
hash = "sha256-43kwZCZZmWD5ens1qCzD7LTg/jKMfcb9Vw/DBiN2sSo=";
};
buildInputs = [ lua ];
installPhase = ''
mkdir -p "$out/lib/lua/${lua.luaversion}"
echo cp /build/source/bin/release/libluaperf.so "$out/lib/lua/${lua.luaversion}"
cp /build/source/bin/release/libluaperf.so "$out/lib/lua/${lua.luaversion}"
'';
nativeBuildInputs = [ cmake ];
};
let package = pkgs.callPackage ./. {};
in
package.overrideAttrs(o: {
nativeBuildInputs = [ fennel-ls luaProfiler flamegraph ] ++ o.nativeBuildInputs;
shellHook = ''
mkdir -p bin
( cd bin && ln -sf `type -p fennel-ls` `type -p fennel` . )
export LUA_CPATH=$(lua -e "print(package.cpath)")\;${luaProfiler}/lib/lua/${package.lua.luaversion}/\?.so
export LUA_CPATH=$(lua -e "print(package.cpath)")
export LUA_PATH=$(lua -e "print(package.path)")\;$RXI_JSON/share/lua/5.3/?.lua
'';
})

View File

@ -46,10 +46,10 @@
(fn overpass [lat lon zoom]
(let [width (/ 360 (^ 2 zoom))
n lat
w lon
s (- lat width)
e (+ lon width)]
n (+ lat width) ;XXX adjust for latitude
w (- lon width)
s lat
e lon]
(->
[
"[out:json];"
@ -59,24 +59,6 @@
]
(table.concat "\n"))))
(fn label-coords [points]
(var biggest 0)
(var biggest-n 0)
(for [i 2 (# points)]
(let [[x1 y1] (. points (- i 1))
[x2 y2] (. points i)
dist
(+ (* (- x2 x1) (- x2 x1))
(* (- y2 y1) (- y2 y1)))]
(when (>= dist biggest)
(set biggest dist)
(set biggest-n (- i 1)))))
(let [[x y] (. points biggest-n)
[nx ny] (. points (+ 1 biggest-n))
angle (math.atan (- ny y) (- nx x))]
[(/ (+ nx x) 2) (/ (+ ny y) 2) angle]))
(fn canvas [elements zoom]
(let [nodes {}
lines {}]
@ -84,21 +66,18 @@
(case e.type
:node (tset nodes e.id e)
:way
(let [points
(icollect [_ nd (ipairs e.nodes)]
(let [node (. nodes nd)
(tx ty) (latlon->tile node.lat node.lon zoom)]
;;(print e.tags.name e.id e.name node.lat node.lon)
[ tx ty ]))]
(tset
lines
e.id
{
:name (?. e :tags :name)
:tags e.tags
:label-place (label-coords points)
: points
}))))
(tset
lines
e.id
{
:name (?. e :tags :name)
:points
(icollect [_ nd (ipairs e.nodes)]
(let [node (. nodes nd)
(tx ty) (latlon->tile node.lat node.lon zoom)]
;;(print e.tags.name e.id e.name node.lat node.lon)
[ tx ty ]))
})))
lines))
@ -129,29 +108,32 @@
;; we'd like to have a way for completed background fetch to signal
;; so that the map can be redrawn
(fn tile-name [x y zoom]
(.. x "_" y "_" zoom))
(fn fetch [cq x y zoom cb]
(let [k (tile-name x y zoom)
(fn polylines [cq x y zoom]
(let [k (.. x "_" y "_" zoom)
pathname (.. "/tmp/tiles/" k ".json")]
(if (file-exists? pathname)
(let [payload (with-open [i (io.open pathname :r)] (i:read "*a"))]
(when (not (= payload ""))
(cb (canvas (. (json.decode payload) :elements) zoom))))
(let [data (with-open [i (io.open pathname :r)] (i:read "*a"))]
(if (= data "")
[]
(canvas (. (json.decode data) :elements) zoom)))
(let [out (io.open pathname :w)]
(cq:wrap (fn []
(print "getting " k)
(var payload nil)
(var json nil)
(with-open [f out]
(while (not payload)
(set payload (unparsed-for-xyz x y zoom))
(when (not payload)
(while (not json)
(set json (unparsed-for-xyz x y zoom))
(when (not json)
(print "sleeping " k)
(cqueues.sleep (math.random 2 6))))
(print "got " k)
(f:write payload)
(cb (canvas (. (json.decode payload) :elements) zoom))
true)))))))
(f:write json)
true)))
[] ; return no lines for now
))))
{ : fetch : latlon->tile :name tile-name }
{ : polylines : latlon->tile }