commit b94fc23afb7cb8f5c67c7b95751ae12d9c807cf9
parent c2a97b18f868be8c4493419f0fc27a035fdd8316
Author: Leah (ctucx) <git@ctu.cx>
Date: Sun, 11 Dec 2022 11:02:32 +0100
parent c2a97b18f868be8c4493419f0fc27a035fdd8316
Author: Leah (ctucx) <git@ctu.cx>
Date: Sun, 11 Dec 2022 11:02:32 +0100
flake.nix: add scripts for tiles generation
3 files changed, 170 insertions(+), 8 deletions(-)
diff --git a/flake.nix b/flake.nix @@ -1,5 +1,5 @@ { - description = ""; + description = "render gpx files to tiles and display them on a map"; inputs = { flake-utils.url = "github:numtide/flake-utils"; @@ -10,8 +10,26 @@ overlay = final: prev: { - bikemap = final.mkYarnPackage rec { - name = "bikemap"; + datamaps = final.stdenv.mkDerivation { + name = "datamaps"; + + src = final.fetchFromGitHub { + owner = "e-n-f"; + repo = "datamaps"; + rev = "76e620adabbedabd6866b23b30c145b53bae751e"; + sha256 = "1rdqbyfmgidiv4aqy1s6llls304dxbg5226c7k622smd2rnda2jk"; + }; + + buildInputs = with final; [ pkgconfig libpng ]; + + installPhase = '' + mkdir -p $out/bin; + cp {encode,render,merge,enumerate} $out/bin; + ''; + }; + + gpx-map = final.mkYarnPackage { + name = "gpx-map"; src = self; packageJSON = ./package.json; @@ -26,10 +44,78 @@ installPhase = '' mkdir -p $out - cp -r deps/bikemap/{index.html,bundle.js} $out + cp -r deps/gpx-map/{index.html,bundle.js} $out ''; }; + generateTilesFromGPX = ( + let + parse-gpx = final.stdenv.mkDerivation rec { + name = "parse-gpx"; + src = ./parse-gpx.pl; + + dontUnpack = true; + + nativeBuildInputs = [ final.makeWrapper ]; + buildInputs = [ final.perl ]; + + installPhase = ''mkdir -p $out/bin; cp $src $out/bin/parse-gpx; chmod +x $out/bin/parse-gpx;''; + postFixup = ''wrapProgram $out/bin/parse-gpx --prefix PERL5LIB : "${with final.perlPackages; makePerlPath [ XMLParser ]}"''; + }; + + makeTile = final.writeShellScript "makeTile.sh" '' + mkdir -p tiles/$2/$3 + echo "rendering $1 $2 $3 $4 $5 $6" + + if [ $2 -gt 13 ]; + then + ${final.datamaps}/bin/render -g -t0 -L4 -c 'ff8800' -S 'ff8800' $1 $2 $3 $4 | ${final.pngquant}/bin/pngquant 256 > tiles/$2/$3/$4.png + else + ${final.datamaps}/bin/render -g -t0 -L7 -c 'ff8800' -S 'ff8800' $1 $2 $3 $4 | ${final.pngquant}/bin/pngquant 256 > tiles/$2/$3/$4.png + fi + ''; + + in final.writeShellScriptBin "generateTilesFromGPX" '' + # strict mode + set -euo pipefail + IFS=$'\n\t' + + TMP_DIR=`mktemp -d -t tiles.XXXXXXXXXX` + + trap "{ rm -rf "$TMP_DIR"; }" SIGINT SIGTERM ERR EXIT + + if [ ! -d "$1" ]; then + echo "$1 does not exist." + exit 1 + fi + + if [ -z "$(ls -A $1)" ]; then + echo "$1 is empty." + exit 1 + fi + + if [ ! -d "$2" ]; then + echo "$2 does not exist." + exit 1 + fi + + if [ ! -z "$(ls -A $2)" ]; then + echo "$2 is not empty." + exit 1 + fi + + GPX_DIR=`realpath $1` + TILES_DIR=`realpath $2` + + cd $TMP_DIR + + find $GPX_DIR -name '*.gpx' -print0 | xargs -0 ${parse-gpx}/bin/parse-gpx | ${final.datamaps}/bin/encode -z16 -m8 -o $TMP_DIR/gpx.dm + ${final.datamaps}/bin/enumerate -s -Z6 -z16 ./gpx.dm | xargs -L1 -P3 ${makeTile} + + mv $TMP_DIR/tiles/* $TILES_DIR/ + '' + ); + }; } // (flake-utils.lib.eachDefaultSystem (system: @@ -41,17 +127,23 @@ in rec { - packages.bikemap = pkgs.bikemap; - packages.default = pkgs.bikemap; + packages.generateTilesFromGPX = pkgs.generateTilesFromGPX; + packages.datamaps = pkgs.datamaps; + packages.gpx-map = pkgs.gpx-map; devShells.default = pkgs.mkShell { - nativeBuildInputs = [ pkgs.yarn pkgs.yarn2nix pkgs.php ]; + nativeBuildInputs = with pkgs; [ yarn yarn2nix php datamaps generateTilesFromGPX ]; shellHook = '' export NODE_OPTIONS=--openssl-legacy-provider alias serve="yarn build && php -S localhost:8080" ''; }; + apps.default = { + type = "app"; + program = "${pkgs.generateTilesFromGPX}/bin/generateTilesFromGPX"; + }; + } )); } \ No newline at end of file
diff --git a/package.json b/package.json @@ -1,5 +1,5 @@ { - "name": "bikemap", + "name": "gpx-map", "version": "1.0.0", "description": "Just a simple map with 2 layers", "scripts": {
diff --git a/parse-gpx.pl b/parse-gpx.pl @@ -0,0 +1,70 @@ +#!/usr/bin/env perl +use POSIX; +use XML::Parser; + +$pi = 4 * atan2(1, 1); + +sub handle_start { + my ($expat, $element, @tags) = @_; + my %tags = @tags; + + if ($element eq "trkpt") { + $lat = $tags{'lat'}; + $lon = $tags{'lon'}; + } elsif ($element eq "ele") { + $state = "ele"; + } elsif ($element eq "time") { + $state = "time"; + } else { + $state = ""; + } +} + +sub handle_char { + my ($expat, $string) = @_; + + $text{$state} .= $string; +} + +sub handle_end { + my ($expat, $element) = @_; + + if ($element eq "trkseg" || $element eq "trk") { + $oldlat = $oldlon = ""; + } elsif ($element eq "trkpt") { + if ($lat ne "" && $lon ne "" && $oldlat ne "" && $oldlon ne "") { + if ($lat ne $oldlat || $lon ne $oldlon) { + print "$oldlat,$oldlon $lat,$lon "; + + $rat = cos(($lat + $oldlat) / 2 * $pi / 180); + $ang = atan2($lat - $oldlat, ($lon - $oldlon) * $rat); + + printf("8:%d\n", ($ang + $pi) * 256 / (2 * $pi)); + } + } + + $oldlat = $lat; + $oldlon = $lon; + %text = (); + } +} + +if ($#ARGV < 0) { + $parser = new XML::Parser(Handlers => { Start => \&handle_start, + End => \&handle_end, + Char => \&handle_char, + }); + + $parser->parse(*STDIN); +} else { + for $file (@ARGV) { + $oldwhen = 0; + + $parser = new XML::Parser(Handlers => { Start => \&handle_start, + End => \&handle_end, + Char => \&handle_char, + }); + + $parser->parsefile($file); + } +}