Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extract functions (Geojson to Topojson) #46

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ocamlformat
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = 0.21.0
version = 0.24.1
profile = conventional
break-infix = fit-or-vertical
parse-docstrings = true
Expand Down
2 changes: 1 addition & 1 deletion src/topojson/dune
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(library
(public_name topojson)
(name topojson)
(libraries))
(libraries geojson))
70 changes: 70 additions & 0 deletions src/topojson/topojson.ml
Original file line number Diff line number Diff line change
Expand Up @@ -616,4 +616,74 @@ module Make (J : Intf.Json) = struct
Geometry.to_json g

let v ?bbox topojson = { bbox; topojson }

module Geojson = Geojson.Make (J)

let extract_lines_from_geometry (t : Geojson.t) :
Geojson.Geometry.Position.t array list =
let open Geojson.Geometry in
let geojson = Geojson.geojson t in
match geojson with
| Geometry g -> (
let geo = geometry g in
match geo with
| LineString ls -> [ LineString.coordinates ls ]
| MultiLineString ml ->
let lines = MultiLineString.lines ml in
let arr = Array.map LineString.coordinates lines in
Array.to_list arr
| Polygon p ->
let rings = Polygon.rings p in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm thinking about this a little more, perhaps we have to treat rings slightly differently because the first and last point are technically connected.

let arr = Array.map LineString.coordinates rings in
Array.to_list arr
| MultiPolygon mp ->
let rings = MultiPolygon.polygons mp in
let r = Array.to_list (Array.map Polygon.rings rings) in
let arr = Array.map LineString.coordinates (Array.concat r) in
Array.to_list arr
| Point _ -> []
| MultiPoint _ -> []
| _ -> [])
| _ -> failwith "Not a valid Geometry"

let extract_from_feature (f : Geojson.Feature.t) :
Geojson.Geometry.Position.t array list =
let open Geojson.Feature in
let geometry = geometry f in
match geometry with
| Some t ->
let p = Geojson.Geometry t in
let geo = Geojson.v p in
extract_lines_from_geometry geo
| None -> []

let extract_from_feature_collection (fc : Geojson.Feature.Collection.t) :
Geojson.Geometry.Position.t array list =
let open Geojson.Feature.Collection in
let features = features fc in
List.concat (List.map extract_from_feature features)

let find_junctions (lines : Geojson.Geometry.Position.t array list) :
Geojson.Geometry.Position.t list =
let open Geojson.Geometry in
let junction_table : (Position.t, Position.t list) Hashtbl.t =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make this a position to position set hash table to stay closer to how the blog post does it?

Hashtbl.create (List.length lines)
in
let junction : Position.t list = [] in
List.iter
(fun line ->
let line_length = Array.length line in
for i = 0 to line_length - 2 do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Points on a line other than the first and the last have two neighbours: n - 1 and n + 1.

let point1 = line.(i) in
let point2 = line.(i + 1) in
if Hashtbl.mem junction_table point1 then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for every point we visit we want to put in the hash table it's neighbours as a set. So if we are at point N then we but N, { N - 1, N - 2} as a key-value pair into the hash table.

Then if we come across N again we check that Ns neighbours are different (therefore a junction) or if they are the same then it isn't.

let neighbors = Hashtbl.find junction_table point1 in
Hashtbl.replace junction_table point1 (point2 :: neighbors)
else Hashtbl.add junction_table point1 [ point2 ];

let neighbors = Hashtbl.find junction_table point1 in
if List.length neighbors > 2 then ref junction := point1 :: junction
done)
lines;
!(ref junction)
end