M Makefile => Makefile +1 -7
@@ 6,11 6,6 @@ dst := _build/geohash-$(os)-$(cpu)-$(ver).cgi
final: build $(dst)
-lib/res.ml: res res/doap.rdf res/doap2html.xslt
- find res -name .DS_Store -delete
- # opam install ocp-ocamlres
- ocp-ocamlres -format ocaml $< -o $@
-
$(dst): _build/default/bin/main.exe
cp $< $@
chmod u+w $@
@@ 26,8 21,7 @@ test/assert.ml:
#
.PHONY: all build clean test install uninstall doc examples
-build: lib/res.ml
- @echo "let git_sha = \""`git rev-parse --short HEAD`"\"" > bin/version.ml
+build:
dune build bin/main.exe
all: build
M bin/cgi.ml => bin/cgi.ml +32 -31
@@ 29,42 29,43 @@ let handle_hash oc req =
match Geohash.decode hash with
| Error _ -> error oc 406 "Cannot decode hash."
| Ok ((lat, lon), (dlat, dlon)) ->
- let mime = "text/xml"
- and xslt = "gpx2html.xslt"
- and uri = req |> request_uri
- and base = "http://purl.mro.name/geohash" in
- Printf.fprintf oc "%s: %s\n" "Content-Type" mime;
- Printf.fprintf oc "\n";
- Printf.fprintf oc
- "<?xml version='1.0'?><!-- \
- https://www.topografix.com/GPX/1/1/gpx.xsd -->\n\
- <?xml-stylesheet type='text/xsl' href='%s'?>\n\
- <gpx xmlns='http://www.topografix.com/GPX/1/1' version='1.1' \
- creator='%s'>\n\
- \ <metadata>\n\
- \ <link href='%s://%s:%s%s'/>\n\
- \ <bounds minlat='%f' minlon='%f' maxlat='%f' maxlon='%f'/>\n\
- \ </metadata>\n\
- \ <wpt lat='%f' lon='%f'>\n\
- \ <name>#%s</name>\n\
- \ <link href='%s://%s:%s%s'/>\n\
- \ </wpt>\n\
- </gpx>"
- xslt base req.scheme req.host req.server_port uri (lat -. dlat)
- (lon -. dlon) (lat +. dlat) (lon +. dlon) lat lon hash req.scheme
- req.host req.server_port uri;
- 0)
+ let mime = "text/xml"
+ and xslt = "gpx2html.xslt"
+ and uri = req |> request_uri
+ and base = "http://purl.mro.name/geohash" in
+ Printf.fprintf oc "%s: %s\n" "Content-Type" mime;
+ Printf.fprintf oc "\n";
+ Printf.fprintf oc
+ "<?xml version='1.0'?><!-- \
+ https://www.topografix.com/GPX/1/1/gpx.xsd -->\n\
+ <?xml-stylesheet type='text/xsl' href='%s'?>\n\
+ <gpx xmlns='http://www.topografix.com/GPX/1/1' version='1.1' \
+ creator='%s'>\n\
+ \ <metadata>\n\
+ \ <link href='%s://%s:%s%s'/>\n\
+ \ <bounds minlat='%f' minlon='%f' maxlat='%f' maxlon='%f'/>\n\
+ \ </metadata>\n\
+ \ <wpt lat='%f' lon='%f'>\n\
+ \ <name>#%s</name>\n\
+ \ <link href='%s://%s:%s%s'/>\n\
+ \ </wpt>\n\
+ </gpx>"
+ xslt base req.scheme req.host req.server_port uri (lat -. dlat)
+ (lon -. dlon) (lat +. dlat) (lon +. dlon) lat lon hash req.scheme
+ req.host req.server_port uri;
+ 0)
| _ -> error oc 404 "Not found"
let handle oc req =
let mercator_birth = "u154c" and uri = req |> request_uri in
match req.request_method with
| "GET" -> (
+ let r n = n |> Res.read |> Option.value ~default:"" in
match req.path_info with
- | "/about" -> dump_clob oc "text/xml" Res.doap_rdf
- | "/LICENSE" -> dump_clob oc "text/plain" Res._LICENSE
- | "/doap2html.xslt" -> dump_clob oc "text/xml" Res.doap2html_xslt
- | "/gpx2html.xslt" -> dump_clob oc "text/xml" Res.gpx2html_xslt
+ | "/about" -> dump_clob oc "text/xml" (r "doap.rdf")
+ | "/LICENSE" -> dump_clob oc "text/plain" (r "LICENSE")
+ | "/doap2html.xslt" -> dump_clob oc "text/xml" (r "doap2html.xslt")
+ | "/gpx2html.xslt" -> dump_clob oc "text/xml" (r "gpx2html.xslt")
| "" -> uri ^ "/" |> redirect oc
| "/" -> (
match req.query_string with
@@ 72,9 73,9 @@ let handle oc req =
| qs -> (
match qs |> Route.coord_from_qs with
| Error (`NoMatch (_, s')) ->
- error oc 406 ("Cannot encode coords: '" ^ s' ^ "'")
+ error oc 406 ("Cannot encode coords: '" ^ s' ^ "'")
| Error (`ConverterFailure _) ->
- error oc 406 "Cannot encode coords."
+ error oc 406 "Cannot encode coords."
| Ok co -> (
(* actually logic :-( *)
let prec =
M bin/dune => bin/dune +8 -2
@@ 1,9 1,15 @@
;; https://discuss.ocaml.org/t/dune-how-to-link-statically-on-linux-not-on-others/8537/4?u=mro
(rule (with-stdout-to link_flags.sexp (run sh %{dep:gen_link_flags.sh})))
+(rule (with-stdout-to version.ml (run echo "let git_sha = \""`git rev-parse --short HEAD`"\"")))
+(rule
+ (target res.ml)
+ (deps (source_tree ../res))
+ (action (with-stdout-to %{target}
+ (run ocaml-crunch --mode=plain ../res))))
; https://stackoverflow.com/a/53325230/349514
(executable
(name main)
- (libraries lib geohash)
- ; (link_flags (:include link_flags.sexp))
+ (libraries lib geohash tyre)
+ (link_flags (:include link_flags.sexp))
)
M bin/gen_link_flags.sh => bin/gen_link_flags.sh +1 -1
@@ 7,7 7,7 @@ case "$(uname -s)" in
echo '()'
;;
*)
- echo '(-ccopt "-static")'
+ echo '(-ccopt -static)'
;;
esac
M bin/shell.ml => bin/shell.ml +2 -1
@@ 53,7 53,8 @@ let exec args =
| [ "-h" ] | [ "--help" ] -> print_help oc
| [ "-V" ] | [ "--version" ] -> print_version oc
| [ "--doap" ] ->
- Printf.fprintf oc "%s" Lib.Res.doap_rdf;
+ let r n = n |> Res.read |> Option.value ~default:"" in
+ Printf.fprintf oc "%s" (r "doap.rdf");
0
| [ i ] ->
(i |> to_hash |> function
M dune-project => dune-project +5 -5
@@ 6,7 6,7 @@
(implicit_transitive_deps true)
(generate_opam_files true)
-(license GPLv3)
+(license GPL-3.0-or-later)
(maintainers "Marcus Rohrmoser <work@mro.name>")
(authors "The GeoHash# programmers")
@@ 16,7 16,7 @@
(package
(name geohash)
- (synopsis "#🌐 geohash converter 🐫 library.")
+ (synopsis "#🌐 geohash converter 🐫 library")
(description "Convert WGS84 lat/lon pairs to [Gustavo Niemeyer](http://niemeyer.net/)s
[Geohash](http://en.wikipedia.org/wiki/Geohash) and back.")
(tags (Social Web GeoHash CGI RFC3875))
@@ 24,16 24,16 @@
(optint (>= 0.3))
(tyre (>= 0.5))
(odoc :with-doc)
- (ocaml (and (>= 4.05) (< 4.12)))
+ (ocaml (>= 4.05))
))
(package
(name geohash_bin)
- (synopsis "#🌐 geohash converter 🐫. Commandline and CGI. Zero-config, single-file.")
+ (synopsis "#🌐 geohash converter 🐫. Commandline and CGI. Zero-config, single-file")
(description "Convert WGS84 lat/lon pairs to [Gustavo Niemeyer](http://niemeyer.net/)s
[Geohash](http://en.wikipedia.org/wiki/Geohash) and back. Web and commandline, 🐪,
statically linked, single-file, zero-config.")
(tags (Social Web GeoHash CGI RFC3875))
(depends
- ocp-ocamlres
+ crunch
))
M geohash.opam => geohash.opam +3 -3
@@ 1,12 1,12 @@
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
-synopsis: "#🌐 geohash converter 🐫 library."
+synopsis: "#🌐 geohash converter 🐫 library"
description: """
Convert WGS84 lat/lon pairs to [Gustavo Niemeyer](http://niemeyer.net/)s
[Geohash](http://en.wikipedia.org/wiki/Geohash) and back."""
maintainer: ["Marcus Rohrmoser <work@mro.name>"]
authors: ["The GeoHash# programmers"]
-license: "GPLv3"
+license: "GPL-3.0-or-later"
tags: ["Social" "Web" "GeoHash" "CGI" "RFC3875"]
homepage: "https://demo.mro.name/geohash.cgi"
bug-reports: "https://codeberg.org/mro/geohash/issues"
@@ 15,7 15,7 @@ depends: [
"optint" {>= "0.3"}
"tyre" {>= "0.5"}
"odoc" {with-doc}
- "ocaml" {>= "4.05" & < "4.12"}
+ "ocaml" {>= "4.05"}
]
build: [
["dune" "subst"] {dev}
M geohash_bin.opam => geohash_bin.opam +3 -3
@@ 1,20 1,20 @@
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
synopsis:
- "#🌐 geohash converter 🐫. Commandline and CGI. Zero-config, single-file."
+ "#🌐 geohash converter 🐫. Commandline and CGI. Zero-config, single-file"
description: """
Convert WGS84 lat/lon pairs to [Gustavo Niemeyer](http://niemeyer.net/)s
[Geohash](http://en.wikipedia.org/wiki/Geohash) and back. Web and commandline, 🐪,
statically linked, single-file, zero-config."""
maintainer: ["Marcus Rohrmoser <work@mro.name>"]
authors: ["The GeoHash# programmers"]
-license: "GPLv3"
+license: "GPL-3.0-or-later"
tags: ["Social" "Web" "GeoHash" "CGI" "RFC3875"]
homepage: "https://demo.mro.name/geohash.cgi"
bug-reports: "https://codeberg.org/mro/geohash/issues"
depends: [
"dune" {>= "2.8"}
- "ocp-ocamlres"
+ "crunch"
"odoc" {with-doc}
]
build: [
M lib/dune => lib/dune +1 -3
@@ 1,7 1,5 @@
(library
(name lib)
- (libraries
- tyre
- )
(public_name geohash_bin.lib)
+ (libraries tyre)
)
M public/dune => public/dune +1 -4
@@ 1,8 1,5 @@
(library
(name geohash)
- (libraries
- tyre
- optint
- )
(public_name geohash)
+ (libraries optint)
)
M public/geohash.mli => public/geohash.mli +9 -5
@@ 1,12 1,16 @@
-(** Hello, world! *)
+(** Geohash math to encode and decode base32 character hashes. *)
+(** a WGS84 coordinate pair *)
+type wgs84_lat_lon = (float * float)
+type delta = (float * float)
- (** encode a WGS84 (lat,lon) coordinate pair into Geohash of given length. *)
- val encode : int -> (float * float) -> (string, int) result
+(** encode a WGS84 (lat,lon) coordinate pair into Geohash of given
+ hash character count. *)
+val encode : int -> wgs84_lat_lon -> (string, int) result
- (** decode a geohash base32 string to a WGS84 coordinate region center,delta. *)
- val decode : string -> (((float * float) * (float * float)), char) result
+(** decode a geohash base32 string to a WGS84 coordinate region center,delta. *)
+val decode : string -> ((wgs84_lat_lon * delta), char) result
(**/**) (* hide Whitebox module *)
module Whitebox : module type of Whitebox
M public/whitebox.ml => public/whitebox.ml +29 -18
@@ 19,11 19,14 @@
*)
(* Inspired by https://mmcloughlin.com/posts/geohash-assembly
- * and https://github.com/mmcloughlin/geohash/blob/master/geohash.go
+ * and https://github.com/mmcloughlin/geohash/blob/master/geohash.go
* and https://github.com/mmcloughlin/deconstructedgeohash/blob/master/geohash.go *)
open Optint.Int63
+type wgs84_lat_lon = (float * float)
+type delta = (float * float)
+
(*
* 60 bits are fine, because
* - 12 geohash characters equal 60 bit
@@ 56,16 59,24 @@ let x5555555555555555 = of_int64 0x3555555555555555L
let interleave (x, y) =
let spread x =
let f s m x' = m |> logand (x' |> logor (shift_left x' s)) in
- x |> f 16 x0000ffff0000ffff |> f 8 x00ff00ff00ff00ff
- |> f 4 x0f0f0f0f0f0f0f0f |> f 2 x3333333333333333 |> f 1 x5555555555555555
+ x
+ |> f 16 x0000ffff0000ffff
+ |> f 8 x00ff00ff00ff00ff
+ |> f 4 x0f0f0f0f0f0f0f0f
+ |> f 2 x3333333333333333
+ |> f 1 x5555555555555555
in
spread x |> logor (shift_left (spread y) 1)
let deinterleave x =
let squash x =
let f s m x' = m |> logand (x' |> logor (shift_right x' s)) in
- x |> logand x5555555555555555 |> f 1 x3333333333333333
- |> f 2 x0f0f0f0f0f0f0f0f |> f 4 x00ff00ff00ff00ff |> f 8 x0000ffff0000ffff
+ x
+ |> logand x5555555555555555
+ |> f 1 x3333333333333333
+ |> f 2 x0f0f0f0f0f0f0f0f
+ |> f 4 x00ff00ff00ff00ff
+ |> f 8 x0000ffff0000ffff
|> f 16 x00000000ffffffff
in
(squash x, squash (shift_right x 1))
@@ 79,8 90,8 @@ let base32_encode chars x =
match i with
| -1 -> b
| _ ->
- x' |> logand x1f |> to_int |> b32_int_to_char |> Bytes.set b i;
- f (i - 1) (shift_right x' 5) b
+ x' |> logand x1f |> to_int |> b32_int_to_char |> Bytes.set b i;
+ f (i - 1) (shift_right x' 5) b
in
chars |> Bytes.create |> f (chars - 1) x |> Bytes.to_string
@@ 92,23 103,23 @@ let base32_decode hash =
match len <= 12 with
| false -> Error '_'
| true ->
- let rec f idx x =
- match len - idx with
- | 0 -> Ok x
- | _ ->
- Result.bind
- (hash.[idx] |> b32_int_of_char)
- (fun v -> v |> of_int |> logor (shift_left x 5) |> f (idx + 1))
- in
- f 0 zero
+ let rec f idx x =
+ match len - idx with
+ | 0 -> Ok x
+ | _ ->
+ Result.bind
+ (hash.[idx] |> b32_int_of_char)
+ (fun v -> v |> of_int |> logor (shift_left x 5) |> f (idx + 1))
+ in
+ f 0 zero
let encode chars wgs84 =
(* is the empty string a geohash? representing all earth? *)
match 1 <= chars && chars <= 12 with
| false -> Error chars
| true ->
- let h60 = wgs84 |> quantize30 |> interleave in
- Ok (shift_right h60 (60 - (5 * chars)) |> base32_encode chars)
+ let h60 = wgs84 |> quantize30 |> interleave in
+ Ok (shift_right h60 (60 - (5 * chars)) |> base32_encode chars)
let error_with_precision bits factor =
let latBits = bits / 2 in