Skip to content

extract functions (Geojson to Topojson)#46

Draft
Priceless-P wants to merge 6 commits into
geocaml:mainfrom
Priceless-P:geo_topojson
Draft

extract functions (Geojson to Topojson)#46
Priceless-P wants to merge 6 commits into
geocaml:mainfrom
Priceless-P:geo_topojson

Conversation

@Priceless-P
Copy link
Copy Markdown
Contributor

Extracts lines and rings from Geojson's Objects

@Priceless-P
Copy link
Copy Markdown
Contributor Author

@patricoferris what do i think about this? I also intend to add extract_geomteries and extract_features_collection (if necessary).

Comment thread src/topojson/topojson.ml Outdated
loop [] coordinates
| _ -> failwith "Not a valid LineString or Polygon"

let extract_lines = function
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This comment applies to pretty much most of the other functions defined here.

I don't think we want to reimplement all of the JSON parsing logic again, GeoJSON does this already for us so we should just uses GeoJSON to extract the coordinates etc. from the geometries (since we are converting from GeoJSON to TopoJSON).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment thread src/topojson/topojson_intf.ml Outdated
val to_json : ?bbox:float array -> t -> json
val of_json : json -> (t, [ `Msg of string ]) result

val extract_lines :
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We won't actually ever expose these internal functions. Instead, eventually, we will just have something like:

val geojson_to_topojson : Geojson.t -> Topojson.t

@Priceless-P
Copy link
Copy Markdown
Contributor Author

Hi @patricoferris,

module Geojson = Geojson.Make (Ezjsonm_parser)

keep giving error Unbound module Ezjsonm_parser

Also with the current implementation, will extract_lines_from_geometry also extract rings from Polgon and MultiPolygon?

@patricoferris
Copy link
Copy Markdown
Contributor

patricoferris commented Feb 11, 2023

Also with the current implementation, will extract_lines_from_geometry also extract rings from Polgon and MultiPolygon?

I think so, they are after all just lines.

keep giving error Unbound module Ezjsonm_parser

Yep, we still want to be agnostic to the JSON parser the user decided to use. You should be able to pass the same one that gets passed to our Make "functor". This is a special name for modules that are parameterised by other modules.

module Make (J : Intf.Json) = struct
  module Geojson = Geojson.Make(J)
  ...

@Priceless-P
Copy link
Copy Markdown
Contributor Author

Hi @patricoferris what do you think?

Comment thread src/topojson/topojson.ml
let arr = Array.map LineString.coordinates lines in
Array.to_list arr
| Polygon p ->
let rings = Polygon.rings p in
Copy link
Copy Markdown
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.

Comment thread src/topojson/topojson.ml Outdated
List.iter
(fun line ->
let line_length = Array.length line in
for i = 0 to line_length - 2 do
Copy link
Copy Markdown
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.

Comment thread src/topojson/topojson.ml Outdated
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
Copy Markdown
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?

Comment thread src/topojson/topojson.ml
for i = 0 to line_length - 2 do
let point1 = line.(i) in
let point2 = line.(i + 1) in
if Hashtbl.mem junction_table point1 then
Copy link
Copy Markdown
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.

@Priceless-P
Copy link
Copy Markdown
Contributor Author

@patricoferris I am having a lot of type incompatibility errors. Please help me out 😥

@patricoferris
Copy link
Copy Markdown
Contributor

Ah, most of the issues were just related to giving functions a little too much generality and also we didn't expose that the positions were float arrays. Feel free to cherry-pick this commit 7b31814

@Priceless-P
Copy link
Copy Markdown
Contributor Author

The type errors are gone now. :)) Thank you @patricoferris

@Priceless-P
Copy link
Copy Markdown
Contributor Author

Do you think the find_junction is correction? I changed the MultiLineString coordinates to

 "coordinates": [
        [
			[1, 1], [2, 2],[3, 3],
			[4, 4], [3, 1],[4, 2]

        ], [
			[4, 2], [4, 4], [4, 6]
        ]
    ]

And the test didn't print out any junctions

@patricoferris
Copy link
Copy Markdown
Contributor

I don't think the function is correct, see my comments inline on the code.

Copy link
Copy Markdown
Contributor

@patricoferris patricoferris left a comment

Choose a reason for hiding this comment

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

These changes are heading in the right direction, nice! Comments are inline.

Comment thread test/topojson/test.ml
let transform = Alcotest.testable pp_transform Stdlib.( = )

let to_string pp a =
let _to_string pp a =
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If this function isn't being used we can probably remove it ?

Comment thread src/topojson/topojson.ml
let point1 = line.(i) in
let point2 = line.(i - 1) in
let point3 = line.(i + 1) in
let point2 = if i > 0 then line.(i - 1) else line.(line_length - 1) in
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Isn't this only true if we're talking about a ring rather than any line?

Comment thread src/topojson/topojson.ml
let point2 = line.(i - 1) in
let point3 = line.(i + 1) in
let point2 = if i > 0 then line.(i - 1) else line.(line_length - 1) in
let point3 = if i < line_length - 1 then line.(i + 1) else line.(0) in
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same question here?

Comment thread src/topojson/topojson.ml
@@ -698,24 +696,23 @@ module Make (J : Intf.Json) = struct
let junction_table : (Position.t, Set.t) Hashtbl.t =
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We really want to know every set of neighbours to begin with (let's get the algorithm correct before optimising). So I think we want something like: (Position.t, Set.t list) Hashtbl.t.

Comment thread src/topojson/topojson.ml
let point3 = if i < line_length - 1 then line.(i + 1) else line.(0) in
if Hashtbl.mem junction_table point1 then
let neighbors = Hashtbl.find junction_table point1 in
Hashtbl.replace junction_table point1
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Instead of adding the neighbours to an ever growing set, I think we want to keep them distinct so we know what two neighbours belong together. Consider the problem of having two lines ABC and CBA. B in this case is not a junction because the neighbours A and C are the same in both lines.

Comment thread src/topojson/topojson.ml
let neighbors = Hashtbl.find junction_table point1 in
if Set.cardinal neighbors > 2 then
ref junction := point1 :: !(ref junction)
if Set.cardinal neighbors > 2 then junction := point1 :: !junction
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

With the above changes this will become List.length when we store the list of possible neighbours and store a new pair iff they are different as defined in the algorithm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants