Compare commits
16 Commits
e728052bb6
...
53e377cabd
Author | SHA1 | Date | |
---|---|---|---|
53e377cabd | |||
56f070ceee | |||
8ec64bb1b0 | |||
8c51113d6e | |||
ee286a0aef | |||
2d2f592d73 | |||
1ee968d3a2 | |||
f275190538 | |||
0b22a7e538 | |||
beca254bf6 | |||
4c2bed5ef0 | |||
fef52f812a | |||
cdd7ea3a94 | |||
63e592d33d | |||
e5e1060c3f | |||
c53120ab2a |
@ -84,4 +84,9 @@ in stdenv.mkDerivation {
|
|||||||
icon = "nix-snowflake"; # "${placeholder "out"}/share/icons/${pname}.svg";
|
icon = "nix-snowflake"; # "${placeholder "out"}/share/icons/${pname}.svg";
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
passthru = {
|
||||||
|
inherit lua luaPackages;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,8 @@ label.readout {
|
|||||||
(os.difftime (os.time localt) (os.time utct))))
|
(os.difftime (os.time localt) (os.time utct))))
|
||||||
|
|
||||||
|
|
||||||
(local map-width 720)
|
(local viewport-width 720)
|
||||||
(local map-height 800)
|
(local viewport-height 800)
|
||||||
(local tile-size 256)
|
(local tile-size 256)
|
||||||
|
|
||||||
(fn styles []
|
(fn styles []
|
||||||
@ -49,8 +49,8 @@ label.readout {
|
|||||||
(local window (Gtk.Window {
|
(local window (Gtk.Window {
|
||||||
:title "Map"
|
:title "Map"
|
||||||
:name "toplevel"
|
:name "toplevel"
|
||||||
:default_width map-width
|
:default_width viewport-width
|
||||||
:default_height map-height
|
:default_height viewport-height
|
||||||
|
|
||||||
:on_destroy Gtk.main_quit
|
:on_destroy Gtk.main_quit
|
||||||
}))
|
}))
|
||||||
@ -65,7 +65,8 @@ label.readout {
|
|||||||
:lat 49
|
:lat 49
|
||||||
:lon 0
|
:lon 0
|
||||||
:zoom 17
|
:zoom 17
|
||||||
:course 22
|
:course 0
|
||||||
|
:smooth-course 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -75,36 +76,95 @@ label.readout {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
(fn map-bounds [lat lon zoom]
|
(fn map-bounds-tile [tile-x tile-y]
|
||||||
(let [num-tiles-x (+ 1 (math.ceil (/ map-width tile-size)))
|
;; we fetch enough tiles around the current location that the screen
|
||||||
num-tiles-y (+ 1 (math.ceil (/ map-height tile-size)))
|
;; can be freely rotated without needing to fetch more.
|
||||||
(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)))
|
;; when facing north, we have e.g.
|
||||||
max-tile-x (+ min-tile-x num-tiles-x 4)
|
;; 720 width is 2.8 * 256 pixel tiles
|
||||||
min-tile-y (math.floor (- tile-y (/ num-tiles-y 2)))
|
;; 800 height is 3.125 tiles
|
||||||
max-tile-y (+ min-tile-y num-tiles-y 4)]
|
;;
|
||||||
|
;; 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))]
|
||||||
{
|
{
|
||||||
:min { :x min-tile-x :y min-tile-y }
|
:min { :x min-tile-x :y min-tile-y }
|
||||||
:max { :x max-tile-x :y max-tile-y }
|
:max { :x max-tile-x :y max-tile-y }
|
||||||
: num-tiles-x : num-tiles-y
|
: num-tiles-x
|
||||||
|
: num-tiles-y
|
||||||
|
:pixels {
|
||||||
|
:x (* tile-size num-tiles-x)
|
||||||
|
:y (* tile-size 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}))
|
||||||
|
|
||||||
|
(let [bounds (map-bounds-tile 65539.0 45014.0)]
|
||||||
|
(expect= bounds.min {:x 65536 :y 45011})
|
||||||
|
(expect= bounds.max {:x 65541 :y 45016})
|
||||||
|
)
|
||||||
|
|
||||||
|
(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)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(local cq (cqueues.new))
|
(local cq (cqueues.new))
|
||||||
|
|
||||||
(fn cairo-roads-path [g lines bounds]
|
(fn road-width-for [line offset]
|
||||||
(each [_ line (pairs lines)]
|
(+ (or offset 0)
|
||||||
(case line.points
|
(case (?. line :tags :highway)
|
||||||
[[sx sy] & more]
|
:motorway 18
|
||||||
(do
|
:trunk 17
|
||||||
(g:save)
|
:primary 16
|
||||||
(g:move_to (* tile-size (- sx bounds.min.x))
|
:secondary 14
|
||||||
(* tile-size (- sy bounds.min.y)))
|
:cycleway 4
|
||||||
(each [_ [x y] (ipairs more)]
|
:footway 4
|
||||||
(let [x1 (* tile-size (- x bounds.min.x))
|
other (do (print "highway " other) 12))))
|
||||||
y1 (* tile-size (- y bounds.min.y))]
|
|
||||||
(g:line_to x1 y1)))
|
(fn cairo-road-path [g [[sx sy] & points] bounds width]
|
||||||
(g:stroke)
|
(g:save)
|
||||||
(g:restore)))))
|
(g:set_line_width width)
|
||||||
|
(g:move_to (* tile-size (- sx bounds.min.x))
|
||||||
|
(* tile-size (- sy bounds.min.y)))
|
||||||
|
(each [_ [x y] (ipairs points)]
|
||||||
|
(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 cairo-roads [g lines bounds]
|
||||||
|
(let [road-width 14]
|
||||||
|
(g:set_source_rgb 0 0 0)
|
||||||
|
(each [_ line (pairs lines)]
|
||||||
|
(cairo-road-path g line.points bounds (road-width-for line)))
|
||||||
|
(g:set_source_rgb 1 1 1)
|
||||||
|
(each [_ line (pairs lines)]
|
||||||
|
(cairo-road-path g line.points bounds (road-width-for line -2)))))
|
||||||
|
|
||||||
(fn label-coords [{ : points } bounds]
|
(fn label-coords [{ : points } bounds]
|
||||||
(var biggest 0)
|
(var biggest 0)
|
||||||
@ -122,40 +182,37 @@ label.readout {
|
|||||||
(let [[x y] (. points biggest-n)
|
(let [[x y] (. points biggest-n)
|
||||||
[nx ny] (. points (+ 1 biggest-n))
|
[nx ny] (. points (+ 1 biggest-n))
|
||||||
angle (math.atan (- ny y) (- nx x))]
|
angle (math.atan (- ny y) (- nx x))]
|
||||||
(values
|
(if (> nx x)
|
||||||
(* tile-size (- x bounds.min.x))
|
(values
|
||||||
(* tile-size (- y bounds.min.y))
|
(* tile-size (- x bounds.min.x))
|
||||||
angle)))
|
(* tile-size (- y bounds.min.y))
|
||||||
|
angle)
|
||||||
|
(values ; if way runs r->l, prefer label to read l->r
|
||||||
|
(* tile-size (- nx bounds.min.x))
|
||||||
|
(* tile-size (- ny bounds.min.y))
|
||||||
|
(+ math.pi angle)))))
|
||||||
|
|
||||||
|
(var map-surface nil)
|
||||||
|
|
||||||
(fn cairo-the-map [window]
|
(fn draw-onto-map-surface [surface bounds zoom]
|
||||||
(let [{ : lat : lon : zoom } app-state
|
(let [{ : num-tiles-x : num-tiles-y } bounds
|
||||||
{ : num-tiles-x : num-tiles-y &as bounds } (map-bounds lat lon zoom)
|
|
||||||
road-width 14
|
road-width 14
|
||||||
lines []]
|
lines []]
|
||||||
|
|
||||||
(for [x bounds.min.x bounds.max.x]
|
(for [x bounds.min.x bounds.max.x]
|
||||||
(for [y bounds.min.y bounds.max.y]
|
(for [y bounds.min.y bounds.max.y]
|
||||||
(merge lines (tiles.polylines cq x y zoom))))
|
(merge lines (tiles.polylines cq x y zoom
|
||||||
|
#(set map-surface nil)
|
||||||
|
))))
|
||||||
|
|
||||||
(let [map-surface
|
(let [seen-road-names {}
|
||||||
(window:create_similar_surface
|
g (cairo.Context.create surface)]
|
||||||
cairo.Content.COLOR
|
|
||||||
(* tile-size (+ 4 num-tiles-x))
|
|
||||||
(* tile-size (+ 4 num-tiles-y)))
|
|
||||||
seen-road-names {}
|
|
||||||
g (cairo.Context.create map-surface)]
|
|
||||||
|
|
||||||
(g:set_source_rgb 0.7 0.8 0.8)
|
(g:set_source_rgb 0.7 0.8 0.8)
|
||||||
(g:rectangle 0 0 (* tile-size num-tiles-x) (* tile-size num-tiles-y))
|
(g:rectangle 0 0 bounds.pixels.x bounds.pixels.y)
|
||||||
(g:fill)
|
(g:fill)
|
||||||
|
|
||||||
(g:set_source_rgb 0 0 0)
|
(cairo-roads g lines bounds)
|
||||||
(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_source_rgb 0.2 0.2 0.2)
|
||||||
(g:set_font_size (+ road-width 1))
|
(g:set_font_size (+ road-width 1))
|
||||||
@ -169,7 +226,7 @@ label.readout {
|
|||||||
(tset seen-road-names n true)
|
(tset seen-road-names n true)
|
||||||
(g:save)
|
(g:save)
|
||||||
(g:set_line_width h)
|
(g:set_line_width h)
|
||||||
(g:set_source_rgb 1 1 1)
|
(g:set_source_rgba 1 0.95 1 0.7)
|
||||||
(g:move_to (- x 1) (- y 1))
|
(g:move_to (- x 1) (- y 1))
|
||||||
(g:rotate angle)
|
(g:rotate angle)
|
||||||
(g:rel_line_to (+ w 1) 0)
|
(g:rel_line_to (+ w 1) 0)
|
||||||
@ -184,24 +241,32 @@ label.readout {
|
|||||||
(g:fill)
|
(g:fill)
|
||||||
(g:restore)))))
|
(g:restore)))))
|
||||||
|
|
||||||
map-surface)))
|
surface)))
|
||||||
|
|
||||||
(var map-surface nil)
|
|
||||||
|
|
||||||
(fn on-osm-draw [widget g]
|
(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)
|
(let [(tile-x tile-y) (tiles.latlon->tile app-state.lat app-state.lon app-state.zoom)
|
||||||
bounds (map-bounds tile-x tile-y)
|
bounds (map-bounds-tile tile-x tile-y)
|
||||||
offset-x (- (* tile-size (- tile-x bounds.min.x)) (/ map-width 2))
|
offset-x (- (* tile-size (- tile-x bounds.min.x)) (/ viewport-width 2))
|
||||||
offset-y (- (* tile-size (- tile-y bounds.min.y)) (/ map-height 2))]
|
offset-y (- (* tile-size (- tile-y bounds.min.y)) (/ viewport-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)))))
|
||||||
|
|
||||||
|
(g:translate (+ (/ viewport-width 2)) (+ (/ viewport-height 2)))
|
||||||
|
(g:rotate (* (/ (- 360 app-state.smooth-course) 180) math.pi))
|
||||||
|
(g:translate (- (/ viewport-width 2)) (- (/ viewport-height 2)))
|
||||||
|
|
||||||
(g:set_source_surface map-surface (- offset-x) (- offset-y))
|
(g:set_source_surface map-surface (- offset-x) (- offset-y))
|
||||||
(g:set_operator cairo.Operator.SOURCE)
|
(g:set_operator cairo.Operator.SOURCE)
|
||||||
(g:rectangle 0 0 map-width map-height)
|
(g:paint)))
|
||||||
(g:fill)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -213,7 +278,7 @@ label.readout {
|
|||||||
(register-widget
|
(register-widget
|
||||||
:osm
|
:osm
|
||||||
(Gtk.DrawingArea {
|
(Gtk.DrawingArea {
|
||||||
:width map-width :height map-height
|
:width viewport-width :height viewport-height
|
||||||
:on_draw on-osm-draw
|
:on_draw on-osm-draw
|
||||||
})))
|
})))
|
||||||
|
|
||||||
@ -238,9 +303,6 @@ label.readout {
|
|||||||
(expect= (hhmmss (+ 45 (* 60 12) (* 60 60 3))) "3:12:45")
|
(expect= (hhmmss (+ 45 (* 60 12) (* 60 60 3))) "3:12:45")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(fn update-app-state [new-vals]
|
(fn update-app-state [new-vals]
|
||||||
(let [old-bounds
|
(let [old-bounds
|
||||||
(map-bounds app-state.lat app-state.lon app-state.zoom)]
|
(map-bounds app-state.lat app-state.lon app-state.zoom)]
|
||||||
@ -251,6 +313,9 @@ label.readout {
|
|||||||
(not (= old-bounds.min.x bounds.min.x))
|
(not (= old-bounds.min.x bounds.min.x))
|
||||||
(not (= old-bounds.min.y bounds.min.y)))
|
(not (= old-bounds.min.y bounds.min.y)))
|
||||||
(set map-surface nil)))
|
(set map-surface nil)))
|
||||||
|
(set app-state.smooth-course
|
||||||
|
(+ app-state.smooth-course
|
||||||
|
(* 0.05 (- app-state.course app-state.smooth-course))))
|
||||||
(each [name widget (pairs state-widgets)]
|
(each [name widget (pairs state-widgets)]
|
||||||
(case name
|
(case name
|
||||||
:speed (widget:set_label
|
:speed (widget:set_label
|
||||||
@ -285,7 +350,8 @@ label.readout {
|
|||||||
(fn [self g]
|
(fn [self g]
|
||||||
(g:set_source_rgb 0.4 0.0 0.1)
|
(g:set_source_rgb 0.4 0.0 0.1)
|
||||||
(g:translate (// height 2) (// height 2))
|
(g:translate (// height 2) (// height 2))
|
||||||
(g:rotate (/ (* -2 app-state.course math.pi) 360) )
|
(g:rotate (* (/ (- app-state.course app-state.smooth-course)
|
||||||
|
180) math.pi))
|
||||||
(g:translate (// height -2) (// height -2))
|
(g:translate (// height -2) (// height -2))
|
||||||
(g:set_line_width 4)
|
(g:set_line_width 4)
|
||||||
(g:move_to 10 height)
|
(g:move_to 10 height)
|
||||||
@ -336,9 +402,13 @@ label.readout {
|
|||||||
|
|
||||||
(GLib.timeout_add
|
(GLib.timeout_add
|
||||||
GLib.PRIORITY_DEFAULT
|
GLib.PRIORITY_DEFAULT
|
||||||
20 ; ms
|
100 ; ms
|
||||||
(fn []
|
(fn []
|
||||||
|
;; run cqueues scheduler
|
||||||
(cq:step 0)
|
(cq:step 0)
|
||||||
|
;; for smoother rotation when course changes, repaint more often than
|
||||||
|
;; once per gnss message
|
||||||
|
(update-app-state {})
|
||||||
true)
|
true)
|
||||||
nil nil)
|
nil nil)
|
||||||
|
|
||||||
|
@ -1,8 +1,27 @@
|
|||||||
with import <nixpkgs> {};
|
with import <nixpkgs> {};
|
||||||
let package = pkgs.callPackage ./. {};
|
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; };
|
||||||
in
|
in
|
||||||
package.overrideAttrs(o: {
|
package.overrideAttrs(o: {
|
||||||
|
nativeBuildInputs = [ fennel-ls ] ++ o.nativeBuildInputs;
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
|
mkdir -p bin
|
||||||
|
( cd bin && ln -sf `type -p fennel-ls` `type -p fennel` . )
|
||||||
export LUA_CPATH=$(lua -e "print(package.cpath)")
|
export LUA_CPATH=$(lua -e "print(package.cpath)")
|
||||||
export LUA_PATH=$(lua -e "print(package.path)")\;$RXI_JSON/share/lua/5.3/?.lua
|
export LUA_PATH=$(lua -e "print(package.path)")\;$RXI_JSON/share/lua/5.3/?.lua
|
||||||
'';
|
'';
|
||||||
|
@ -46,10 +46,10 @@
|
|||||||
|
|
||||||
(fn overpass [lat lon zoom]
|
(fn overpass [lat lon zoom]
|
||||||
(let [width (/ 360 (^ 2 zoom))
|
(let [width (/ 360 (^ 2 zoom))
|
||||||
n (+ lat width) ;XXX adjust for latitude
|
n lat
|
||||||
w (- lon width)
|
w lon
|
||||||
s lat
|
s (- lat width)
|
||||||
e lon]
|
e (+ lon width)]
|
||||||
(->
|
(->
|
||||||
[
|
[
|
||||||
"[out:json];"
|
"[out:json];"
|
||||||
@ -71,6 +71,7 @@
|
|||||||
e.id
|
e.id
|
||||||
{
|
{
|
||||||
:name (?. e :tags :name)
|
:name (?. e :tags :name)
|
||||||
|
:tags e.tags
|
||||||
:points
|
:points
|
||||||
(icollect [_ nd (ipairs e.nodes)]
|
(icollect [_ nd (ipairs e.nodes)]
|
||||||
(let [node (. nodes nd)
|
(let [node (. nodes nd)
|
||||||
@ -108,7 +109,7 @@
|
|||||||
;; we'd like to have a way for completed background fetch to signal
|
;; we'd like to have a way for completed background fetch to signal
|
||||||
;; so that the map can be redrawn
|
;; so that the map can be redrawn
|
||||||
|
|
||||||
(fn polylines [cq x y zoom]
|
(fn polylines [cq x y zoom cb]
|
||||||
(let [k (.. x "_" y "_" zoom)
|
(let [k (.. x "_" y "_" zoom)
|
||||||
pathname (.. "/tmp/tiles/" k ".json")]
|
pathname (.. "/tmp/tiles/" k ".json")]
|
||||||
(if (file-exists? pathname)
|
(if (file-exists? pathname)
|
||||||
@ -128,6 +129,7 @@
|
|||||||
(cqueues.sleep (math.random 2 6))))
|
(cqueues.sleep (math.random 2 6))))
|
||||||
(print "got " k)
|
(print "got " k)
|
||||||
(f:write json)
|
(f:write json)
|
||||||
|
(cb)
|
||||||
true)))
|
true)))
|
||||||
[] ; return no lines for now
|
[] ; return no lines for now
|
||||||
))))
|
))))
|
||||||
|
Loading…
Reference in New Issue
Block a user