Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
0f64758e77 | |||
66d1a585fb | |||
2906360c2e | |||
052cd4b578 | |||
35f0f3c71e | |||
05401941b4 | |||
8ee10214c8 | |||
195e028e22 | |||
acbe27e6e2 | |||
2a86a2bfde | |||
f64bfeb7fd | |||
cb0314d1d6 | |||
6e61113366 | |||
86682a2ad6 | |||
995880e5a3 |
116
README
116
README
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
'';
|
||||
})
|
||||
|
@ -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 }
|
||||
|
Loading…
Reference in New Issue
Block a user