├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .ocamlformat ├── CHANGES.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── bench ├── bench.ml ├── dune └── input.json ├── dune ├── dune-project ├── geojson.opam ├── geojsone.opam ├── src ├── geojson │ ├── dune │ ├── geojson.ml │ ├── geojson.mli │ ├── geojson_intf.ml │ ├── index.mld │ └── optics.ml └── geojsone │ ├── dune │ ├── eio │ ├── dune │ ├── geojsone_eio.ml │ └── geojsone_eio.mli │ ├── geojsone.ml │ ├── geojsone.mli │ └── vendor │ ├── ezjsone │ └── ezjsone.ml │ ├── jsone │ └── jsone.ml │ └── uutfe │ └── uutfe.ml └── test ├── geojson ├── dune ├── files │ └── valid │ │ ├── 3d_featurecollection.json │ │ ├── feature.json │ │ ├── featurecollection.json │ │ ├── geo_with_bbox.json │ │ ├── linestring.json │ │ ├── multi_polygon.json │ │ ├── multilinestring.json │ │ ├── multipoint.json │ │ ├── point.json │ │ ├── polygon.json │ │ ├── prop1.json │ │ └── prop2.json └── test.ml ├── geojsonm └── expect │ ├── dune │ ├── input │ └── simple.geojson │ ├── test.expected │ ├── test.ml │ ├── test_iters.expected │ └── test_iters.ml └── prelude.txt /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI only for geojson 2 | 3 | on: 4 | pull_request: 5 | push: 6 | schedule: 7 | # Prime the caches every Monday 8 | - cron: 0 1 * * MON 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: 16 | - ubuntu-latest 17 | ocaml-compiler: 18 | - 4.08.X 19 | 20 | runs-on: ${{ matrix.os }} 21 | 22 | steps: 23 | - name: Checkout code 24 | uses: actions/checkout@v3 25 | 26 | - name: Use OCaml ${{ matrix.ocaml-compiler }} 27 | uses: ocaml/setup-ocaml@v2 28 | with: 29 | ocaml-compiler: ${{ matrix.ocaml-compiler }} 30 | opam-local-packages: geojson.opam 31 | 32 | - run: opam install geojson.dev --deps-only --with-test 33 | 34 | - run: opam exec -- dune build -p geojson @install @runtest @check -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _opam 3 | .vscode 4 | bench/large-file.json 5 | *install -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | version = 0.26.0 2 | profile = conventional 3 | break-infix = fit-or-vertical 4 | parse-docstrings = true 5 | module-item-spacing = compact 6 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # v0.2.0 2023-11-22 Cambridge 2 | 3 | - Remove eio dependency and replace with functions (#54 @patricoferris) 4 | 5 | # v0.1.1 2023-08-16 Cambridge 6 | 7 | - Work with eio.0.11 and hopefully beyond (#53 @patricoferris) 8 | 9 | # v0.1.0 2022-10-29 Cambridge 10 | 11 | Initial release -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## OCaml and opam 4 | 5 | After following [these instructions for setting up OCaml with opam](https://v3.ocaml.org/docs/up-and-running) you should be in a position to start building this 6 | repository. Refer to the following section for cloning the repository using git. 7 | 8 | Once you have that set up to install the dependencies you can run 9 | 10 | ``` 11 | opam install . --deps-only --with-test 12 | ``` 13 | 14 | This will install the main dependencies along with any we will need for running the tests. The build the project after opam has finished installing everything run: 15 | 16 | ``` 17 | dune build 18 | ``` 19 | 20 | And to run the tests (which are in the `test` directory) run: 21 | 22 | ``` 23 | dune runtest 24 | ``` 25 | 26 | 27 | After making changes to the code please also run the formatter to maintain a common style across the codebase. You can achieve this by running the following command: 28 | 29 | ``` 30 | dune build @fmt --auto 31 | ``` 32 | 33 | To run this command without any error, you might need to install the correct version of `ocamlformat`. The `.ocamlformat` file records the current version the repository uses. Install this version by running: 34 | 35 | ``` 36 | opam install ocamlformat=X.XX.X 37 | ``` 38 | 39 | where, `X.XX.X` denotes the version in the `.ocamlformat` file. *For example*, if the `0.20.1` version of `ocamlformat` has to be installed, then you must run `opam install ocamlformat=0.20.1`. To know more, kindly visit [OCamlFormat](https://github.com/ocaml-ppx/ocamlformat). 40 | 41 | If you hit any problems please feel free to open an issue. 42 | 43 | ## Git and GitHub workflow 44 | 45 | The preferred workflow for contributing to a repository is to fork the main repository on GitHub, clone, and develop on a new branch. 46 | 47 | If you aren't familiar with how to work with Github or would like to learn it, here is [a great tutorial](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). 48 | 49 | Feel free to use any approach while creating a pull request. Here are a few suggestions from the dev team: 50 | 51 | - If you are not sure whether your changes will be accepted or want to discuss the method before delving into it, please create an issue and ask it. 52 | - Clone the repo locally (or continue editing directly in github if the change is small). Checkout 53 | out the branch that you created. 54 | - Create a draft pull request with a small initial commit. Here's how you can [create a draft pull request.](https://github.blog/2019-02-14-introducing-draft-pull-requests/) 55 | - Continue developing, feel free to ask questions in the PR, if you run into obstacles or uncertainty as you make changes 56 | - Review your implementation according to the checks noted in the PR template 57 | - Once you feel your branch is ready, change the PR status to "ready to review" 58 | - Consult the tasks noted in the PR template 59 | - When merging, consider cleaning up the commit body 60 | - Close any issues that were addressed by this PR. 61 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2022 Patrick Ferris 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 12 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 13 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ocaml-geojson 2 | 3 | A collection of libraries for reading and writing [GeoJSON](https://www.rfc-editor.org/rfc/rfc7946). This repository contains two libraries, a non-blocking streaming GeoJSON library called `geojsone` (this requires OCaml 5 with effects) and a normal GeoJSON library. 4 | 5 | - [Geojson](#geojson) 6 | - [Reading GeoJSON Values](#reading-geojson-values) 7 | - [Foreign Members](#foreign-members) 8 | - [Accessing Deeply Nested Objects with Optics](#accessing-deeply-nested-objects-with-optics) 9 | - [Building GeoJSON values](#building-geojson-values) 10 | - [Geojsone](#geojsone) 11 | - [Constructing Decoders and Encoders](#constructing-decoders-and-encoders) 12 | - [Eio sources and sinks](#eio-sources-and-sinks) 13 | - [Mapping](#mapping) 14 | - [Folding](#folding) 15 | 16 | ## Geojson 17 | 18 | The `geojson` library allows you to parse GeoJSON objects. The implementation requires you to provide a JSON parser of your choosing. Take a look in [the prelude file](./docs/prelude.txt) for an implementation using [ezjsonm](https://github.com/mirage/ezjsonm). The prelude also contains encoded GeoJSON objects as OCaml strings for use in the examples. 19 | 20 | The first thing to do is create a GeoJSON parser from your JSON parser. 21 | 22 | ```ocaml 23 | module G = Geojson.Make (Ezjsonm_parser) 24 | ``` 25 | 26 | ### Reading GeoJSON Values 27 | 28 | Reading values relies on your JSON parser's methods for creating a JSON value. With `ezjsonm` we can read strings. 29 | 30 | ```ocaml 31 | # let feature = G.of_json (Ezjsonm.value_from_string feature_example);; 32 | val feature : (G.t, [ `Msg of string ]) result = Ok 33 | ``` 34 | 35 | This returns a result, so either `Ok g` where `g` is a GeoJSON object or an `Error`. 36 | 37 | ```ocaml 38 | # let feature = Result.get_ok feature;; 39 | val feature : G.t = 40 | ``` 41 | 42 | A GeoJSON object can either be a feature, a geometry or a feature collection. To know which one you have, you will need to pattern match on the value for the `geojson` value. With this example we know we should have a feature so we will just `assert false`. 43 | 44 | ```ocaml 45 | # let f = match G.geojson feature with 46 | | G.Feature f -> f 47 | | _ -> assert false;; 48 | val f : G.Feature.t = 49 | ``` 50 | 51 | Now we can access feature specific values from our OCaml value. 52 | 53 | ```ocaml 54 | # let props = G.Feature.properties f;; 55 | val props : G.json option = Some (`O [("name", `String "Dinagat Islands")]) 56 | ``` 57 | 58 | ### Foreign Members 59 | 60 | Foreign members are those JSON key-value pairs that are not a part of the specification. Sometimes your GeoJSON data might include extra information and this is a way to gain access to it after you have parsed the value. 61 | 62 | ```ocaml 63 | # G.Feature.foreign_members f;; 64 | - : (string * G.json) list = [("title", `String "Some Islands")] 65 | ``` 66 | 67 | ### Accessing Deeply Nested Objects with Optics 68 | 69 | There is an experimental module in the library called `Geojson.Accessor`. This uses [optics]() to allow you to more easily access values that are deeply nested. An important note is that they will always tend to be less efficient than manually pattern-matching. 70 | 71 | However, using our feature as an example, if we wanted to access the multipoint without matching all the way down, we can construct an optic to help us. 72 | 73 | ```ocaml 74 | # let g_to_mp = G.Accessor.(geojson >& feature &> Feature.geometry_exn &> Geometry.geometry $> Geometry.multipoint);; 75 | val g_to_mp : (G.t, G.Geometry.MultiPoint.t) G.Accessor.Optics.Optional.t = 76 | Geojson__Optics.Lens.V (, ) 77 | ``` 78 | 79 | This is a lens that lets use focus all the way down from the GeoJSON object containing a feature, containing a geometry that is a multipoint. 80 | 81 | ```ocaml 82 | # G.Accessor.get g_to_mp feature |> Option.get |> G.Geometry.MultiPoint.coordinates;; 83 | - : G.Geometry.Position.t array = [|[|125.1; 40.|]; [|155.9; 22.5|]|] 84 | ``` 85 | 86 | ### Building GeoJSON values 87 | 88 | You can also construct GeoJSON objects using OCaml values. 89 | 90 | ```ocaml 91 | # let geometry = 92 | G.Geometry.(v 93 | ~foreign_members:["hello", `String "World"] 94 | (Point (Point.v (Position.v ~lat:1.123 ~lng:2.321 ()))));; 95 | val geometry : G.Geometry.t = 96 | # let g = G.(v (Geometry geometry));; 97 | val g : G.t = 98 | # G.to_json g |> Ezjsonm.value_to_string;; 99 | - : string = 100 | "{\"type\":\"Point\",\"coordinates\":[2.321,1.123],\"hello\":\"World\"}" 101 | ``` 102 | 103 | ## Geojsone 104 | 105 | Geojsone is a non-blocking, streaming parser for GeoJSON objects. Currently, it 106 | uses a modified version of [jsonm](http://erratique.ch/software/jsonm), called jsone. 107 | It uses effects to provide non-blocking reading and writing functions rather than 108 | passing continuations the whole way through the parser. It is still experimental. 109 | 110 | ### Constructing Decoders and Encoders 111 | 112 | In order to build decoders and encoder, you must provide a source and destination. 113 | 114 | ```ocaml 115 | # #show_type Geojsone.Jsone.src;; 116 | type nonrec src = unit -> Cstruct.t 117 | # #show_type Geojsone.Jsone.dst;; 118 | type nonrec dst = Cstruct.t -> unit 119 | ``` 120 | 121 | These are functions for filling a buffer (a `Cstruct.t`) and reading a buffer. Note they appear as normal OCaml functions (no IO monad like `Lwt.t`). You will have to use a library that uses effects for non-blocking IO in order to make the decoders and encoders non-blocking. If you don't mind blocking (for example, in js_of_ocaml) you can provide blocking versions of these functions. 122 | 123 | #### Eio sources and sinks 124 | 125 | The `geojsone.eio` sublibrary has some useful Eio-related functions for working with the `geojsone` libary. 126 | 127 | ```ocaml 128 | # let src_of_flow = Geojsone_eio.src_of_flow;; 129 | val src_of_flow : 130 | ?buff:Cstruct.t -> [> Eio.Flow.source_ty ] Eio.Std.r -> Geojsone.Jsone.src = 131 | 132 | # let buffer_to_dst buf = Geojsone_eio.dst_of_flow (Eio.Flow.buffer_sink buf);; 133 | val buffer_to_dst : Buffer.t -> Geojsone.Jsone.dst = 134 | ``` 135 | 136 | Note that your source function should raise `End_of_file` when there are no more bytes to be read. `Eio.Flow.single_read` does this. 137 | 138 | With both of these we can now construct an encoder and decoder. 139 | 140 | ```ocaml 141 | # let decoder s = Geojsone.Jsone.decoder (src_of_flow @@ Eio.Flow.string_source s);; 142 | val decoder : string -> Geojsone.Jsone.decoder = 143 | # let encoder buf = Geojsone.Jsone.encoder (buffer_to_dst buf);; 144 | val encoder : Buffer.t -> Geojsone.Jsone.encoder = 145 | ``` 146 | 147 | ### Mapping 148 | 149 | There are various mapping functions for iterating over a GeoJSON object. For example, you may wish to visit all of the `properties` in your object. 150 | 151 | ```ocaml 152 | # Geojsone.map_props;; 153 | - : (Geojsone.G.json -> Geojsone.G.json) -> 154 | Geojsone.Jsone.src -> Geojsone.Jsone.dst -> (unit, Geojsone.Err.t) result 155 | = 156 | ``` 157 | 158 | We can see if any properties are objects with a field called `name`, and capitalise its value. 159 | 160 | ```ocaml 161 | let capitalise_name = function 162 | | `O [ "name", `String s ] -> `O [ "name", `String (String.uppercase_ascii s) ] 163 | | v -> v 164 | let buf = Buffer.create 256 165 | ``` 166 | 167 | We can then use this function for our example. 168 | 169 | ```ocaml 170 | # let feature_source () = src_of_flow @@ Eio.Flow.string_source feature_example;; 171 | val feature_source : unit -> Geojsone.Jsone.src = 172 | # Geojsone.(map_props capitalise_name (feature_source ()) (buffer_to_dst buf));; 173 | - : (unit, Geojsone.Err.t) result = Ok () 174 | # Buffer.contents buf;; 175 | - : string = 176 | "{\"type\":\"Feature\",\"geometry\":{\"type\":\"MultiPoint\",\"coordinates\":[[125.1,40],[155.9,22.5]]},\"properties\":{\"name\":\"DINAGAT ISLANDS\"},\"title\":\"Some Islands\"}" 177 | ``` 178 | 179 | ### Folding 180 | 181 | Folding is similar to mapping except you can accumulate a value as you iterate over the document. 182 | 183 | ```ocaml 184 | # Geojsone.fold_geometry;; 185 | - : ('a -> Geojsone.G.Geometry.t -> 'a) -> 186 | 'a -> Geojsone.Jsone.src -> ('a, Geojsone.Err.t) result 187 | = 188 | ``` 189 | 190 | So we could simply count the number of geometry objects for example. 191 | 192 | ```ocaml 193 | let count_geometries acc _ = acc + 1 194 | ``` 195 | 196 | And we can apply it to our running example. 197 | 198 | ```ocaml 199 | # Geojsone.fold_geometry count_geometries 0 (feature_source ()) ;; 200 | - : (int, Geojsone.Err.t) result = Ok 1 201 | ``` 202 | -------------------------------------------------------------------------------- /bench/bench.ml: -------------------------------------------------------------------------------- 1 | (* Comparing Jsonm and Jsone for simple 2 | reading and writing JSON files. *) 3 | module Jsone = Geojsone.Jsone 4 | 5 | let ensure_ok = function `Ok -> () | `Partial -> assert false 6 | 7 | let read_write decode encode = 8 | let rec loop () = 9 | match decode () with 10 | | `Lexeme _ as v -> 11 | encode v |> ensure_ok; 12 | loop () 13 | | `End -> () 14 | | `Error _ -> failwith "Error in read-write!" 15 | | `Await -> assert false 16 | in 17 | loop () 18 | 19 | let run_test_effects ~file ~out = 20 | Eio.Path.with_open_in file @@ fun in_flow -> 21 | Eio.Path.with_open_out ~create:(`If_missing 0o666) out @@ fun out_flow -> 22 | let decoder = Jsone.decoder (src_of_flow in_flow) in 23 | let encoder = Jsone.encoder (dst_of_flow out_flow) in 24 | read_write (fun () -> Jsone.decode decoder) (Jsone.encode encoder) 25 | 26 | let run_test ~file ~out = 27 | In_channel.with_open_bin file @@ fun ic -> 28 | Out_channel.with_open_bin out @@ fun oc -> 29 | let decoder = Jsonm.decoder (`Channel ic) in 30 | let encoder = Jsonm.encoder (`Channel oc) in 31 | read_write (fun () -> Jsonm.decode decoder) (Jsonm.encode encoder) 32 | 33 | let in_file fs = Eio.Path.(fs / "./bench/large-file.json") 34 | let out_file fs = Eio.Path.(fs / "./test.geojson") 35 | 36 | let run_bench ~fs ~effects ~clock ~n_iters = 37 | Gc.full_major (); 38 | let file = in_file fs in 39 | let out = out_file fs in 40 | let _minor0, prom0, _major0 = Gc.counters () in 41 | let t0 = Eio.Time.now clock in 42 | for _i = 0 to n_iters do 43 | match effects with 44 | | true -> run_test_effects ~file ~out 45 | | false -> run_test ~file:(snd file) ~out:(snd out) 46 | done; 47 | let t1 = Eio.Time.now clock in 48 | let time_total = t1 -. t0 in 49 | let time_per_iter = time_total /. float n_iters in 50 | let _minor1, prom1, _major1 = Gc.counters () in 51 | let prom = prom1 -. prom0 in 52 | Eio.Path.unlink out; 53 | Printf.printf "%8d, %7.2f, %13.4f\n%!" n_iters (1e9 *. time_per_iter) 54 | (prom /. float n_iters) 55 | 56 | let main ~fs ~clock ~effects = 57 | Printf.printf " n_iters, ns/iter, promoted/iter\n%!"; 58 | [ 1; 5; 10 ] 59 | |> List.iter (fun n_iters -> run_bench ~fs ~effects ~clock ~n_iters) 60 | 61 | let () = 62 | Printf.printf "<><><><> Jsonm <><><><>\n%!"; 63 | Eio_main.run @@ fun env -> 64 | let fs = env#fs in 65 | main ~fs ~effects:false ~clock:(Eio.Stdenv.clock env); 66 | Printf.printf "<><><><> Jsone <><><><>\n%!"; 67 | main ~fs ~effects:true ~clock:(Eio.Stdenv.clock env) 68 | -------------------------------------------------------------------------------- /bench/dune: -------------------------------------------------------------------------------- 1 | ; (executable 2 | ; (name bench) 3 | ; (libraries eio_main geojsone jsonm)) 4 | -------------------------------------------------------------------------------- /bench/input.json: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[1.49886,43.09493],[1.50194,43.09495],[1.50487,43.09542],[1.50774,43.09653],[1.51541,43.09749],[1.51878,43.09719],[1.52344,43.09858],[1.5239,43.10001],[1.52728,43.10058],[1.52862,43.10136],[1.53029,43.10047],[1.53139,43.09638],[1.53073,43.09502],[1.52915,43.09403],[1.52906,43.09177],[1.52817,43.08966],[1.52867,43.08782],[1.53017,43.08665],[1.53063,43.08391],[1.53336,43.08186],[1.528,43.07899],[1.52657,43.07627],[1.52457,43.07388],[1.5244,43.07221],[1.52517,43.07046],[1.52672,43.0689],[1.52764,43.06459],[1.52944,43.06266],[1.53187,43.06096],[1.53532,43.05982],[1.54835,43.07031],[1.54656,43.07094],[1.5471,43.07336],[1.55135,43.07628],[1.55274,43.07872],[1.55414,43.07954],[1.55531,43.07858],[1.55383,43.07648],[1.55628,43.0764],[1.56261,43.0796],[1.56186,43.08062],[1.56405,43.08355],[1.56301,43.08779],[1.56615,43.09109],[1.57137,43.09144],[1.57617,43.09111],[1.57821,43.09032],[1.58135,43.08976],[1.58284,43.08864],[1.58627,43.08758],[1.59249,43.08623],[1.59508,43.08683],[1.59888,43.08663],[1.59964,43.08524],[1.59897,43.08416],[1.5992,43.08087],[1.598,43.07866],[1.59861,43.07438],[1.59591,43.07342],[1.60246,43.07058],[1.6038,43.07028],[1.61314,43.07088],[1.61493,43.06927],[1.61659,43.0691],[1.62389,43.06942],[1.62796,43.06837],[1.62903,43.07058],[1.62815,43.07305],[1.62936,43.07427],[1.63009,43.07963],[1.6313,43.08171],[1.63566,43.0865],[1.63559,43.08786],[1.63832,43.0899],[1.63843,43.09215],[1.64021,43.09185],[1.64155,43.09299],[1.64682,43.09396],[1.64894,43.09154],[1.65376,43.09301],[1.65439,43.09225],[1.66009,43.09309],[1.66273,43.09284],[1.66493,43.09448],[1.66998,43.09234],[1.67203,43.09181],[1.68038,43.09097],[1.68235,43.09021],[1.6854,43.09333],[1.69332,43.09306],[1.69358,43.09199],[1.69193,43.09049],[1.69,43.08742],[1.68891,43.08642],[1.68959,43.08494],[1.69276,43.08325],[1.69337,43.082],[1.69289,43.08063],[1.69519,43.07935],[1.69669,43.07719],[1.69768,43.07477],[1.70005,43.07231],[1.70511,43.07464],[1.70886,43.07488],[1.71229,43.07449],[1.71092,43.07097],[1.71175,43.06846],[1.71043,43.06599],[1.71119,43.06395],[1.7107,43.06238],[1.71141,43.05964],[1.70751,43.05665],[1.70607,43.05371],[1.70799,43.0521],[1.71453,43.04894],[1.71821,43.04897],[1.71822,43.0478],[1.71947,43.04645],[1.72131,43.04642],[1.72329,43.0449],[1.72809,43.04312],[1.72957,43.04187],[1.73253,43.04135],[1.73446,43.04333],[1.7377,43.04542],[1.74275,43.04973],[1.74468,43.05334],[1.75072,43.06331],[1.75594,43.0619],[1.76385,43.05811],[1.7673,43.05683],[1.77117,43.05582],[1.77162,43.05411],[1.77058,43.05243],[1.77161,43.04771],[1.77088,43.04614],[1.7672,43.04008],[1.76593,43.03864],[1.76656,43.03608],[1.76553,43.03554],[1.75905,43.03361],[1.75659,43.03346],[1.75204,43.03435],[1.75033,43.0332],[1.75116,43.03132],[1.75056,43.02852],[1.75114,43.02538],[1.75105,43.02299],[1.74862,43.0206],[1.74612,43.01919],[1.74558,43.01729],[1.74756,43.01609],[1.74907,43.01173],[1.74811,43.00938],[1.74598,43.00806],[1.74368,43.00486],[1.74264,43.00436],[1.74273,43.00116],[1.74409,43.00073],[1.74509,42.99888],[1.74849,42.99692],[1.74746,42.99453],[1.75087,42.99166],[1.75357,42.99015],[1.75335,42.98862],[1.75624,42.98746],[1.7564,42.98341],[1.75724,42.98022],[1.75452,42.98084],[1.74996,42.98261],[1.74574,42.9835],[1.74458,42.98336],[1.74219,42.98177],[1.74227,42.98015],[1.74002,42.97941],[1.73708,42.97773],[1.73463,42.97763],[1.73348,42.97541],[1.73104,42.975],[1.72387,42.97569],[1.72175,42.97243],[1.71917,42.97308],[1.7142,42.97379],[1.71239,42.97084],[1.71247,42.9695],[1.71634,42.9679],[1.71619,42.9652],[1.71823,42.96385],[1.71701,42.96241],[1.71874,42.96039],[1.71617,42.96052],[1.71479,42.95884],[1.71252,42.95824],[1.70955,42.95611],[1.70648,42.95573],[1.70538,42.95488],[1.70073,42.9548],[1.7051,42.95343],[1.70834,42.95291],[1.71125,42.95158],[1.71347,42.94933],[1.71531,42.94845],[1.71774,42.94607],[1.71899,42.94317],[1.72114,42.94227],[1.72538,42.9359],[1.72655,42.93524],[1.72584,42.9328],[1.72436,42.93207],[1.72186,42.93219],[1.72268,42.92982],[1.72011,42.92845],[1.71922,42.9262],[1.72189,42.92224],[1.72106,42.9203],[1.72018,42.91625],[1.71875,42.91377],[1.72203,42.91522],[1.72323,42.91708],[1.72453,42.91749],[1.72685,42.91711],[1.72889,42.91473],[1.7305,42.91388],[1.73527,42.91278],[1.74378,42.91459],[1.74715,42.91378],[1.75125,42.9139],[1.7538,42.91553],[1.75596,42.91451],[1.75834,42.91493],[1.76222,42.91366],[1.76508,42.91442],[1.76754,42.91403],[1.77072,42.91414],[1.77368,42.91235],[1.77168,42.90919],[1.77129,42.90635],[1.77031,42.90496],[1.76824,42.90354],[1.76857,42.90135],[1.76694,42.89962],[1.76658,42.89809],[1.76224,42.89775],[1.75959,42.89239],[1.76061,42.8896],[1.76314,42.88754],[1.76254,42.88565],[1.75974,42.88406],[1.7562,42.88279],[1.75504,42.88174],[1.74883,42.87896],[1.74708,42.87711],[1.73649,42.87085],[1.73284,42.86911],[1.72863,42.86827],[1.72747,42.86764],[1.72614,42.8657],[1.72616,42.8644],[1.72464,42.86281],[1.72149,42.86108],[1.71936,42.8581],[1.71622,42.85543],[1.71624,42.854],[1.71851,42.85256],[1.71717,42.85165],[1.71701,42.84999],[1.71602,42.8494],[1.71611,42.84787],[1.71467,42.84493],[1.71579,42.84274],[1.71546,42.83958],[1.7221,42.83597],[1.72378,42.83323],[1.72666,42.83352],[1.73039,42.83215],[1.73205,42.83063],[1.73525,42.83042],[1.73991,42.82896],[1.7423,42.82777],[1.74403,42.82606],[1.74832,42.82509],[1.75608,42.81945],[1.7612,42.82048],[1.76407,42.82063],[1.76922,42.81921],[1.77332,42.81888],[1.77755,42.81799],[1.78115,42.81805],[1.78306,42.81743],[1.78481,42.81915],[1.78603,42.81957],[1.78775,42.81832],[1.79154,42.81779],[1.79748,42.818],[1.80101,42.81883],[1.80334,42.81857],[1.80495,42.81646],[1.80661,42.8153],[1.80838,42.81508],[1.81039,42.81564],[1.81553,42.8148],[1.81725,42.81373],[1.82204,42.81281],[1.82685,42.81468],[1.82917,42.816],[1.83282,42.81673],[1.83441,42.81638],[1.83655,42.81761],[1.83984,42.81739],[1.84473,42.818],[1.84997,42.81805],[1.8545,42.81921],[1.85938,42.82081],[1.85994,42.81959],[1.86215,42.81762],[1.86589,42.81728],[1.86816,42.81553],[1.87123,42.81455],[1.87399,42.81411],[1.87795,42.81413],[1.88163,42.81246],[1.88414,42.81245],[1.88578,42.81167],[1.88807,42.81147],[1.89394,42.80965],[1.89609,42.80867],[1.89775,42.80715],[1.89856,42.80431],[1.89839,42.80269],[1.90096,42.80077],[1.90612,42.79841],[1.90761,42.79566],[1.90731,42.79436],[1.90823,42.79292],[1.9134,42.78893],[1.91482,42.78593],[1.91963,42.78081],[1.92134,42.77992],[1.92338,42.77769],[1.92884,42.77495],[1.9235,42.77151],[1.9217,42.76996],[1.91788,42.76992],[1.91324,42.76918],[1.90938,42.76921],[1.90818,42.76767],[1.90899,42.76525],[1.91139,42.76251],[1.91361,42.75788],[1.91979,42.75719],[1.92207,42.75517],[1.92982,42.7552],[1.93179,42.75345],[1.93094,42.75115],[1.9334,42.75031],[1.9367,42.74996],[1.93861,42.74794],[1.94129,42.74688],[1.94644,42.74576],[1.94717,42.74437],[1.94689,42.74172],[1.94775,42.73915],[1.9488,42.73799],[1.95163,42.73669],[1.95949,42.73686],[1.96249,42.73771],[1.96365,42.73696],[1.9681,42.7363],[1.97025,42.73535],[1.97585,42.73668],[1.98178,42.73602],[1.98452,42.73675],[1.98686,42.73519],[1.99144,42.7348],[1.99724,42.73594],[2.00009,42.73595],[2.00323,42.73457],[2.00384,42.73348],[2.00793,42.73412],[2.0117,42.73648],[2.01448,42.73973],[2.01998,42.74002],[2.02346,42.73903],[2.02543,42.73899],[2.02681,42.73999],[2.03078,42.74148],[2.03142,42.74652],[2.03472,42.74739],[2.03918,42.74614],[2.04163,42.74633],[2.04327,42.74737],[2.04784,42.74904],[2.04978,42.75139],[2.05234,42.75373],[2.05641,42.756],[2.05769,42.75614],[2.0632,42.75507],[2.06613,42.75412],[2.06997,42.75432],[2.07181,42.75364],[2.07505,42.7537],[2.07791,42.75312],[2.07976,42.75214],[2.0814,42.75228],[2.0839,42.75161],[2.08629,42.7518],[2.08594,42.7494],[2.08501,42.74851],[2.08638,42.74594],[2.08639,42.7441],[2.08749,42.74122],[2.08744,42.73901],[2.08892,42.73717],[2.09217,42.73466],[2.09358,42.73448],[2.09638,42.73314],[2.09883,42.73328],[2.10084,42.73148],[2.10206,42.73148],[2.10471,42.7292],[2.10806,42.72722],[2.11155,42.72678],[2.11381,42.7245],[2.11723,42.72381],[2.11925,42.72153],[2.12084,42.7214],[2.12072,42.71995],[2.1233,42.71857],[2.12506,42.71925],[2.12678,42.71683],[2.13155,42.71494],[2.13356,42.71471],[2.13595,42.71324],[2.13955,42.7123],[2.14078,42.71046],[2.14061,42.70582],[2.14415,42.70448],[2.14666,42.70138],[2.14788,42.70097],[2.15203,42.70156],[2.15466,42.70135],[2.15971,42.70221],[2.16211,42.69951],[2.16528,42.69821],[2.16786,42.69618],[2.17133,42.69111],[2.1714,42.6895],[2.17587,42.6828],[2.176,42.68145],[2.17308,42.67797],[2.17242,42.67576],[2.16748,42.67338],[2.16532,42.66585],[2.16605,42.66392],[2.15965,42.66435],[2.15386,42.66224],[2.15129,42.66196],[2.14898,42.66277],[2.14337,42.6637],[2.13262,42.66836],[2.1301,42.67115],[2.12784,42.67227],[2.12388,42.67168],[2.11798,42.66847],[2.11201,42.66643],[2.10897,42.66386],[2.10678,42.66525],[2.10428,42.66506],[2.09824,42.66402],[2.09611,42.66402],[2.09189,42.66567],[2.08659,42.66544],[2.0853,42.66678],[2.08378,42.66669],[2.0811,42.66465],[2.07634,42.66477],[2.07049,42.66639],[2.06976,42.66495],[2.0677,42.66412],[2.06458,42.66426],[2.06088,42.66168],[2.05452,42.66306],[2.04702,42.66359],[2.0443,42.66016],[2.04327,42.6575],[2.02763,42.65251],[2.02256,42.65339],[2.01879,42.65342],[2.01725,42.65472],[2.01353,42.65611],[2.01225,42.65695],[2.01248,42.65944],[2.007,42.65861],[2.00455,42.65936],[2.00369,42.66022],[2.00051,42.66156],[1.99856,42.66133],[1.99692,42.66007],[1.99736,42.65719],[1.997,42.65611],[1.9953,42.65506],[1.99392,42.65268],[1.99257,42.65232],[1.99271,42.65083],[1.99077,42.64664],[1.98793,42.64479],[1.98635,42.64086],[1.98668,42.64032],[1.98528,42.63722],[1.98257,42.63347],[1.97995,42.63166],[1.97978,42.63039],[1.97759,42.62927],[1.97583,42.62693],[1.97597,42.62481],[1.97726,42.62355],[1.97636,42.62107],[1.97413,42.61941],[1.97176,42.61692],[1.96829,42.61637],[1.96525,42.61631],[1.96311,42.61712],[1.96043,42.61693],[1.9581,42.61746],[1.95602,42.61871],[1.95401,42.61889],[1.95293,42.61816],[1.95012,42.61774],[1.94722,42.61468],[1.94363,42.61314],[1.94451,42.61003],[1.94379,42.60855],[1.93887,42.60686],[1.93497,42.60492],[1.93339,42.60482],[1.92991,42.60611],[1.92666,42.60943],[1.92477,42.60879],[1.92228,42.60932],[1.91467,42.60763],[1.9115,42.60761],[1.90729,42.60863],[1.90368,42.61154],[1.90018,42.6149],[1.89748,42.60905],[1.89305,42.60633],[1.89361,42.60476],[1.89546,42.60233],[1.89413,42.60016],[1.89318,42.59616],[1.89127,42.59331],[1.88709,42.59055],[1.88385,42.58987],[1.88019,42.59007],[1.87423,42.59],[1.8735,42.58942],[1.87317,42.58541],[1.87449,42.58141],[1.87015,42.58143],[1.86938,42.57999],[1.86432,42.57899],[1.8617,42.58135],[1.86038,42.58401],[1.85724,42.58278],[1.85617,42.5816],[1.8539,42.58129],[1.85365,42.58232],[1.85157,42.58433],[1.84821,42.58372],[1.84707,42.58305],[1.84176,42.58303],[1.83907,42.58441],[1.83706,42.58454],[1.83131,42.58149],[1.82473,42.58124],[1.82157,42.57956],[1.81768,42.57954],[1.81599,42.57818],[1.80634,42.57458],[1.801,42.5724],[1.79874,42.57239],[1.79601,42.57148],[1.79039,42.574],[1.78766,42.573],[1.78613,42.57362],[1.78661,42.57431],[1.78441,42.57587],[1.78313,42.58012],[1.78094,42.5826],[1.77741,42.58205],[1.77316,42.58068],[1.76805,42.58052],[1.76074,42.58092],[1.75859,42.58195],[1.75475,42.58292],[1.75286,42.58264],[1.74961,42.58469],[1.74846,42.58468],[1.74197,42.58775],[1.73539,42.58791],[1.73375,42.58853],[1.73173,42.58806],[1.72826,42.58841],[1.72612,42.58969],[1.72631,42.60031],[1.72899,42.60123],[1.73127,42.60399],[1.73114,42.60569],[1.7319,42.60741],[1.73519,42.60851],[1.73663,42.61077],[1.73796,42.61131],[1.73727,42.61424],[1.73622,42.61503],[1.73607,42.61715],[1.73353,42.61579],[1.72951,42.61482],[1.72817,42.61563],[1.72628,42.61548],[1.72281,42.61636],[1.7202,42.61504],[1.71411,42.61461],[1.7102,42.61633],[1.70705,42.6187],[1.70063,42.62119],[1.69818,42.62274],[1.69204,42.62195],[1.69031,42.62325],[1.68671,42.6239],[1.6836,42.62486],[1.68056,42.62481],[1.67838,42.6225],[1.67577,42.6223],[1.67346,42.62081],[1.67205,42.62125],[1.66687,42.62153],[1.6644,42.61904],[1.66203,42.61948],[1.66086,42.62037],[1.65689,42.62115],[1.65553,42.62255],[1.65496,42.62447],[1.65372,42.62631],[1.63883,42.62842],[1.63716,42.6303],[1.63072,42.62779],[1.62788,42.62633],[1.62232,42.62728],[1.61953,42.62583],[1.61691,42.62662],[1.61149,42.62667],[1.60934,42.62855],[1.60762,42.62832],[1.60357,42.62585],[1.60205,42.62548],[1.59974,42.62619],[1.59527,42.63179],[1.59313,42.63313],[1.58568,42.63357],[1.5842,42.6341],[1.58236,42.63589],[1.57978,42.63753],[1.57921,42.63902],[1.57944,42.64117],[1.57845,42.64229],[1.57599,42.64336],[1.57589,42.6462],[1.57489,42.64803],[1.57192,42.64796],[1.56879,42.64843],[1.56707,42.64973],[1.56607,42.65148],[1.56239,42.65348],[1.5605,42.65333],[1.55832,42.65215],[1.55562,42.6533],[1.5533,42.65368],[1.54925,42.65578],[1.54396,42.6542],[1.54093,42.65284],[1.53832,42.65255],[1.53548,42.65009],[1.53384,42.64968],[1.53083,42.65124],[1.52864,42.65144],[1.52638,42.65084],[1.52458,42.64956],[1.52215,42.64918],[1.52143,42.64743],[1.51891,42.64548],[1.51262,42.64615],[1.5058,42.6452],[1.50288,42.64544],[1.50061,42.64515],[1.49925,42.6474],[1.49758,42.64873],[1.49375,42.65321],[1.49106,42.65297],[1.48814,42.65194],[1.48474,42.65206],[1.4801,42.65139],[1.47877,42.65165],[1.47874,42.6494],[1.47809,42.64675],[1.47702,42.64524],[1.47796,42.6435],[1.47691,42.64074],[1.4757,42.63957],[1.46991,42.63565],[1.46993,42.63385],[1.46834,42.63082],[1.47098,42.62918],[1.47142,42.62824],[1.47053,42.62626],[1.47105,42.62467],[1.47382,42.62141],[1.47428,42.61939],[1.47573,42.61656],[1.47727,42.61497],[1.47656,42.61302],[1.47423,42.61089],[1.47042,42.60869],[1.46946,42.60689],[1.46777,42.60638],[1.46478,42.60685],[1.46339,42.60562],[1.46175,42.60562],[1.45981,42.60475],[1.45704,42.60229],[1.4504,42.6026],[1.44782,42.60379],[1.44282,42.60366],[1.43783,42.60321],[1.43567,42.60581],[1.4334,42.60692],[1.43302,42.60831],[1.43085,42.61136],[1.42923,42.61269],[1.42933,42.61444],[1.43056,42.61733],[1.4281,42.61845],[1.42728,42.62095],[1.42379,42.62201],[1.42206,42.62397],[1.4207,42.62473],[1.42019,42.62607],[1.41996,42.62949],[1.41872,42.63069],[1.41974,42.63223],[1.41807,42.63348],[1.41652,42.63568],[1.41593,42.63764],[1.41617,42.63913],[1.4173,42.64],[1.41691,42.64184],[1.41444,42.64407],[1.41366,42.64766],[1.41466,42.6496],[1.41359,42.65145],[1.41441,42.65402],[1.41366,42.65552],[1.40837,42.65759],[1.40537,42.65721],[1.40315,42.65887],[1.40377,42.66002],[1.40196,42.66188],[1.40132,42.66457],[1.39664,42.66805],[1.39424,42.66839],[1.38986,42.66804],[1.38778,42.66837],[1.38773,42.67099],[1.38888,42.67289],[1.38891,42.67427],[1.38703,42.67741],[1.38876,42.67991],[1.38752,42.68151],[1.3896,42.68508],[1.38933,42.68679],[1.3866,42.68952],[1.38469,42.69067],[1.381,42.69181],[1.37737,42.69452],[1.37248,42.69475],[1.36955,42.69563],[1.3651,42.69451],[1.3617,42.69776],[1.35693,42.69867],[1.35317,42.70035],[1.35124,42.70266],[1.35103,42.7046],[1.35235,42.70623],[1.35195,42.70835],[1.35303,42.70971],[1.35288,42.71128],[1.35528,42.71355],[1.35801,42.71493],[1.35803,42.71694],[1.35738,42.71941],[1.35914,42.72407],[1.36029,42.72485],[1.36344,42.72559],[1.36479,42.72898],[1.36627,42.73147],[1.37193,42.73291],[1.37452,42.73136],[1.37759,42.73017],[1.37978,42.73086],[1.38295,42.73112],[1.38521,42.73182],[1.38703,42.73159],[1.39139,42.73465],[1.38908,42.74134],[1.39178,42.74478],[1.39149,42.74766],[1.39682,42.7541],[1.39696,42.75689],[1.39955,42.75939],[1.40014,42.76461],[1.40384,42.76644],[1.40497,42.76829],[1.40506,42.77028],[1.40595,42.77208],[1.40408,42.77464],[1.40287,42.77775],[1.40431,42.77954],[1.40491,42.78333],[1.40721,42.78595],[1.40696,42.79027],[1.40786,42.79078],[1.40635,42.79328],[1.40737,42.79518],[1.40458,42.79727],[1.4064,42.80246],[1.40847,42.80298],[1.41046,42.80493],[1.41424,42.80929],[1.41397,42.81126],[1.41435,42.81419],[1.41602,42.81654],[1.41521,42.8178],[1.4158,42.81933],[1.41852,42.82142],[1.4211,42.82491],[1.42772,42.82699],[1.43373,42.82997],[1.43552,42.8297],[1.43752,42.83036],[1.43996,42.83032],[1.4405,42.83178],[1.44291,42.83445],[1.4417,42.83705],[1.44016,42.83865],[1.44043,42.84065],[1.43945,42.84428],[1.43942,42.84639],[1.44185,42.8479],[1.44326,42.85128],[1.44991,42.85741],[1.45112,42.85814],[1.4501,42.8611],[1.45084,42.86458],[1.45031,42.86722],[1.45033,42.87096],[1.45225,42.8726],[1.45275,42.87593],[1.45364,42.87873],[1.45471,42.87986],[1.45293,42.88106],[1.45192,42.8834],[1.44927,42.88418],[1.44838,42.88621],[1.44579,42.88802],[1.45102,42.88992],[1.45154,42.89199],[1.45114,42.8942],[1.45148,42.89617],[1.4531,42.89926],[1.45759,42.90563],[1.45494,42.90632],[1.45376,42.9074],[1.44947,42.90822],[1.44645,42.90937],[1.44458,42.91165],[1.44268,42.91186],[1.438,42.9139],[1.43731,42.91488],[1.43413,42.91503],[1.42954,42.91436],[1.42695,42.91515],[1.42224,42.91498],[1.41285,42.91665],[1.41063,42.91781],[1.41093,42.92146],[1.41213,42.92268],[1.41198,42.92965],[1.41113,42.93352],[1.41104,42.93856],[1.41332,42.94245],[1.41632,42.94558],[1.42123,42.94917],[1.42446,42.95059],[1.42656,42.95394],[1.42723,42.95778],[1.43062,42.96032],[1.43398,42.96084],[1.43643,42.96181],[1.44481,42.96237],[1.45186,42.96207],[1.45674,42.96391],[1.45845,42.96397],[1.46125,42.96538],[1.46072,42.96804],[1.46185,42.97016],[1.46308,42.97053],[1.46749,42.97034],[1.47232,42.97114],[1.47567,42.97346],[1.4806,42.97584],[1.48615,42.97714],[1.48502,42.97943],[1.48524,42.98177],[1.48704,42.98408],[1.48898,42.98535],[1.4945,42.98499],[1.49615,42.98554],[1.49551,42.99143],[1.49765,42.99221],[1.49974,42.99169],[1.50341,42.99248],[1.50771,42.99211],[1.50919,42.99109],[1.51213,42.99112],[1.51268,42.9917],[1.51431,42.99729],[1.51879,42.99723],[1.52023,42.99922],[1.52051,43.0017],[1.519,43.00448],[1.51744,43.00632],[1.51178,43.00852],[1.5114,43.00964],[1.51414,43.00967],[1.51722,43.00888],[1.5222,43.00864],[1.52095,43.00994],[1.51897,43.01587],[1.52028,43.01853],[1.51887,43.01865],[1.52074,43.02146],[1.51851,43.02234],[1.51668,43.02201],[1.51567,43.02382],[1.51356,43.0256],[1.51281,43.02734],[1.51133,43.02715],[1.50719,43.02974],[1.50686,43.03054],[1.50392,43.03098],[1.49948,43.03251],[1.50031,43.03567],[1.50127,43.0367],[1.50401,43.03768],[1.50465,43.03975],[1.50683,43.04265],[1.49874,43.04628],[1.49644,43.04751],[1.49629,43.05004],[1.45429,43.0684],[1.45395,43.07078],[1.45576,43.07281],[1.4578,43.07594],[1.45913,43.07703],[1.463,43.07755],[1.46623,43.0787],[1.46866,43.0816],[1.46917,43.08336],[1.47078,43.08257],[1.47352,43.08466],[1.47585,43.08476],[1.48344,43.08748],[1.48582,43.08794],[1.49246,43.08754],[1.49682,43.09217],[1.49779,43.09236],[1.49806,43.09443],[1.49886,43.09493]]]},"properties":{"code":"09001","nom":"Foix"}}]} -------------------------------------------------------------------------------- /dune: -------------------------------------------------------------------------------- 1 | (mdx 2 | (files README.md) 3 | (package geojsone) 4 | (libraries eio geojsone.eio) 5 | (preludes ./test/prelude.txt)) 6 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 3.2) 2 | 3 | (name geojson) 4 | 5 | (generate_opam_files true) 6 | 7 | (source 8 | (github geocaml/ocaml-geojson)) 9 | 10 | (license MIT) 11 | 12 | (authors "Patrick Ferris") 13 | 14 | (maintainers "patrick@sirref.org") 15 | 16 | (package 17 | (name geojson) 18 | (synopsis "Pure OCaml library for GeoJSON") 19 | (tags ("geojson" "geospatial" "geocaml")) 20 | (description "GeoJSON is a 'schema' for JSON to describe geospatial information. This library provides a JSON-parser-agnostic library for manipulating and parsing GeoJSON into OCaml.") 21 | (depends 22 | (ocaml (>= 4.08.0)) 23 | (bos :with-test) 24 | (mdx :with-test) 25 | (alcotest :with-test) 26 | (ezjsonm :with-test))) 27 | 28 | (package 29 | (name geojsone) 30 | (synopsis "Streaming GeoJSON library") 31 | (tags ("geojson" "geospatial" "geocaml")) 32 | (description "A library for manipulating GeoJSON using a streaming parser. This is useful because GeoJSON can be gigabytes in size.") 33 | (depends 34 | (geojson (= :version)) 35 | (mdx :with-test) 36 | (ezjsonm :with-test) ; Needed for benchmarks 37 | (eio_main (and (>= 0.6) :with-test)) 38 | hex 39 | sexplib0)) 40 | 41 | (using mdx 0.3) -------------------------------------------------------------------------------- /geojson.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | synopsis: "Pure OCaml library for GeoJSON" 4 | description: 5 | "GeoJSON is a 'schema' for JSON to describe geospatial information. This library provides a JSON-parser-agnostic library for manipulating and parsing GeoJSON into OCaml." 6 | maintainer: ["patrick@sirref.org"] 7 | authors: ["Patrick Ferris"] 8 | license: "MIT" 9 | tags: ["geojson" "geospatial" "geocaml"] 10 | homepage: "https://github.com/geocaml/ocaml-geojson" 11 | bug-reports: "https://github.com/geocaml/ocaml-geojson/issues" 12 | depends: [ 13 | "dune" {>= "3.2"} 14 | "ocaml" {>= "4.08.0"} 15 | "bos" {with-test} 16 | "mdx" {with-test} 17 | "alcotest" {with-test} 18 | "ezjsonm" {with-test} 19 | "odoc" {with-doc} 20 | ] 21 | build: [ 22 | ["dune" "subst"] {dev} 23 | [ 24 | "dune" 25 | "build" 26 | "-p" 27 | name 28 | "-j" 29 | jobs 30 | "@install" 31 | "@runtest" {with-test} 32 | "@doc" {with-doc} 33 | ] 34 | ] 35 | dev-repo: "git+https://github.com/geocaml/ocaml-geojson.git" 36 | -------------------------------------------------------------------------------- /geojsone.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | synopsis: "Streaming GeoJSON library" 4 | description: 5 | "A library for manipulating GeoJSON using a streaming parser. This is useful because GeoJSON can be gigabytes in size." 6 | maintainer: ["patrick@sirref.org"] 7 | authors: ["Patrick Ferris"] 8 | license: "MIT" 9 | tags: ["geojson" "geospatial" "geocaml"] 10 | homepage: "https://github.com/geocaml/ocaml-geojson" 11 | bug-reports: "https://github.com/geocaml/ocaml-geojson/issues" 12 | depends: [ 13 | "dune" {>= "3.2"} 14 | "geojson" {= version} 15 | "mdx" {with-test} 16 | "ezjsonm" {with-test} 17 | "eio_main" {>= "0.6" & with-test} 18 | "hex" 19 | "sexplib0" 20 | "odoc" {with-doc} 21 | ] 22 | build: [ 23 | ["dune" "subst"] {dev} 24 | [ 25 | "dune" 26 | "build" 27 | "-p" 28 | name 29 | "-j" 30 | jobs 31 | "@install" 32 | "@runtest" {with-test} 33 | "@doc" {with-doc} 34 | ] 35 | ] 36 | dev-repo: "git+https://github.com/geocaml/ocaml-geojson.git" 37 | -------------------------------------------------------------------------------- /src/geojson/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (public_name geojson) 3 | (name geojson)) 4 | 5 | (documentation 6 | (package geojson)) 7 | -------------------------------------------------------------------------------- /src/geojson/geojson.ml: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2021-2022 Patrick Ferris 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 12 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 13 | DEALINGS IN THE SOFTWARE. 14 | *) 15 | module Intf = Geojson_intf 16 | 17 | module type S = Geojson_intf.S 18 | module type Json = Geojson_intf.Json 19 | 20 | let decode_or_err f v = 21 | match f v with Ok x -> x | Error (`Msg m) -> failwith m 22 | 23 | module Make (J : Intf.Json) = struct 24 | type json = J.t 25 | 26 | let bbox_to_json_or_empty bbox = 27 | Option.( 28 | if is_some bbox then [ ("bbox", J.array J.float (get bbox)) ] else []) 29 | 30 | module Geometry = struct 31 | type json = J.t 32 | 33 | let keys_in_use = [ "type"; "coordinates"; "bbox" ] 34 | 35 | let foreign_members json = 36 | match J.to_obj json with 37 | | Ok assoc -> 38 | List.filter (fun (k, _v) -> not (List.mem k keys_in_use)) assoc 39 | | Error _ -> [] 40 | 41 | module Position = struct 42 | (* We use a float array internally for performance *) 43 | type t = float array 44 | 45 | let lng t = t.(0) 46 | let lat t = t.(1) 47 | let altitude t = try Some t.(2) with _ -> None 48 | 49 | let v ?altitude ~lng ~lat () = 50 | match altitude with 51 | | Some f -> [| lng; lat; f |] 52 | | None -> [| lng; lat |] 53 | 54 | let equal l1 l2 = 55 | let n1 = Array.length l1 and n2 = Array.length l2 in 56 | if n1 <> n2 then false 57 | else 58 | let rec loop i = 59 | if i = n1 then true 60 | else if Float.equal (Array.unsafe_get l1 i) (Array.unsafe_get l2 i) 61 | then loop (succ i) 62 | else false 63 | in 64 | loop 0 65 | 66 | let to_json arr = J.array J.float arr 67 | end 68 | 69 | (* Returns the float array of coordinates if all goes well for any Geometry type *) 70 | let parse_by_type json p_c typ = 71 | match (J.find json [ "type" ], J.find json [ "coordinates" ]) with 72 | | None, _ -> 73 | Error 74 | (`Msg 75 | ("JSON should" 76 | ^ "have a key-value for `type' whilst parsing " 77 | ^ typ)) 78 | | _, None -> Error (`Msg "JSON should have a key-value for `coordinates'") 79 | | Some typ, Some coords -> ( 80 | Result.bind (J.to_string typ) @@ fun typ -> 81 | match typ with 82 | | t when t = typ -> p_c coords 83 | | t -> Error (`Msg ("Expected type of `" ^ typ ^ "' but got " ^ t))) 84 | 85 | module Point = struct 86 | type t = Position.t 87 | 88 | let typ = "Point" 89 | let position = Fun.id 90 | let v position = position 91 | let parse_coords coords = J.to_array (decode_or_err J.to_float) coords 92 | let base_of_json json = parse_by_type json parse_coords typ 93 | 94 | let to_json ?bbox ?(foreign_members = []) position = 95 | J.obj 96 | ([ 97 | ("type", J.string typ); ("coordinates", Position.to_json position); 98 | ] 99 | @ bbox_to_json_or_empty bbox 100 | @ foreign_members) 101 | end 102 | 103 | module MultiPoint = struct 104 | type t = Point.t array 105 | 106 | let typ = "MultiPoint" 107 | let coordinates = Fun.id 108 | let v positions = positions 109 | 110 | let parse_coords coords = 111 | try J.to_array (decode_or_err Point.parse_coords) coords 112 | with Failure m -> Error (`Msg m) 113 | 114 | let base_of_json json = parse_by_type json parse_coords typ 115 | 116 | let to_json ?bbox ?(foreign_members = []) positions = 117 | J.obj 118 | ([ 119 | ("type", J.string typ); 120 | ("coordinates", J.array Position.to_json positions); 121 | ] 122 | @ bbox_to_json_or_empty bbox 123 | @ foreign_members) 124 | end 125 | 126 | module LineString = struct 127 | type t = Position.t array 128 | 129 | let typ = "LineString" 130 | let coordinates = Fun.id 131 | let v = Fun.id 132 | 133 | let parse_coords coords = 134 | Result.bind 135 | (try MultiPoint.parse_coords coords with Failure m -> Error (`Msg m)) 136 | @@ fun arr -> 137 | if Array.length arr < 2 then 138 | Error (`Msg "LineStrings should have two or more points") 139 | else Ok arr 140 | 141 | let base_of_json json = parse_by_type json parse_coords typ 142 | 143 | let to_json ?bbox ?(foreign_members = []) positions = 144 | J.obj 145 | ([ 146 | ("type", J.string typ); 147 | ("coordinates", J.array Position.to_json positions); 148 | ] 149 | @ bbox_to_json_or_empty bbox 150 | @ foreign_members) 151 | end 152 | 153 | module MultiLineString = struct 154 | type t = LineString.t array 155 | 156 | let typ = "MultiLineString" 157 | let lines = Fun.id 158 | let v = Fun.id 159 | let to_positions = Fun.id 160 | let of_positions = Fun.id 161 | 162 | let parse_coords coords = 163 | try J.to_array (decode_or_err LineString.parse_coords) coords 164 | with Failure m -> Error (`Msg m) 165 | 166 | let base_of_json json = parse_by_type json parse_coords typ 167 | 168 | let to_json ?bbox ?(foreign_members = []) positions = 169 | J.obj 170 | ([ 171 | ("type", J.string typ); 172 | ("coordinates", J.array (J.array (J.array J.float)) positions); 173 | ] 174 | @ bbox_to_json_or_empty bbox 175 | @ foreign_members) 176 | end 177 | 178 | module Polygon = struct 179 | type t = LineString.t array 180 | 181 | let typ = "Polygon" 182 | let rings = Fun.id 183 | let exterior_ring t = t.(0) 184 | 185 | (* If used a lot, should changed to cstruct style off and len 186 | to avoid the allocations here. *) 187 | let interior_rings t = Array.sub t 1 (Array.length t - 1) 188 | let v = Fun.id 189 | let to_positions = Fun.id 190 | let of_positions = Fun.id 191 | 192 | let parse_coords coords = 193 | try 194 | J.to_array 195 | (decode_or_err 196 | (J.to_array 197 | (decode_or_err (J.to_array (decode_or_err J.to_float))))) 198 | coords 199 | with Failure m -> Error (`Msg m) 200 | 201 | let base_of_json json = parse_by_type json parse_coords typ 202 | 203 | let to_json ?bbox ?(foreign_members = []) positions = 204 | J.obj 205 | ([ 206 | ("type", J.string typ); 207 | ("coordinates", J.array (J.array (J.array J.float)) positions); 208 | ] 209 | @ bbox_to_json_or_empty bbox 210 | @ foreign_members) 211 | end 212 | 213 | module MultiPolygon = struct 214 | type t = Polygon.t array 215 | 216 | let typ = "MultiPolygon" 217 | let polygons = Fun.id 218 | let v = Fun.id 219 | let to_positions = Fun.id 220 | let of_positions = Fun.id 221 | 222 | let parse_coords coords = 223 | try J.to_array (decode_or_err Polygon.parse_coords) coords 224 | with Failure m -> Error (`Msg m) 225 | 226 | let base_of_json json = parse_by_type json parse_coords typ 227 | 228 | let to_json ?bbox ?(foreign_members = []) positions = 229 | J.obj 230 | ([ 231 | ("type", J.string typ); 232 | ( "coordinates", 233 | J.array (J.array (J.array (J.array J.float))) positions ); 234 | ] 235 | @ bbox_to_json_or_empty bbox 236 | @ foreign_members) 237 | end 238 | 239 | type geometry = 240 | | Point of Point.t 241 | | MultiPoint of MultiPoint.t 242 | | LineString of LineString.t 243 | | MultiLineString of MultiLineString.t 244 | | Polygon of Polygon.t 245 | | MultiPolygon of MultiPolygon.t 246 | | Collection of t list 247 | 248 | and t = geometry * (string * json) list 249 | 250 | let rec base_of_json json = 251 | let fm = foreign_members json in 252 | match J.find json [ "type" ] with 253 | | Some typ -> ( 254 | match J.to_string typ with 255 | | Ok "Point" -> 256 | Result.map (fun v -> (Point v, fm)) @@ Point.base_of_json json 257 | | Ok "MultiPoint" -> 258 | Result.map (fun v -> (MultiPoint v, fm)) 259 | @@ MultiPoint.base_of_json json 260 | | Ok "LineString" -> 261 | Result.map (fun v -> (LineString v, fm)) 262 | @@ LineString.base_of_json json 263 | | Ok "MultiLineString" -> 264 | Result.map (fun v -> (MultiLineString v, fm)) 265 | @@ MultiLineString.base_of_json json 266 | | Ok "Polygon" -> 267 | Result.map (fun v -> (Polygon v, fm)) @@ Polygon.base_of_json json 268 | | Ok "MultiPolygon" -> 269 | Result.map (fun v -> (MultiPolygon v, fm)) 270 | @@ MultiPolygon.base_of_json json 271 | | Ok "GeometryCollection" -> ( 272 | match J.find json [ "geometries" ] with 273 | | Some list -> 274 | let geo = J.to_list (decode_or_err base_of_json) list in 275 | Result.map (fun v -> (Collection v, fm)) geo 276 | | None -> 277 | Error 278 | (`Msg 279 | "A geometry collection should have a member called \ 280 | geometries")) 281 | | Ok typ -> Error (`Msg ("Unknown type of geometry " ^ typ)) 282 | | Error _ as e -> e) 283 | | None -> 284 | Error 285 | (`Msg 286 | "A Geojson text should contain one object with a member `type`.") 287 | 288 | let rec to_json ?bbox = function 289 | | Point point, foreign_members -> 290 | Point.to_json ?bbox ~foreign_members point 291 | | MultiPoint mp, foreign_members -> 292 | MultiPoint.to_json ?bbox ~foreign_members mp 293 | | LineString ls, foreign_members -> 294 | LineString.to_json ?bbox ~foreign_members ls 295 | | MultiLineString mls, foreign_members -> 296 | MultiLineString.to_json ?bbox ~foreign_members mls 297 | | Polygon p, foreign_members -> Polygon.to_json ?bbox ~foreign_members p 298 | | MultiPolygon mp, foreign_members -> 299 | MultiPolygon.to_json ?bbox ~foreign_members mp 300 | | Collection c, foreign_members -> 301 | J.obj 302 | ([ 303 | ("type", J.string "GeometryCollection"); 304 | ("geometries", J.list to_json c); 305 | ] 306 | @ bbox_to_json_or_empty bbox 307 | @ foreign_members) 308 | 309 | let foreign_members (_, fm) = fm 310 | let geometry (g, _) = g 311 | let v ?(foreign_members = []) g = (g, foreign_members) 312 | end 313 | 314 | module Feature = struct 315 | type t = { 316 | geometry : Geometry.t option; 317 | properties : json option; 318 | foreign_members : (string * json) list; 319 | id : [ `String of string | `Float of float ] option; 320 | } 321 | 322 | let v ?id ?properties ?(foreign_members = []) geo = 323 | { geometry = Some geo; properties; foreign_members; id } 324 | 325 | let geometry t = t.geometry 326 | let properties t = t.properties 327 | let keys_in_use = [ "type"; "geometry"; "properties"; "id"; "bbox" ] 328 | 329 | let foreign_members json = 330 | match J.to_obj json with 331 | | Ok assoc -> 332 | List.filter (fun (k, _v) -> not (List.mem k keys_in_use)) assoc 333 | | Error _ -> [] 334 | 335 | let id_of_json = function 336 | | Some json -> ( 337 | match J.to_string json with 338 | | Ok s -> Ok (Some (`String s)) 339 | | _ -> ( 340 | match J.to_float json with 341 | | Ok f -> Ok (Some (`Float f)) 342 | | _ -> Error (`Msg "Identifier is not a string or number"))) 343 | | None -> Ok None 344 | 345 | let id_to_json = function `String s -> J.string s | `Float f -> J.float f 346 | 347 | let base_of_json json = 348 | match J.find json [ "type" ] with 349 | | Some typ -> ( 350 | match J.to_string typ with 351 | | Ok "Feature" -> ( 352 | let fm = foreign_members json in 353 | match 354 | ( J.find json [ "geometry" ], 355 | J.find json [ "properties" ], 356 | J.find json [ "id" ] ) 357 | with 358 | | Some geometry, properties, id -> 359 | Result.bind (id_of_json id) (fun id -> 360 | Result.map 361 | (fun v -> 362 | { 363 | geometry = Option.some v; 364 | properties; 365 | foreign_members = fm; 366 | id; 367 | }) 368 | (Geometry.base_of_json geometry)) 369 | | None, properties, id -> 370 | Result.map 371 | (fun id -> 372 | { geometry = None; properties; foreign_members = fm; id }) 373 | (id_of_json id)) 374 | | Ok s -> 375 | Error 376 | (`Msg 377 | ("A Geojson feature requires the type `Feature`. Found type, \ 378 | but it was " 379 | ^ s)) 380 | | Error _ as e -> e) 381 | | None -> 382 | Error 383 | (`Msg 384 | "A Geojson feature requires the type `Feature`. No type was \ 385 | found.") 386 | 387 | let to_json ?bbox { geometry; properties; foreign_members; id } = 388 | J.obj 389 | ([ ("type", J.string "Feature") ] 390 | @ (match geometry with 391 | | Some p -> [ ("geometry", Geometry.to_json p) ] 392 | | None -> []) 393 | @ (match properties with Some p -> [ ("properties", p) ] | None -> []) 394 | @ (match id with Some s -> [ ("id", id_to_json s) ] | None -> []) 395 | @ bbox_to_json_or_empty bbox 396 | @ foreign_members) 397 | 398 | let foreign_members t = t.foreign_members 399 | let id t = t.id 400 | 401 | module Collection = struct 402 | type feature = t 403 | 404 | type nonrec t = { 405 | features : feature list; 406 | foreign_members : (string * json) list; 407 | } 408 | 409 | let features t = t.features 410 | let v ?(foreign_members = []) features = { features; foreign_members } 411 | 412 | let keys_in_use = 413 | [ "type"; "features"; "geometry"; "properties"; "id"; "bbox" ] 414 | 415 | let foreign_members json = 416 | match J.to_obj json with 417 | | Ok assoc -> 418 | List.filter (fun (k, _v) -> not (List.mem k keys_in_use)) assoc 419 | | Error _ -> [] 420 | 421 | let base_of_json json = 422 | match J.find json [ "type" ] with 423 | | Some typ -> ( 424 | match J.to_string typ with 425 | | Ok "FeatureCollection" -> ( 426 | let fm = foreign_members json in 427 | match J.find json [ "features" ] with 428 | | Some features -> 429 | let features = 430 | J.to_list 431 | (fun geometry -> decode_or_err base_of_json geometry) 432 | features 433 | in 434 | Result.map 435 | (fun v -> { features = v; foreign_members = fm }) 436 | features 437 | | None -> 438 | Error 439 | (`Msg 440 | "A feature collection should have a member called \ 441 | `features`.")) 442 | | Ok s -> 443 | Error 444 | (`Msg 445 | ("A Geojson feature collection requires the type \ 446 | `FeatureCollection`. Found type, but it was " 447 | ^ s)) 448 | | Error _ as e -> e) 449 | | None -> 450 | Error 451 | (`Msg 452 | "A Geojson feature collection requires the type \ 453 | `FeatureCollection`. No type was found.") 454 | 455 | let to_json ?bbox { features; foreign_members } = 456 | J.obj 457 | ([ 458 | ("type", J.string "FeatureCollection"); 459 | ("features", J.list to_json features); 460 | ] 461 | @ bbox_to_json_or_empty bbox 462 | @ foreign_members) 463 | 464 | let foreign_members t = t.foreign_members 465 | end 466 | end 467 | 468 | type geojson = 469 | | Feature of Feature.t 470 | | FeatureCollection of Feature.Collection.t 471 | | Geometry of Geometry.t 472 | 473 | and t = { geojson : geojson; bbox : float array option } 474 | 475 | let geojson t = t.geojson 476 | let bbox t = t.bbox 477 | let v ?bbox geojson = { geojson; bbox } 478 | let geojson_to_t gjson bbox = { geojson = gjson; bbox } 479 | 480 | let json_to_bbox json = 481 | match J.to_array (decode_or_err J.to_float) json with 482 | | Ok v -> Some v 483 | | Error _ -> None 484 | 485 | let of_json json = 486 | match (J.find json [ "type" ], J.find json [ "bbox" ]) with 487 | | Some typ, bbx -> ( 488 | match J.to_string typ with 489 | | Ok "Feature" -> ( 490 | match Feature.base_of_json json with 491 | | Ok v -> 492 | Ok (geojson_to_t (Feature v) @@ Option.bind bbx json_to_bbox) 493 | | Error e -> Error e) 494 | | Ok "FeatureCollection" -> ( 495 | match Feature.Collection.base_of_json json with 496 | | Ok v -> 497 | Ok 498 | (geojson_to_t (FeatureCollection v) 499 | @@ Option.bind bbx json_to_bbox) 500 | | Error e -> Error e) 501 | | Ok _maybe_geometry -> ( 502 | match Geometry.base_of_json json with 503 | | Ok v -> 504 | Ok (geojson_to_t (Geometry v) @@ Option.bind bbx json_to_bbox) 505 | | Error e -> Error e) 506 | | Error _ as e -> e) 507 | | None, _ -> 508 | Error 509 | (`Msg 510 | "A Geojson text should contain one object with a member `type`.") 511 | 512 | let to_json = function 513 | | { geojson = Feature f; bbox } -> Feature.to_json ?bbox f 514 | | { geojson = FeatureCollection fc; bbox } -> 515 | Feature.Collection.to_json ?bbox fc 516 | | { geojson = Geometry g; bbox } -> Geometry.to_json ?bbox g 517 | 518 | module Accessor = struct 519 | module Optics = Optics 520 | include Optics.Infix 521 | 522 | let get = Optics.Lens.get 523 | 524 | let geojson = 525 | Optics.Lens.V 526 | ((fun t -> (t.geojson, t)), fun (geojson, t) -> { t with geojson }) 527 | 528 | let bbox = 529 | Optics.Lens.V ((fun t -> (t.bbox, t)), fun (bbox, t) -> { t with bbox }) 530 | 531 | let feature = 532 | let into = function 533 | | Feature f -> Ok f 534 | | v -> 535 | ignore (failwith "Big yikes"); 536 | Error v 537 | in 538 | let out_of = function Ok f -> Feature f | Error v -> v in 539 | Optics.Prism.V (into, out_of) 540 | 541 | let feature_collection = 542 | let into = function FeatureCollection f -> Ok f | v -> Error v in 543 | let out_of = function Ok f -> FeatureCollection f | Error v -> v in 544 | Optics.Prism.V (into, out_of) 545 | 546 | let geometry = 547 | let into = function Geometry f -> Ok f | v -> Error v in 548 | let out_of = function Ok f -> Geometry f | Error v -> v in 549 | Optics.Prism.V (into, out_of) 550 | 551 | module Feature = struct 552 | let properties = 553 | Optics.Lens.V 554 | ( (fun t -> (t.Feature.properties, t)), 555 | fun (properties, t) -> { t with properties } ) 556 | 557 | let foreign_members = 558 | Optics.Lens.V 559 | ( (fun t -> (t.Feature.foreign_members, t)), 560 | fun (foreign_members, t) -> { t with foreign_members } ) 561 | 562 | let geometry = 563 | Optics.Lens.V 564 | ( (fun t -> (t.Feature.geometry, t)), 565 | fun (geometry, t) -> { t with geometry } ) 566 | 567 | let geometry_exn = 568 | Optics.Lens.V 569 | ( (fun t -> (Option.get t.Feature.geometry, t)), 570 | fun (geometry, t) -> { t with geometry = Some geometry } ) 571 | end 572 | 573 | module Geometry = struct 574 | let geometry : (Geometry.t, Geometry.geometry) Optics.Lens.t = 575 | Optics.Lens.fst 576 | 577 | let foreign_members : (Geometry.t, (string * json) list) Optics.Lens.t = 578 | Optics.Lens.snd 579 | 580 | let point : (Geometry.geometry, Geometry.Point.t) Optics.Prism.t = 581 | let into = function Geometry.Point f -> Ok f | v -> Error v in 582 | let out_of = function Ok f -> Geometry.Point f | Error v -> v in 583 | Optics.Prism.V (into, out_of) 584 | 585 | let multipoint : (Geometry.geometry, Geometry.MultiPoint.t) Optics.Prism.t 586 | = 587 | let into = function Geometry.MultiPoint f -> Ok f | v -> Error v in 588 | let out_of = function Ok f -> Geometry.MultiPoint f | Error v -> v in 589 | Optics.Prism.V (into, out_of) 590 | 591 | let linestring : (Geometry.geometry, Geometry.LineString.t) Optics.Prism.t 592 | = 593 | let into = function Geometry.LineString f -> Ok f | v -> Error v in 594 | let out_of = function Ok f -> Geometry.LineString f | Error v -> v in 595 | Optics.Prism.V (into, out_of) 596 | 597 | let multilinestring : 598 | (Geometry.geometry, Geometry.MultiLineString.t) Optics.Prism.t = 599 | let into = function 600 | | Geometry.MultiLineString f -> Ok f 601 | | v -> Error v 602 | in 603 | let out_of = function 604 | | Ok f -> Geometry.MultiLineString f 605 | | Error v -> v 606 | in 607 | Optics.Prism.V (into, out_of) 608 | 609 | let polygon : (Geometry.geometry, Geometry.Polygon.t) Optics.Prism.t = 610 | let into = function Geometry.Polygon f -> Ok f | v -> Error v in 611 | let out_of = function Ok f -> Geometry.Polygon f | Error v -> v in 612 | Optics.Prism.V (into, out_of) 613 | 614 | let multipolygon : 615 | (Geometry.geometry, Geometry.MultiPolygon.t) Optics.Prism.t = 616 | let into = function Geometry.MultiPolygon f -> Ok f | v -> Error v in 617 | let out_of = function 618 | | Ok f -> Geometry.MultiPolygon f 619 | | Error v -> v 620 | in 621 | Optics.Prism.V (into, out_of) 622 | end 623 | end 624 | 625 | module Random = struct 626 | type geometry = 627 | | Point 628 | | MultiPoint of int 629 | | LineString of int 630 | | MultiLineString of int * int 631 | | Polygon of int 632 | | MultiPolygon of int * int 633 | | Collection of geometry list 634 | 635 | type feature = { properties : json option; geometry : geometry } 636 | type r = FC of feature list | F of feature | G of geometry 637 | 638 | let random ~f t = 639 | let rec aux_random = function 640 | | FC fs -> 641 | let features = List.map random_f fs in 642 | { 643 | geojson = FeatureCollection { features; foreign_members = [] }; 644 | bbox = None; 645 | } 646 | | F f -> { geojson = Feature (random_f f); bbox = None } 647 | | G g -> { geojson = Geometry (random_g g); bbox = None } 648 | and random_f { properties; geometry } = 649 | let geo = random_g geometry in 650 | { geometry = Some geo; properties; foreign_members = []; id = None } 651 | and random_g = function 652 | | Point -> (Geometry.Point (random_point ()), []) 653 | | MultiPoint i -> 654 | (Geometry.MultiPoint (Array.init i (fun _ -> random_point ())), []) 655 | | LineString i -> 656 | (Geometry.LineString (Array.init i (fun _ -> random_point ())), []) 657 | | MultiLineString (i, j) -> 658 | ( Geometry.MultiLineString 659 | ( Array.init i @@ fun _ -> 660 | Array.init j (fun _ -> random_point ()) ), 661 | [] ) 662 | | Polygon i -> (Geometry.Polygon (random_polygon i), []) 663 | | MultiPolygon (i, j) -> 664 | let arr = Array.init i (fun _ -> random_polygon j) in 665 | (Geometry.MultiPolygon arr, []) 666 | | Collection lst -> 667 | let lst = List.map random_g lst in 668 | (Geometry.Collection lst, []) 669 | and random_point () = 670 | Geometry.(Point.v (Position.v ~lat:(f ()) ~lng:(f ()) ())) 671 | and random_polygon i = 672 | (* This geometry is not going to be very country like... *) 673 | let points = Array.init i (fun _ -> random_point ()) in 674 | points.(i - 1) <- points.(0); 675 | [| points |] 676 | in 677 | aux_random t 678 | end 679 | end 680 | -------------------------------------------------------------------------------- /src/geojson/geojson.mli: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2021-2022 Patrick Ferris 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 12 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 13 | DEALINGS IN THE SOFTWARE. 14 | *) 15 | 16 | include Geojson_intf.Geojson 17 | (** @inline *) 18 | -------------------------------------------------------------------------------- /src/geojson/geojson_intf.ml: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2021-2022 Patrick Ferris 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 12 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 13 | DEALINGS IN THE SOFTWARE. 14 | *) 15 | 16 | (** The GeoJSON library does not force you to use a particular JSON parsing 17 | library. You must provide one. See the tests and benchmarks for an [Ezjsone] 18 | parser and one for JS using [Brr]'s [Jv] library. *) 19 | module type Json = sig 20 | type t 21 | (** The type your parser uses to represent a parsed JSON object. *) 22 | 23 | val find : t -> string list -> t option 24 | (** Recursively find keys in nested objects. *) 25 | 26 | val to_string : t -> (string, [ `Msg of string ]) result 27 | (** Convert the JSON to a string. *) 28 | 29 | val string : string -> t 30 | (** Create a JSON string. *) 31 | 32 | val to_float : t -> (float, [ `Msg of string ]) result 33 | (** Convert the JSON to a float. *) 34 | 35 | val float : float -> t 36 | (** Converts a float to JSON *) 37 | 38 | val to_int : t -> (int, [ `Msg of string ]) result 39 | (** Convert the JSON to an integer. *) 40 | 41 | val int : int -> t 42 | (** Converts an integer to JSON *) 43 | 44 | val to_list : (t -> 'a) -> t -> ('a list, [ `Msg of string ]) result 45 | (** [to_list f] converts the JSON array to a list and applies [f] to each 46 | element to convert them too. *) 47 | 48 | val list : ('a -> t) -> 'a list -> t 49 | (** Make a JSON array from a list *) 50 | 51 | val to_array : (t -> 'a) -> t -> ('a array, [ `Msg of string ]) result 52 | (** Like {!to_list} except to an array. *) 53 | 54 | val array : ('a -> t) -> 'a array -> t 55 | (** Like {!list} except for OCaml arrays *) 56 | 57 | val to_obj : t -> ((string * t) list, [ `Msg of string ]) result 58 | (** Convert the JSON object to an association list *) 59 | 60 | val obj : (string * t) list -> t 61 | (** A JSON object from an association list *) 62 | 63 | val null : t 64 | (** Null value *) 65 | 66 | val is_null : t -> bool 67 | (** Test for null *) 68 | end 69 | 70 | (** {2 GeoJSON Geometry Objects} 71 | 72 | The basic primitives for building geometrical shapes in GeoJSON. *) 73 | 74 | module type Geometry = sig 75 | type json 76 | (** A type to represt JSON values. *) 77 | 78 | module Position : sig 79 | type t = float array 80 | (** A position - a longitude and latitude with an optional altitude *) 81 | 82 | val lng : t -> float 83 | (** The longitude value of the position *) 84 | 85 | val lat : t -> float 86 | (** The latitude value of the position *) 87 | 88 | val altitude : t -> float option 89 | (** Optional altitude/elevation value of the position *) 90 | 91 | val equal : t -> t -> bool 92 | (** Whether two positions are equal by comparing each value *) 93 | 94 | val v : ?altitude:float -> lng:float -> lat:float -> unit -> t 95 | (** A position constructor *) 96 | end 97 | 98 | module Point : sig 99 | type t 100 | (** A point is a single {!Position.t} *) 101 | 102 | val position : t -> Position.t 103 | (** Convert a point to a position *) 104 | 105 | val v : Position.t -> t 106 | (** Create a poitn from a position. *) 107 | end 108 | 109 | module MultiPoint : sig 110 | type t 111 | (** A multipoint is an array of positions. *) 112 | 113 | val coordinates : t -> Position.t array 114 | (** Get the positions that make up this multipoint object. *) 115 | 116 | val v : Position.t array -> t 117 | (** Create a multipoint object from an array of positions. *) 118 | end 119 | 120 | module LineString : sig 121 | type t 122 | (** A line string is two or more points *) 123 | 124 | val coordinates : t -> Position.t array 125 | (** Convert the line into a position array *) 126 | 127 | val v : Position.t array -> t 128 | (** Create a line string from positions, will raise [Invalid_argument] if 129 | the array doesn't have at least two positions. *) 130 | end 131 | 132 | module MultiLineString : sig 133 | type t 134 | (** A collection of line strings *) 135 | 136 | val lines : t -> LineString.t array 137 | (** Access the lines *) 138 | 139 | val v : LineString.t array -> t 140 | (** Create a multiline string *) 141 | 142 | val to_positions : t -> Position.t array array 143 | (** Convert directly to the positions that make up the lines. *) 144 | 145 | val of_positions : Position.t array array -> t 146 | (** Convert directly from positions to lines *) 147 | end 148 | 149 | module Polygon : sig 150 | type t 151 | (** A close loop with optional rings *) 152 | 153 | val rings : t -> LineString.t array 154 | (** [rings t] returns the linear rings contained in [t] (a Polygon object) *) 155 | 156 | val exterior_ring : t -> LineString.t 157 | (** [exterior_ring t] returns the first linear ring contained in [t] (a 158 | Polygon object). This ring bounds the surface *) 159 | 160 | val interior_rings : t -> LineString.t array 161 | (** If [t] (a Polygon object) contains more than 1 linear ring, 162 | [interior_rings t] returns the rest of the linear rings apart from the 163 | first. These rings (if present), bound the holes. *) 164 | 165 | val v : LineString.t array -> t 166 | (** Create a polygon object from an array of close line strings (note no 167 | checking is down here to ensure the loops are indeed closed.) *) 168 | 169 | val to_positions : t -> Position.t array array 170 | (** Convert directly to the positions that make up the lines. *) 171 | 172 | val of_positions : Position.t array array -> t 173 | (** Convert directly from positions to lines *) 174 | end 175 | 176 | module MultiPolygon : sig 177 | type t 178 | (** A multi-polygon object *) 179 | 180 | val polygons : t -> Polygon.t array 181 | (** Access the polygons *) 182 | 183 | val v : Polygon.t array -> t 184 | (** Create a multi-polygon object from an array of {!Polygon.t}s *) 185 | 186 | val to_positions : t -> Position.t array array array 187 | (** Convert directly to the positions that make up the polygons *) 188 | 189 | val of_positions : Position.t array array array -> t 190 | (** Convert directly from positions to polygons *) 191 | end 192 | 193 | type geometry = 194 | | Point of Point.t 195 | | MultiPoint of MultiPoint.t 196 | | LineString of LineString.t 197 | | MultiLineString of MultiLineString.t 198 | | Polygon of Polygon.t 199 | | MultiPolygon of MultiPolygon.t 200 | | Collection of t list 201 | 202 | and t 203 | 204 | val foreign_members : t -> (string * json) list 205 | (** [foreign_members t] will extract name/value pair of a foreign member from 206 | t (a GeoJSON object) *) 207 | 208 | val geometry : t -> geometry 209 | (** [geometry t] will extract the underlying geometry. *) 210 | 211 | val v : ?foreign_members:(string * json) list -> geometry -> t 212 | end 213 | 214 | module type S = sig 215 | type json 216 | (** The internal representation of a JSON value. *) 217 | 218 | module Geometry : Geometry with type json = json 219 | (** Geometries *) 220 | 221 | (** Features which contain a geometry *) 222 | module Feature : sig 223 | type t 224 | (** A feature object is a geojson object with optional geometry and 225 | properties members. *) 226 | 227 | val geometry : t -> Geometry.t option 228 | val properties : t -> json option 229 | 230 | val foreign_members : t -> (string * json) list 231 | (** [foreign_members t] will extract name/value pair of a foreign member 232 | from t (a GeoJSON object) *) 233 | 234 | val id : t -> [ `String of string | `Float of float ] option 235 | (** [id f] extracts the identifier for the feature if it exists. *) 236 | 237 | val v : 238 | ?id:[ `String of string | `Float of float ] -> 239 | ?properties:json -> 240 | ?foreign_members:(string * json) list -> 241 | Geometry.t -> 242 | t 243 | (** [v geo] creates a new feature object, you may wish to provide a 244 | [properties] JSON object for the feature too. *) 245 | 246 | module Collection : sig 247 | type feature = t 248 | type t 249 | 250 | val features : t -> feature list 251 | 252 | val v : ?foreign_members:(string * json) list -> feature list -> t 253 | (** [v features] creates a feature collection from a list of features *) 254 | 255 | val foreign_members : t -> (string * json) list 256 | (** [foreign_members t] will extract name/value pair of a foreign member 257 | from t (a GeoJSON object) *) 258 | end 259 | end 260 | 261 | type geojson = 262 | | Feature of Feature.t 263 | | FeatureCollection of Feature.Collection.t 264 | | Geometry of Geometry.t 265 | 266 | (** A {!geojson} object which could be a geometry, a feature or a collection 267 | of features. *) 268 | 269 | type t 270 | (** The type for GeoJSON objects. *) 271 | 272 | val geojson : t -> geojson 273 | (** [geojson t] will extract geojson value from t (a GeoJSON object) *) 274 | 275 | val bbox : t -> float array option 276 | (** [bbox t] will extract bbox value from t (a GeoJSON object) *) 277 | 278 | val v : ?bbox:float array -> geojson -> t 279 | (** [v geojson bbox] combines geojson and bbox to return a GeoJSON object (a 280 | type {!t}) *) 281 | 282 | val of_json : json -> (t, [ `Msg of string ]) result 283 | (** [of_json json] converts the JSON to a GeoJSON object (a type {!t}) or an 284 | error. *) 285 | 286 | val to_json : t -> json 287 | (** [to_json g] converts the GeoJSON object [g] to JSON *) 288 | 289 | module Accessor : sig 290 | module Optics = Optics 291 | 292 | (** The accessor module uses optics to allow users to build reusable values 293 | that can be used to get values deeply nested in GeoJSON values. Bare in 294 | mind if you care more about performance and/or memory footprint, you are 295 | probably better off writing pattern-matching statements by hand than 296 | using accessors.*) 297 | 298 | val get : ('a, 'b) Optics.Lens.t -> 'a -> 'b 299 | (** [get lens v] focuses onto the field in [lens] for the value [v]. *) 300 | 301 | val geojson : (t, geojson) Optics.Lens.t 302 | (** A lens for focusing on the [geojson] value. *) 303 | 304 | val bbox : (t, float array option) Optics.Lens.t 305 | (** A lens for focusing on the bounding box if any. *) 306 | 307 | val feature : (geojson, Feature.t) Optics.Prism.t 308 | (** A prism for matching on a feature. *) 309 | 310 | val geometry : (geojson, Geometry.t) Optics.Prism.t 311 | (** A prism for matching on a geometry. *) 312 | 313 | val feature_collection : (geojson, Feature.Collection.t) Optics.Prism.t 314 | (** A prism for matching on a feature collection. *) 315 | 316 | module Feature : sig 317 | val properties : (Feature.t, json option) Optics.Lens.t 318 | (** A lens for focusing on the properties if any. *) 319 | 320 | val foreign_members : (Feature.t, (string * json) list) Optics.Lens.t 321 | (** A lens for focusing on the foreign members if any. *) 322 | 323 | val geometry : (Feature.t, Geometry.t option) Optics.Lens.t 324 | (** A lens for focusing on the feature's geometry if any. *) 325 | 326 | val geometry_exn : (Feature.t, Geometry.t) Optics.Lens.t 327 | (** Like {!geometry} except using [Option.get] internally. *) 328 | end 329 | 330 | module Geometry : sig 331 | val geometry : (Geometry.t, Geometry.geometry) Optics.Lens.t 332 | (** A lens for focusing on the geometry value. *) 333 | 334 | val foreign_members : (Geometry.t, (string * json) list) Optics.Lens.t 335 | (** A lens for focusing on the possibly empty foreign members. *) 336 | 337 | (** {3 Prisms for Geometries} *) 338 | 339 | val point : (Geometry.geometry, Geometry.Point.t) Optics.Prism.t 340 | val multipoint : (Geometry.geometry, Geometry.MultiPoint.t) Optics.Prism.t 341 | val linestring : (Geometry.geometry, Geometry.LineString.t) Optics.Prism.t 342 | 343 | val multilinestring : 344 | (Geometry.geometry, Geometry.MultiLineString.t) Optics.Prism.t 345 | 346 | val polygon : (Geometry.geometry, Geometry.Polygon.t) Optics.Prism.t 347 | 348 | val multipolygon : 349 | (Geometry.geometry, Geometry.MultiPolygon.t) Optics.Prism.t 350 | end 351 | 352 | (** {3 Infix Operators} 353 | 354 | These operators allow you to combine lenses and prisms into more 355 | complicated lenses and prisms.*) 356 | 357 | open Optics 358 | 359 | val ( >> ) : 360 | ('a, 'b) Optional.t -> ('b, 'c) Optional.t -> ('a, 'c) Optional.t 361 | 362 | val ( &> ) : ('a, 'b) Optional.t -> ('b, 'c) Lens.t -> ('a, 'c) Optional.t 363 | val ( $> ) : ('a, 'b) Optional.t -> ('b, 'c) Prism.t -> ('a, 'c) Optional.t 364 | val ( >& ) : ('a, 'b) Lens.t -> ('b, 'c) Prism.t -> ('a, 'c) Optional.t 365 | val ( >$ ) : ('a, 'b) Prism.t -> ('b, 'c) Lens.t -> ('a, 'c) Optional.t 366 | val ( & ) : ('a, 'b) Lens.t -> ('b, 'c) Lens.t -> ('a, 'c) Lens.t 367 | val ( $ ) : ('a, 'b) Prism.t -> ('b, 'c) Prism.t -> ('a, 'c) Prism.t 368 | end 369 | 370 | module Random : sig 371 | type geometry = 372 | | Point 373 | | MultiPoint of int 374 | | LineString of int 375 | | MultiLineString of int * int 376 | | Polygon of int 377 | | MultiPolygon of int * int 378 | | Collection of geometry list 379 | 380 | type feature = { properties : json option; geometry : geometry } 381 | type r = FC of feature list | F of feature | G of geometry 382 | 383 | (** {3 Generate random geojson} 384 | 385 | The random module provides a way of quickly constructing random, correct 386 | GeoJSON objects. You provide the skeleton of the document using type 387 | {!t} and tweaking some of the parameters. For example: 388 | 389 | [{ 390 | let random_structure = 391 | FC (List.init 100 (fun _ -> { properties = None; geometry = Point })) 392 | }]*) 393 | 394 | val random : f:(unit -> float) -> r -> t 395 | (** [random ~f r] produces random GeoJSON based on the structure provided by 396 | [r] and using the random float generator [f]. Note the random geometry 397 | maker will follow the rules of GeoJSON (for making Polygons for 398 | example). *) 399 | end 400 | end 401 | 402 | module type Geojson = sig 403 | module type S = S 404 | (** Types for GeoJSON texts and objects *) 405 | 406 | module type Json = Json 407 | (** Types for the JSON parser *) 408 | 409 | (** A functor that takes a JSON parsing implementation and returns a GeoJSON 410 | parser and constructor. *) 411 | module Make (J : Json) : S with type json = J.t 412 | end 413 | -------------------------------------------------------------------------------- /src/geojson/index.mld: -------------------------------------------------------------------------------- 1 | {1 GeoJson} 2 | 3 | The GeoJson library provides a functor {! Make} for building a GeoJson parsing 4 | and manipulating module. You just need to provide a Json parser using tools like 5 | [Ezjsone] or [Yojson]. 6 | 7 | {i Before diving in, it is important to note that if you are dealing with gigabytes 8 | of data in GeoJson format you would be better served by the [Geojsonm] library which 9 | provides functions using the [Jsonm] streaming parser to avoid loading everything 10 | in memory}. 11 | 12 | {2 Providing a Json Parser} 13 | 14 | Before getting a Geojson library you first must provide a parser implementation. {! Geojson} doesn't 15 | depend on a particular Json parsing library (there are quite a few in the OCaml universe). This modularity 16 | comes with a small cost, you must provide a simple parsing module to the {! Geojson.Make} functor. 17 | 18 | {3 Ezjsone Parser} 19 | 20 | The following is an example of such a parser. If you can you would be better checking the tests and benchmarks 21 | {{: https://github.com/patricoferris/ocaml-geojson} of this library} for an up to date, type-checked and building version. 22 | 23 | {[ 24 | module Ezjsone_parser = struct 25 | type t = Ezjsone.value 26 | 27 | let catch_err f v = 28 | try Ok (f v) with Ezjsone.Parse_error (_, s) -> Error (`Msg s) 29 | 30 | let find = Ezjsone.find_opt 31 | let to_string t = catch_err Ezjsone.get_string t 32 | let string = Ezjsone.string 33 | let to_float t = catch_err Ezjsone.get_float t 34 | let float = Ezjsone.float 35 | let to_list f t = catch_err (Ezjsone.get_list f) t 36 | let list f t = Ezjsone.list f t 37 | let to_array f t = Result.map Array.of_list @@ to_list f t 38 | let array f t = list f (Array.to_list t) 39 | let obj = Ezjsone.dict 40 | let null = `Null 41 | let is_null = function `Null -> true | _ -> false 42 | end 43 | ]} 44 | 45 | Feel free to copy and paste any of the parsers without attribution :) 46 | 47 | {2 Using the Library} 48 | 49 | {3 Reading Geojson} 50 | 51 | Once you have provided the Json parser, you can then start using the library properly. 52 | The simplest way to get started is first reading Json into your choosen parsers internal 53 | representation and calling {! Geojson.S.of_json}. 54 | 55 | Sticking with the [Ezjsone] example we can do 56 | 57 | {[ 58 | module G = Geojson.Make(Ezjsone_parser) 59 | 60 | let geojson_of_string s = 61 | let json = Ezjsone.value_from_string s in 62 | match G.of_json json with 63 | | Ok v -> v 64 | | Error (`Msg m) -> failwith m 65 | ]} 66 | 67 | {3 Manipulating and Accessing Geojson} 68 | 69 | Imagine you wished to access all the [properties] fields in your GeoJson document. There are two 70 | ways they can appear -- either as part of a single, toplevel {! Geojson.S.Feature} object or as 71 | a list of features inside {! Geojson.S.Feature.Collection}. Here's one way you could go about this: 72 | 73 | {[ 74 | let get_all_props s = 75 | let json = Ezjsone.value_from_string s in 76 | let geo = G.of_json json in 77 | match geo with 78 | | Ok (Feature f) -> Option.to_list @@ G.Feature.properties f 79 | | Ok (FeatureCollection fc) -> 80 | let fs = G.Feature.Collection.features fc in 81 | List.filter_map G.Feature.properties fs 82 | | _ -> [] 83 | ]} 84 | 85 | -------------------------------------------------------------------------------- /src/geojson/optics.ml: -------------------------------------------------------------------------------- 1 | let undefined _ = 2 | let exception Undefined in 3 | raise Undefined 4 | 5 | module Either = struct 6 | type ('a, 'b) t = Left of 'a | Right of 'b 7 | 8 | let left a = Left a 9 | let right b = Right b 10 | end 11 | 12 | module Lens = struct 13 | type ('s, 'a) t = V : ('s -> 'a * 'r) * ('a * 'r -> 's) -> ('s, 'a) t 14 | 15 | let v (type a b r) (f : a -> b * r) (g : b * r -> a) = V (f, g) 16 | let get (type a b) (V (f, _) : (a, b) t) (v : a) : b = fst @@ f v 17 | 18 | let set (type a b) (V (f, g) : (a, b) t) (t : a) (v : b) = 19 | let _, r = f t in 20 | g (v, r) 21 | 22 | let id x = x 23 | let fst : ('a * 'b, 'a) t = V (id, id) 24 | let snd : ('a * 'b, 'b) t = V ((fun (a, b) -> (b, a)), fun (b, a) -> (a, b)) 25 | 26 | let head : ('a list, 'a) t = 27 | V ((fun lst -> (List.hd lst, List.tl lst)), fun (hd, tl) -> hd :: tl) 28 | 29 | let splice_out lst n = 30 | let rec aux ((before, after) as acc) n = function 31 | | [] -> (List.rev before, List.rev after) 32 | | x :: xs when n < 0 -> aux (before, x :: after) (n - 1) xs 33 | | _ :: xs when n = 0 -> aux acc (n - 1) xs 34 | | x :: xs -> aux (x :: before, after) (n - 1) xs 35 | in 36 | aux ([], []) n lst 37 | 38 | let nth n : ('a list, 'a) t = 39 | V 40 | ( (fun lst -> (List.nth lst n, splice_out lst n)), 41 | fun (n, (b, a)) -> b @ [ n ] @ a ) 42 | 43 | let ( >> ) (type a b c) (V (f, g) : (a, b) t) (V (f', g') : (b, c) t) : 44 | (a, c) t = 45 | V 46 | ( (fun x -> 47 | let a, r1 = f x in 48 | let v, r2 = f' a in 49 | (v, (r1, r2))), 50 | fun (y, (r1, r2)) -> g (g' (y, r2), r1) ) 51 | end 52 | 53 | module Prism = struct 54 | type ('s, 'a) t = 55 | | V : ('s -> ('a, 'r) result) * (('a, 'r) result -> 's) -> ('s, 'a) t 56 | 57 | let get (type s a) (V (f, _) : (s, a) t) (v : s) : a option = 58 | Result.to_option @@ f v 59 | 60 | let set (type s a) (V (_, g) : (s, a) t) (v : a) = g (Ok v) 61 | 62 | let some = 63 | V 64 | ( (function Some t -> Ok t | None -> Error ()), 65 | function Ok t -> Some t | Error () -> None ) 66 | 67 | let none = 68 | V 69 | ( (function None -> Ok () | Some t -> Error t), 70 | function Ok () -> None | Error t -> Some t ) 71 | 72 | let ( >> ) (type a b c) (V (f, g) : (a, b) t) (V (f', g') : (b, c) t) : 73 | (a, c) t = 74 | let first x = 75 | match f x with 76 | | Error r1 -> Error (Either.left r1) 77 | | Ok b -> ( 78 | match f' b with Ok c -> Ok c | Error r2 -> Error (Either.right r2)) 79 | in 80 | let second = function 81 | | Ok v -> g (Ok (g' (Ok v))) 82 | | Error (Either.Left r1) -> g (Error r1) 83 | | Error (Either.Right r2) -> g (Ok (g' (Error r2))) 84 | in 85 | V (first, second) 86 | end 87 | 88 | module Optional = struct 89 | type ('s, 'a) t = ('s, 'a option) Lens.t 90 | 91 | let lens (type a b) (Lens.V (f, g) : (a, b) Lens.t) : (a, b) t = 92 | let wrapped_focus x = 93 | let v, r = f x in 94 | (Some v, r) 95 | in 96 | let wrapped_return = function 97 | | Some x, r -> g (x, r) 98 | | None, _ -> undefined () (* Not possible for a lens! *) 99 | in 100 | Lens.V (wrapped_focus, wrapped_return) 101 | 102 | let prism (type a b) (Prism.V (f, g) : (a, b) Prism.t) : (a, b) t = 103 | let wrapped_focus x = 104 | match f x with Ok v -> (Some v, None) | Error r -> (None, Some r) 105 | in 106 | let wrapped_return = function 107 | | Some x, None -> g (Ok x) 108 | | None, Some r -> g (Error r) 109 | | _ -> undefined () (* Other cases are not possible *) 110 | in 111 | Lens.V (wrapped_focus, wrapped_return) 112 | 113 | let ( >& ) (type a b c) (Lens.V (f1, g1) : (a, b) Lens.t) 114 | (Prism.V (f2, g2) : (b, c) Prism.t) : (a, c) t = 115 | let wrapped_focus x = 116 | let b, r1 = f1 x in 117 | match f2 b with 118 | | Ok c -> (Some c, Either.left r1) 119 | | Error r2 -> (None, Either.right (r1, r2)) 120 | in 121 | let wrapped_return = function 122 | | Some c, Either.Left r1 -> g1 (g2 (Ok c), r1) 123 | | None, Either.Right (r1, r2) -> g1 (g2 (Error r2), r1) 124 | | _ -> undefined () 125 | in 126 | Lens.V (wrapped_focus, wrapped_return) 127 | 128 | let ( >$ ) (type a b c) (Prism.V (f1, g1) : (a, b) Prism.t) 129 | (Lens.V (f2, g2) : (b, c) Lens.t) : (a, c) t = 130 | let wrapped_focus x = 131 | match f1 x with 132 | | Ok b -> 133 | let c, r2 = f2 b in 134 | (Some c, Either.right r2) 135 | | Error r1 -> (None, Either.left r1) 136 | in 137 | let wrapped_return = function 138 | | Some c, Either.Right r2 -> g1 (Ok (g2 (c, r2))) 139 | | None, Either.Left r1 -> g1 (Error r1) 140 | | _ -> undefined () 141 | in 142 | Lens.V (wrapped_focus, wrapped_return) 143 | 144 | let ( >> ) (type a b c) (Lens.V (f1, g1) : (a, b) t) 145 | (Lens.V (f2, g2) : (b, c) t) : (a, c) t = 146 | let wrapped_focus x = 147 | let b, r1 = f1 x in 148 | match b with 149 | | Some b -> 150 | let c, r2 = f2 b in 151 | (c, Either.right (r1, r2)) 152 | | None -> (None, Either.left r1) 153 | in 154 | let wrapped_return = function 155 | | c, Either.Right (r1, r2) -> g1 (Some (g2 (c, r2)), r1) 156 | | None, Either.Left r1 -> g1 (None, r1) 157 | | _ -> undefined () 158 | in 159 | Lens.V (wrapped_focus, wrapped_return) 160 | end 161 | 162 | module Infix = struct 163 | let ( >> ) = Optional.( >> ) 164 | let ( &> ) o l = Optional.(o >> lens l) 165 | let ( $> ) o p = Optional.(o >> prism p) 166 | let ( >& ) = Optional.( >& ) 167 | let ( >$ ) = Optional.( >$ ) 168 | let ( & ) = Lens.( >> ) 169 | let ( $ ) = Prism.( >> ) 170 | end 171 | -------------------------------------------------------------------------------- /src/geojsone/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name geojsone) 3 | (public_name geojsone) 4 | (libraries geojson hex sexplib0)) 5 | 6 | (include_subdirs unqualified) 7 | -------------------------------------------------------------------------------- /src/geojsone/eio/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name geojsone_eio) 3 | (public_name geojsone.eio) 4 | (optional) 5 | (libraries geojsone eio)) 6 | 7 | (include_subdirs no) 8 | -------------------------------------------------------------------------------- /src/geojsone/eio/geojsone_eio.ml: -------------------------------------------------------------------------------- 1 | let src_of_flow ?(buff = Cstruct.create 4096) flow () = 2 | let got = Eio.Flow.(single_read flow buff) in 3 | let t = Cstruct.sub buff 0 got in 4 | t 5 | 6 | let dst_of_flow flow b = 7 | match b with 8 | | bs -> Eio.Flow.(copy (cstruct_source [ bs ]) flow) 9 | | exception End_of_file -> () 10 | -------------------------------------------------------------------------------- /src/geojsone/eio/geojsone_eio.mli: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2021-2022 Patrick Ferris 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 12 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 13 | DEALINGS IN THE SOFTWARE. 14 | *) 15 | 16 | (** This sublibrary provides some convenient wrappers for working with GeoJSON 17 | from the Eio library. *) 18 | 19 | val src_of_flow : ?buff:Cstruct.t -> _ Eio.Flow.source -> Geojsone.Jsone.src 20 | (** [src_of_flow ?buff flow] takes any {! Eio.Flow.source} and allows you to use 21 | it as the source for reading the GeoJSON file. 22 | 23 | You can supply a buffer that will be used to read into, by default a buffer 24 | of size [4096] bytes is used.*) 25 | 26 | val dst_of_flow : _ Eio.Flow.sink -> Geojsone.Jsone.dst 27 | (** [src_of_flow flow] takes any {! Eio.Flow.sink} and allows you to use it as 28 | the sink for writing the GeoJSON file. *) 29 | -------------------------------------------------------------------------------- /src/geojsone/geojsone.ml: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2013 Thomas Gazagnaire 2 | Copyright (c) 2021-2022 Patrick Ferris 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 12 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 13 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 14 | DEALINGS IN THE SOFTWARE. 15 | 16 | The defunctionalised value construction is borrowed from Ezjsone. 17 | *) 18 | 19 | (* A GeoJson document consists of a single JSON document that is either a feature collection 20 | (an array of features), a single feature (an array of geometry objects) or a single geometry 21 | objects (which could contain multiple geometry objects thanks to the collection type). 22 | 23 | Most commmonly, the large size of a GeoJson document is because it is a feature collection 24 | containing many features, although it's probably not infeasible that there are huge documents 25 | containing a single feature with lots of geometry objects. *) 26 | 27 | module Err = struct 28 | type location = (int * int) * (int * int) 29 | type t = [ `Error of location * Jsone.error | `EOI | `Unexpected of string ] 30 | 31 | let pp ppf = function 32 | | `Error (((l1, l2), (l3, l4)), e) -> 33 | Format.fprintf ppf "Error %a (%i:%i - %i:%i)" Jsone.pp_error e l1 l2 l3 34 | l4 35 | | `EOI -> Format.fprintf ppf "Unexpected end of input" 36 | | `Unexpected s -> Format.fprintf ppf "Unexpected %s" s 37 | end 38 | 39 | exception Abort of Err.t 40 | 41 | module G = struct 42 | module Ezjsone_parser = struct 43 | type t = Ezjsone.value 44 | 45 | let catch_err f v = 46 | try Ok (f v) with Ezjsone.Parse_error (_, s) -> Error (`Msg s) 47 | 48 | let find = Ezjsone.find_opt 49 | let to_string t = catch_err Ezjsone.get_string t 50 | let string = Ezjsone.string 51 | let to_float t = catch_err Ezjsone.get_float t 52 | let float = Ezjsone.float 53 | let to_int t = catch_err Ezjsone.get_int t 54 | let int = Ezjsone.int 55 | let to_list f t = catch_err (Ezjsone.get_list f) t 56 | let list f t = Ezjsone.list f t 57 | let to_array f t = Result.map Array.of_list @@ to_list f t 58 | let array f t = list f (Array.to_list t) 59 | let to_obj t = catch_err Ezjsone.get_dict t 60 | let obj = Ezjsone.dict 61 | let null = `Null 62 | let is_null = function `Null -> true | _ -> false 63 | end 64 | 65 | include Geojson.Make (Ezjsone_parser) 66 | end 67 | 68 | let decode_single_object decoder : Ezjsone.value = 69 | let module Stack = struct 70 | type t = 71 | | In_array of Ezjsone.value list * t 72 | | In_object of string * (string * Ezjsone.value) list * t 73 | | Empty 74 | end in 75 | let loc () = Jsone.decoded_range decoder in 76 | let dec () = 77 | match Jsone.decode decoder with 78 | | `Lexeme l -> l 79 | | `Error e -> raise (Abort (`Error (loc (), e))) 80 | | `End -> raise (Abort `EOI) 81 | | `Await -> assert false 82 | in 83 | let rec enter l stack = 84 | match l with 85 | | `Os -> obj [] stack 86 | | _ -> raise (Abort (`Unexpected "decoding single object failed")) 87 | and value l stack = 88 | match l with 89 | | `Os -> obj [] stack 90 | | `As -> arr [] stack 91 | | (`Null | `Bool _ | `String _ | `Float _) as l -> continue l stack 92 | | _ -> raise (Abort (`Unexpected "value")) 93 | and arr so_far stack = 94 | match dec () with 95 | | `Ae -> continue (`A (List.rev so_far)) stack 96 | | l -> 97 | let stack = Stack.In_array (so_far, stack) in 98 | value l stack 99 | and obj so_far stack = 100 | match dec () with 101 | | `Oe -> continue (`O (List.rev so_far)) stack 102 | | `Name n -> 103 | let stack = Stack.In_object (n, so_far, stack) in 104 | value (dec ()) stack 105 | | _ -> raise (Abort (`Unexpected "object fields")) 106 | and continue v stack = 107 | match stack with 108 | | Stack.In_array (vs, stack) -> 109 | let so_far = v :: vs in 110 | arr so_far stack 111 | | Stack.In_object (n, ms, stack) -> 112 | let so_far = (n, v) :: ms in 113 | obj so_far stack 114 | | Stack.Empty -> v 115 | in 116 | enter (dec ()) Empty 117 | 118 | let encode_value e json = 119 | let module Stack = struct 120 | type t = 121 | | In_array of Ezjsone.value list * t 122 | | In_object of (string * Ezjsone.value) list * t 123 | | Empty 124 | end in 125 | let enc e l = ignore (Jsone.encode e (`Lexeme l)) in 126 | let rec t v e stack = 127 | match v with 128 | | `A vs -> 129 | enc e `As; 130 | arr vs e stack 131 | | `O ms -> 132 | enc e `Os; 133 | obj ms e stack 134 | and value v e stack = 135 | match v with 136 | | (`Null | `Bool _ | `Float _ | `String _) as v -> 137 | enc e v; 138 | continue e stack 139 | | #Ezjsone.t as x -> t (x :> Ezjsone.t) e stack 140 | and arr vs e stack = 141 | match vs with 142 | | v :: vs' -> 143 | let stack = Stack.In_array (vs', stack) in 144 | value v e stack 145 | | [] -> 146 | enc e `Ae; 147 | continue e stack 148 | and obj ms e stack = 149 | match ms with 150 | | (n, v) :: ms -> 151 | enc e (`Name n); 152 | let stack = Stack.In_object (ms, stack) in 153 | value v e stack 154 | | [] -> 155 | enc e `Oe; 156 | continue e stack 157 | and continue e stack = 158 | match stack with 159 | | Stack.In_array (vs, stack) -> arr vs e stack 160 | | Stack.In_object (ms, stack) -> obj ms e stack 161 | | Stack.Empty -> () 162 | in 163 | value json e Stack.Empty 164 | 165 | let map_geometry f src dst = 166 | let decoder = Jsone.decoder src in 167 | let encoder = Jsone.encoder dst in 168 | let loc () = Jsone.decoded_range decoder in 169 | let enc v = 170 | match Jsone.encode encoder v with 171 | | `Ok -> () 172 | | `Partial -> raise (Abort (`Unexpected "partial encoding")) 173 | in 174 | let rec go () = 175 | match Jsone.decode decoder with 176 | (* TODO(patricoferris): A geometry collection could explode on us here... *) 177 | | `Lexeme (`Name "geometry" as t) -> ( 178 | match G.of_json @@ decode_single_object decoder with 179 | | Error (`Msg m) -> raise (Abort (`Unexpected m)) 180 | | Ok v -> ( 181 | match G.geojson v with 182 | | Geometry g -> 183 | let g' = f g in 184 | enc (`Lexeme t); 185 | encode_value encoder 186 | (G.to_json @@ G.v ?bbox:(G.bbox v) (Geometry g')); 187 | go () 188 | | _ -> raise (Invalid_argument "Expected a geometry object"))) 189 | | `Lexeme _ as t -> 190 | enc t; 191 | go () 192 | | `Error e -> raise (Abort (`Error (loc (), e))) 193 | | `End -> ignore @@ Jsone.encode encoder `End 194 | | `Await -> assert false 195 | in 196 | try Ok (go ()) with Abort e -> Error e 197 | 198 | let map_props f src dst = 199 | let decoder = Jsone.decoder src in 200 | let encoder = Jsone.encoder dst in 201 | let loc () = Jsone.decoded_range decoder in 202 | let enc v = 203 | match Jsone.encode encoder v with 204 | | `Ok -> () 205 | | `Partial -> raise (Abort (`Unexpected "partial encoding")) 206 | in 207 | let rec go () = 208 | match Jsone.decode decoder with 209 | | `Lexeme (`Name "properties" as t) -> 210 | let o = f @@ decode_single_object decoder in 211 | enc (`Lexeme t); 212 | encode_value encoder o; 213 | go () 214 | | `Lexeme _ as t -> 215 | enc t; 216 | go () 217 | | `Error e -> raise (Abort (`Error (loc (), e))) 218 | | `End -> ignore @@ Jsone.encode encoder `End 219 | | `Await -> assert false 220 | in 221 | try Ok (go ()) with Abort e -> Error e 222 | 223 | let fold_geometry f init src = 224 | let decoder = Jsone.decoder src in 225 | let loc () = Jsone.decoded_range decoder in 226 | let rec go acc = 227 | match Jsone.decode decoder with 228 | | `Lexeme (`Name "geometry") -> ( 229 | match G.of_json @@ decode_single_object decoder with 230 | | Error (`Msg m) -> raise (Abort (`Unexpected m)) 231 | | Ok v -> ( 232 | match G.geojson v with 233 | | Geometry g -> 234 | let acc = f acc g in 235 | go acc 236 | | _ -> raise (Invalid_argument "Expected a geometry object"))) 237 | | `Lexeme _ -> go acc 238 | | `Error e -> raise (Abort (`Error (loc (), e))) 239 | | `End -> acc 240 | | `Await -> assert false 241 | in 242 | try Ok (go init) with Abort e -> Error e 243 | 244 | let fold_props f init src = 245 | let decoder = Jsone.decoder src in 246 | let loc () = Jsone.decoded_range decoder in 247 | let rec go acc = 248 | match Jsone.decode decoder with 249 | | `Lexeme (`Name "properties") -> 250 | let acc' = f acc @@ decode_single_object decoder in 251 | go acc' 252 | | `Lexeme _ -> go acc 253 | | `Error e -> raise (Abort (`Error (loc (), e))) 254 | | `End -> acc 255 | | `Await -> assert false 256 | in 257 | try Ok (go init) with Abort e -> Error e 258 | 259 | let iter_geometry f src = 260 | let decoder = Jsone.decoder src in 261 | let loc () = Jsone.decoded_range decoder in 262 | let rec go () = 263 | match Jsone.decode decoder with 264 | | `Lexeme (`Name "geometry") -> ( 265 | match G.of_json @@ decode_single_object decoder with 266 | | Error (`Msg m) -> raise (Abort (`Unexpected m)) 267 | | Ok g -> 268 | f g; 269 | go ()) 270 | | `Lexeme _ -> go () 271 | | `Error e -> raise (Abort (`Error (loc (), e))) 272 | | `End -> () 273 | | `Await -> assert false 274 | in 275 | try Ok (go ()) with Abort e -> Error e 276 | 277 | let iter_props f src = 278 | let decoder = Jsone.decoder src in 279 | let loc () = Jsone.decoded_range decoder in 280 | let rec go () = 281 | match Jsone.decode decoder with 282 | | `Lexeme (`Name "properties") -> 283 | f @@ decode_single_object decoder; 284 | go () 285 | | `Lexeme _ -> go () 286 | | `Error e -> raise (Abort (`Error (loc (), e))) 287 | | `End -> () 288 | | `Await -> assert false 289 | in 290 | try Ok (go ()) with Abort e -> Error e 291 | 292 | module Ezjsone = Ezjsone 293 | module Jsone = Jsone 294 | module Uutfe = Uutfe 295 | -------------------------------------------------------------------------------- /src/geojsone/geojsone.mli: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2021-2022 Patrick Ferris 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 12 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 13 | DEALINGS IN THE SOFTWARE. 14 | *) 15 | 16 | (** A library for manipulating large GeoJson documents without reading the whole 17 | document into memory using the {!Jsonm} streaming, JSON parser. *) 18 | 19 | module Err : sig 20 | type location = (int * int) * (int * int) 21 | type t = [ `Error of location * Jsone.error | `EOI | `Unexpected of string ] 22 | 23 | val pp : Format.formatter -> t -> unit 24 | end 25 | 26 | module G : Geojson.S with type json = Ezjsone.value 27 | 28 | (** {2 Maps} 29 | 30 | Maps are functions that allow you to manipulate common structure in GeoJson 31 | objects. These will be written directly back to the destination that you 32 | provide. *) 33 | 34 | val map_geometry : 35 | (G.Geometry.t -> G.Geometry.t) -> 36 | Jsone.src -> 37 | Jsone.dst -> 38 | (unit, Err.t) result 39 | (** [map_geometry f src dst] will apply [f] to all GeoJson objects. This is 40 | essentially any 41 | {{:https://datatracker.ietf.org/doc/html/rfc7946#section-3.1} geometry 42 | object}. 43 | 44 | The map will recurse into geometry collections. Note for the moment if you 45 | have a single geometry object as your document, this will not work. *) 46 | 47 | val map_props : 48 | (Ezjsone.value -> Ezjsone.value) -> 49 | Jsone.src -> 50 | Jsone.dst -> 51 | (unit, Err.t) result 52 | (** [map_props src dst ~f] will apply [f] to each feature's properties field. 53 | The properties field is decoded into an {!Ezjsone.value} for convenience. *) 54 | 55 | (** {2 Folds} 56 | 57 | Folds are like maps except you can collect items into an accumulator which 58 | is returned to you. 59 | 60 | For example, you might want to collect all of the [names] in the 61 | [properties] of features. 62 | 63 | {[ 64 | let get_string_exn = function `String s -> s | _ -> failwith "err" 65 | 66 | let get_name = function 67 | | `O assoc -> List.assoc "name" assoc |> get_string_exn 68 | | _ -> failwith "err" 69 | 70 | let places src = 71 | Geojsonm.fold_props (fun acc p -> get_name p :: acc) [] src 72 | ]} *) 73 | 74 | val fold_geometry : 75 | ('a -> G.Geometry.t -> 'a) -> 'a -> Jsone.src -> ('a, Err.t) result 76 | (** [fold_geometry f acc src] is much like {!map_geometry} but allows you to 77 | accumulate some result that is then returned to you. *) 78 | 79 | val fold_props : 80 | ('a -> Ezjsone.value -> 'a) -> 'a -> Jsone.src -> ('a, Err.t) result 81 | (** [fold_props f init src] *) 82 | 83 | (** {2 Iterators} 84 | 85 | Iterators are similar to map functions except they take a function [f] that 86 | takes a single element from the data-structure as an argument and returns 87 | [unit]. In that sense, they tend to be functions with side-effects, such as 88 | [print_endline]. 89 | 90 | For example, we might want to print the JSON value of every geometry object 91 | in a GeoJSON object. 92 | 93 | {[ 94 | let print_geometry g = 95 | print_endline @@ Ezjsone.value_to_string (Geojsonm.G.Geometry.to_json g) 96 | 97 | let values src = Geojsonm.iter_geometry print_geometry src 98 | ]} *) 99 | 100 | val iter_geometry : (G.t -> unit) -> Jsone.src -> (unit, Err.t) result 101 | (** [iter_geometry f src] will apply [f] to all GeoJson objects. *) 102 | 103 | val iter_props : (Ezjsone.value -> unit) -> Jsone.src -> (unit, Err.t) result 104 | (** [iter_props f src] will apply [f] to each feature's properties field. *) 105 | 106 | (** {2 Effect-based, non-blocking libraries} 107 | 108 | These libraries use effects to perform non-blocking parsing. They are 109 | currently a part of Geojsone and exposed for other libraries to use. *) 110 | 111 | module Ezjsone = Ezjsone 112 | module Jsone = Jsone 113 | module Uutfe = Uutfe 114 | -------------------------------------------------------------------------------- /src/geojsone/vendor/ezjsone/ezjsone.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Copyright (c) 2013 Thomas Gazagnaire 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | *) 16 | 17 | (* From http://erratique.ch/software/jsonm/doc/Jsone.html#datamodel *) 18 | type value = 19 | [ `Null 20 | | `Bool of bool 21 | | `Float of float 22 | | `String of string 23 | | `A of value list 24 | | `O of (string * value) list ] 25 | 26 | type t = [ `A of value list | `O of (string * value) list ] 27 | 28 | let value : t -> value = fun t -> (t :> value) 29 | 30 | module List = struct 31 | include List 32 | 33 | (* Tail-recursive List.map *) 34 | let map f l = rev (rev_map f l) 35 | end 36 | 37 | type error_location = (int * int) * (int * int) 38 | 39 | type read_value_error = 40 | [ `Error of error_location * Jsone.error 41 | | `Unexpected of 42 | [ `Lexeme of error_location * Jsone.lexeme * string | `End_of_input ] ] 43 | 44 | type read_error = [ read_value_error | `Not_a_t of value ] 45 | 46 | let json_of_src src : (value, [> read_value_error ]) result = 47 | let d = Jsone.decoder src in 48 | let exception Abort of read_value_error in 49 | let module Stack = struct 50 | type t = 51 | | In_array of value list * t 52 | | In_object of string * (string * value) list * t 53 | | Empty 54 | end in 55 | let loc () = Jsone.decoded_range d in 56 | let dec () = 57 | match Jsone.decode d with 58 | | `Lexeme l -> l 59 | | `Error e -> raise (Abort (`Error (loc (), e))) 60 | | `End -> raise (Abort (`Unexpected `End_of_input)) 61 | | `Await -> assert false 62 | in 63 | let rec value l stack = 64 | match l with 65 | | `Os -> obj [] stack 66 | | `As -> arr [] stack 67 | | (`Null | `Bool _ | `String _ | `Float _) as l -> continue l stack 68 | | _ -> raise (Abort (`Unexpected (`Lexeme (loc (), l, "value")))) 69 | and arr so_far stack = 70 | match dec () with 71 | | `Ae -> continue (`A (List.rev so_far)) stack 72 | | l -> 73 | let stack = Stack.In_array (so_far, stack) in 74 | value l stack 75 | and obj so_far stack = 76 | match dec () with 77 | | `Oe -> continue (`O (List.rev so_far)) stack 78 | | `Name n -> 79 | let stack = Stack.In_object (n, so_far, stack) in 80 | value (dec ()) stack 81 | | l -> raise (Abort (`Unexpected (`Lexeme (loc (), l, "object fields")))) 82 | and continue v stack = 83 | match stack with 84 | | Stack.In_array (vs, stack) -> 85 | let so_far = v :: vs in 86 | arr so_far stack 87 | | Stack.In_object (n, ms, stack) -> 88 | let so_far = (n, v) :: ms in 89 | obj so_far stack 90 | | Stack.Empty -> v 91 | in 92 | try Ok (value (dec ()) Empty) 93 | with Abort (#read_value_error as err) -> Error err 94 | 95 | let value_to_dst ?(minify = true) dst json = 96 | let module Stack = struct 97 | type t = 98 | | In_array of value list * t 99 | | In_object of (string * value) list * t 100 | | Empty 101 | end in 102 | let enc e l = ignore (Jsone.encode e (`Lexeme l)) in 103 | let rec t v e stack = 104 | match v with 105 | | `A vs -> 106 | enc e `As; 107 | arr vs e stack 108 | | `O ms -> 109 | enc e `Os; 110 | obj ms e stack 111 | and value v e stack = 112 | match v with 113 | | (`Null | `Bool _ | `Float _ | `String _) as v -> 114 | enc e v; 115 | continue e stack 116 | | #t as x -> t (x :> t) e stack 117 | and arr vs e stack = 118 | match vs with 119 | | v :: vs' -> 120 | let stack = Stack.In_array (vs', stack) in 121 | value v e stack 122 | | [] -> 123 | enc e `Ae; 124 | continue e stack 125 | and obj ms e stack = 126 | match ms with 127 | | (n, v) :: ms -> 128 | enc e (`Name n); 129 | let stack = Stack.In_object (ms, stack) in 130 | value v e stack 131 | | [] -> 132 | enc e `Oe; 133 | continue e stack 134 | and continue e stack = 135 | match stack with 136 | | Stack.In_array (vs, stack) -> arr vs e stack 137 | | Stack.In_object (ms, stack) -> obj ms e stack 138 | | Stack.Empty -> () 139 | in 140 | let e = Jsone.encoder ~minify dst in 141 | value json e Stack.Empty; 142 | ignore (Jsone.encode e `End) 143 | 144 | exception Parse_error of value * string 145 | 146 | let parse_error t fmt = 147 | Printf.ksprintf (fun msg -> raise (Parse_error (t, msg))) fmt 148 | 149 | let wrap t = `A [ t ] 150 | 151 | let unwrap = function 152 | | `A [ t ] -> t 153 | | v -> parse_error (v :> value) "Not unwrappable" 154 | 155 | let read_error_description : [< read_error ] -> string = function 156 | | `Error (_loc, err) -> Format.asprintf "%a" Jsone.pp_error err 157 | | `Unexpected `End_of_input -> Format.sprintf "Unexpected end of input" 158 | | `Unexpected (`Lexeme (_loc, _l, expectation)) -> 159 | Format.sprintf "Unexpected input when parsing a %s" expectation 160 | | `Not_a_t _value -> 161 | "We expected a well-formed JSON document (array or object)" 162 | 163 | let read_error_location : [< read_error ] -> error_location option = function 164 | | `Error (loc, _) -> Some loc 165 | | `Unexpected `End_of_input -> None 166 | | `Unexpected (`Lexeme (loc, _l, _expectation)) -> Some loc 167 | | `Not_a_t _value -> None 168 | 169 | let value_from_src_result src = json_of_src src 170 | 171 | let value_from_src src = 172 | match value_from_src_result src with 173 | | Ok t -> t 174 | | Error e -> parse_error `Null "JSON.of_buffer %s" (read_error_description e) 175 | 176 | let ensure_document_result : [> value ] -> ([> t ], [> read_error ]) result = 177 | function 178 | | #t as t -> Ok t 179 | | value -> Error (`Not_a_t value) 180 | 181 | let ensure_document : [> value ] -> [> t ] = function 182 | | #t as t -> t 183 | | t -> raise (Parse_error (t, "not a valid JSON array/object")) 184 | 185 | (* unit *) 186 | let unit () = `Null 187 | let get_unit = function `Null -> () | j -> parse_error j "Ezjsone.get_unit" 188 | 189 | (* bool *) 190 | let bool b = `Bool b 191 | let get_bool = function `Bool b -> b | j -> parse_error j "Ezjsone.get_bool" 192 | 193 | (* string *) 194 | let string s = `String s 195 | 196 | let get_string = function 197 | | `String s -> s 198 | | j -> parse_error j "Ezjsone.get_string" 199 | 200 | (* int *) 201 | let int i = `Float (float_of_int i) 202 | let int32 i = `Float (Int32.to_float i) 203 | let int64 i = `Float (Int64.to_float i) 204 | 205 | let get_int = function 206 | | `Float f -> int_of_float f 207 | | j -> parse_error j "Ezjsone.get_int" 208 | 209 | let get_int32 = function 210 | | `Float f -> Int32.of_float f 211 | | j -> parse_error j "Ezjsone.get_int32" 212 | 213 | let get_int64 = function 214 | | `Float f -> Int64.of_float f 215 | | j -> parse_error j "Ezjsone.get_int64" 216 | 217 | (* float *) 218 | let float f = `Float f 219 | 220 | let get_float = function 221 | | `Float f -> f 222 | | j -> parse_error j "Ezjsone.get_float" 223 | 224 | (* list *) 225 | let list fn l = `A (List.map fn l) 226 | 227 | let get_list fn = function 228 | | `A ks -> List.map fn ks 229 | | j -> parse_error j "Ezjsone.get_list" 230 | 231 | (* string lists *) 232 | let strings strings = list string strings 233 | let get_strings = get_list get_string 234 | 235 | (* options *) 236 | let option fn = function None -> `Null | Some x -> `A [ fn x ] 237 | 238 | let get_option fn = function 239 | | `Null -> None 240 | | `A [ j ] -> Some (fn j) 241 | | j -> parse_error j "Ezjsone.get_option" 242 | 243 | (* dict *) 244 | let dict d = `O d 245 | let get_dict = function `O d -> d | j -> parse_error j "Ezjsone.get_dict" 246 | 247 | (* pairs *) 248 | let pair fk fv (k, v) = `A [ fk k; fv v ] 249 | 250 | let get_pair fk fv = function 251 | | `A [ k; v ] -> (fk k, fv v) 252 | | j -> parse_error j "Ezjsone.get_pair" 253 | 254 | (* triple *) 255 | 256 | let triple fa fb fc (a, b, c) = `A [ fa a; fb b; fc c ] 257 | 258 | let get_triple fa fb fc = function 259 | | `A [ a; b; c ] -> (fa a, fb b, fc c) 260 | | j -> parse_error j "Ezjsone.get_triple" 261 | 262 | let mem t path = 263 | let rec aux j p = 264 | match (p, j) with 265 | | [], _ -> true 266 | | h :: tl, `O o -> List.mem_assoc h o && aux (List.assoc h o) tl 267 | | _ -> false 268 | in 269 | aux t path 270 | 271 | let find t path = 272 | let rec aux j p = 273 | match (p, j) with 274 | | [], j -> j 275 | | h :: tl, `O o -> aux (List.assoc h o) tl 276 | | _ -> raise Not_found 277 | in 278 | aux t path 279 | 280 | let find_opt t path = try Some (find t path) with Not_found -> None 281 | 282 | let map_dict f dict label = 283 | let rec aux acc = function 284 | | [] -> ( 285 | match f `Null with 286 | | None -> List.rev acc 287 | | Some j -> List.rev_append acc [ (label, j) ]) 288 | | ((l, j) as e) :: dict -> 289 | if l = label then 290 | match f j with 291 | | None -> List.rev_append acc dict 292 | | Some j -> List.rev_append acc ((l, j) :: dict) 293 | else aux (e :: acc) dict 294 | in 295 | aux [] dict 296 | 297 | let map f t path = 298 | let rec aux t = function 299 | | [] -> f t 300 | | h :: tl -> ( 301 | match t with 302 | | `O d -> Some (`O (map_dict (fun t -> aux t tl) d h)) 303 | | _ -> None) 304 | in 305 | match aux t path with None -> raise Not_found | Some j -> j 306 | 307 | let update t path v = map (fun _ -> v) t path 308 | 309 | exception Not_utf8 310 | 311 | let is_valid_utf8 str = 312 | try 313 | Uutfe.String.fold_utf_8 314 | (fun _ _ -> function `Malformed _ -> raise Not_utf8 | _ -> ()) 315 | () str; 316 | true 317 | with Not_utf8 -> false 318 | 319 | let encode_string str = 320 | if is_valid_utf8 str then string str 321 | else 322 | let (`Hex h) = Hex.of_string str in 323 | `O [ ("hex", string h) ] 324 | 325 | let decode_string = function 326 | | `String str -> Some str 327 | | `O [ ("hex", `String str) ] -> Some (Hex.to_string (`Hex str)) 328 | | _ -> None 329 | 330 | let decode_string_exn j = 331 | match decode_string j with 332 | | Some s -> s 333 | | None -> parse_error j "Ezjsone.decode_string_exn" 334 | 335 | let rec of_sexp = function 336 | | Sexplib0.Sexp.Atom x -> encode_string x 337 | | Sexplib0.Sexp.List l -> list of_sexp l 338 | 339 | let value_of_sexp = of_sexp 340 | 341 | let t_of_sexp s = 342 | match value_of_sexp s with 343 | | `A x -> `A x 344 | | `O x -> `O x 345 | | _ -> 346 | failwith 347 | "Ezjsone: t_of_sexp encountered a value (fragment) rather than a t" 348 | 349 | let rec to_sexp json = 350 | match decode_string json with 351 | | Some s -> Sexplib0.Sexp.Atom s 352 | | None -> ( 353 | match json with 354 | | `A l -> Sexplib0.Sexp.List (List.map to_sexp l) 355 | | _ -> parse_error json "Ezjsone.to_sexp") 356 | 357 | let sexp_of_value = to_sexp 358 | let sexp_of_t t = sexp_of_value @@ value t 359 | -------------------------------------------------------------------------------- /src/geojsone/vendor/jsone/jsone.ml: -------------------------------------------------------------------------------- 1 | (*--------------------------------------------------------------------------- 2 | Copyright (c) 2012 The jsonm programmers. All rights reserved. 3 | Distributed under the ISC license, see terms at the end of the file. 4 | ---------------------------------------------------------------------------*) 5 | 6 | (* Braced non-terminals in comments refer to RFC 4627 non-terminals. *) 7 | let io_buffer_size = 65536 (* IO_BUFFER_SIZE 4.0.0 *) 8 | let pp = Format.fprintf 9 | 10 | (* Unsafe string and bytes manipulations. If you don't believe the authors's 11 | invariants, replacing with safe versions makes everything safe in the 12 | module. He won't be upset. *) 13 | 14 | let unsafe_byte s j = Char.code (String.unsafe_get s j) 15 | 16 | external unsafe_blit_from_string : 17 | string -> int -> Cstruct.buffer -> int -> int -> unit 18 | = "caml_blit_string_to_bigstring" 19 | [@@noalloc] 20 | 21 | let unsafe_blit s so c co l = unsafe_blit_from_string s so c.Cstruct.buffer co l 22 | 23 | let unsafe_set_byte s j byte = 24 | Bigarray.Array1.unsafe_set s.Cstruct.buffer j (Char.unsafe_chr byte) 25 | 26 | (* Characters and their classes *) 27 | 28 | let ux_eoi = max_int (* End of input, outside unicode range. *) 29 | let ux_soi = max_int - 1 (* Start of input, outside unicode range. *) 30 | let u_nl = 0x0A (* \n *) 31 | let u_sp = 0x20 (* *) 32 | let u_quot = 0x22 (* '' *) 33 | let u_lbrack = 0x5B (* [ *) 34 | let u_rbrack = 0x5D (* ] *) 35 | let u_lbrace = 0x7B (* { *) 36 | let u_rbrace = 0x7D (* } *) 37 | let u_colon = 0x3A (* : *) 38 | let u_comma = 0x2C (* , *) 39 | let u_minus = 0x2D (* - *) 40 | let u_slash = 0x2F (* / *) 41 | let u_bslash = 0x5C (* \ *) 42 | let u_rep = Uchar.to_int Uutfe.u_rep 43 | let must_escape u = u <= 0x1F || u = 0x22 || u = 0x5C 44 | let is_digit u = 0x30 <= u && u <= 0x39 45 | 46 | let is_hex_digit u = 47 | (0x30 <= u && u <= 0x39) 48 | || (0x41 <= u && u <= 0x46) 49 | || (0x61 <= u && u <= 0x66) 50 | 51 | let is_white = function 52 | (* N.B. Uutfe normalizes U+000D to U+000A. *) 53 | | 0x20 | 0x09 | 0x0A -> true 54 | | _ -> false 55 | 56 | let is_val_sep = function 57 | (* N.B. Uutfe normalizes U+000D to U+000A. *) 58 | | 0x20 | 0x09 | 0x0A | 0x2C | 0x5D | 0x7D -> true 59 | | _ -> false 60 | 61 | (* Data model *) 62 | 63 | type lexeme = 64 | [ `Null 65 | | `Bool of bool 66 | | `String of string 67 | | `Float of float 68 | | `Name of string 69 | | `As 70 | | `Ae 71 | | `Os 72 | | `Oe ] 73 | 74 | let pp_lexeme ppf = function 75 | | `Null -> pp ppf "`Null" 76 | | `Bool b -> pp ppf "@[`Bool %b@]" b 77 | | `String s -> pp ppf "@[`String %S@]" s 78 | | `Name s -> pp ppf "@[`Name %S@]" s 79 | | `Float f -> pp ppf "@[`Float %s@]" (string_of_float f) 80 | | `As -> pp ppf "`As" 81 | | `Ae -> pp ppf "`Ae" 82 | | `Os -> pp ppf "`Os" 83 | | `Oe -> pp ppf "`Oe" 84 | 85 | (* Decode *) 86 | 87 | type error = 88 | [ `Illegal_BOM 89 | | `Illegal_escape of 90 | [ `Not_hex_uchar of Uchar.t 91 | | `Not_esc_uchar of Uchar.t 92 | | `Not_lo_surrogate of int 93 | | `Lone_lo_surrogate of int 94 | | `Lone_hi_surrogate of int ] 95 | | `Illegal_string_uchar of Uchar.t 96 | | `Illegal_bytes of string 97 | | `Illegal_literal of string 98 | | `Illegal_number of string 99 | | `Unclosed of [ `As | `Os | `String | `Comment ] 100 | | `Expected of 101 | [ `Comment 102 | | `Value 103 | | `Name 104 | | `Name_sep 105 | | `Json 106 | | `Eoi 107 | | `Aval of bool (* [true] if first array value *) 108 | | `Omem of bool (* [true] if first object member *) ] ] 109 | 110 | let err_bom = `Error `Illegal_BOM 111 | let err_not_hex u = `Error (`Illegal_escape (`Not_hex_uchar (Uchar.of_int u))) 112 | let err_not_esc u = `Error (`Illegal_escape (`Not_esc_uchar (Uchar.of_int u))) 113 | let err_not_lo p = `Error (`Illegal_escape (`Not_lo_surrogate p)) 114 | let err_lone_lo p = `Error (`Illegal_escape (`Lone_lo_surrogate p)) 115 | let err_lone_hi p = `Error (`Illegal_escape (`Lone_hi_surrogate p)) 116 | let err_str_char u = `Error (`Illegal_string_uchar (Uchar.of_int u)) 117 | let err_unclosed_string = `Error (`Unclosed `String) 118 | let err_unclosed_arr = `Error (`Unclosed `As) 119 | let err_unclosed_obj = `Error (`Unclosed `Os) 120 | let err_number s = `Error (`Illegal_number s) 121 | let err_literal s = `Error (`Illegal_literal s) 122 | let err_exp_value = `Error (`Expected `Value) 123 | let err_exp_name = `Error (`Expected `Name) 124 | let err_exp_nsep = `Error (`Expected `Name_sep) 125 | let err_exp_arr_fst = `Error (`Expected (`Aval true)) 126 | let err_exp_arr_nxt = `Error (`Expected (`Aval false)) 127 | let err_exp_obj_fst = `Error (`Expected (`Omem true)) 128 | let err_exp_obj_nxt = `Error (`Expected (`Omem false)) 129 | let err_exp_json = `Error (`Expected `Json) 130 | let err_exp_eoi = `Error (`Expected `Eoi) 131 | let pp_cp ppf u = pp ppf "U+%04X" u 132 | 133 | let pp_uchar ppf u = 134 | if Uchar.to_int u <= 0x1F (* most control chars *) then 135 | pp_cp ppf (Uchar.to_int u) 136 | else 137 | let b = Buffer.create 4 in 138 | Uutfe.Buffer.add_utf_8 b u; 139 | pp ppf "'%s' (%a)" (Buffer.contents b) pp_cp (Uchar.to_int u) 140 | 141 | let pp_error ppf = function 142 | | `Illegal_BOM -> pp ppf "@[illegal@ initial@ BOM@ in@ character@ stream@]" 143 | | `Illegal_escape r -> ( 144 | pp ppf "@[illegal@ escape,@ "; 145 | match r with 146 | | `Not_hex_uchar u -> pp ppf "%a@ not@ a@ hex@ digit@]" pp_uchar u 147 | | `Not_esc_uchar u -> 148 | pp ppf "%a@ not@ an@ escaped@ character@]" pp_uchar u 149 | | `Lone_lo_surrogate p -> pp ppf "%a@ lone@ low@ surrogate@]" pp_cp p 150 | | `Lone_hi_surrogate p -> pp ppf "%a@ lone@ high@ surrogate@]" pp_cp p 151 | | `Not_lo_surrogate p -> pp ppf "%a@ not@ a@ low@ surrogate@]" pp_cp p) 152 | | `Illegal_string_uchar u -> 153 | pp ppf "@[illegal@ character@ in@ JSON@ string@ (%a)@]" pp_uchar u 154 | | `Illegal_bytes bs -> 155 | let l = String.length bs in 156 | pp ppf "@[illegal@ bytes@ in@ character@ stream@ ("; 157 | if l > 0 then pp ppf "%02X" (Char.code bs.[0]); 158 | for i = 1 to l - 1 do 159 | pp ppf " %02X" (Char.code bs.[i]) 160 | done; 161 | pp ppf ")@]" 162 | | `Illegal_number n -> pp ppf "@[illegal@ number@ (%s)@]" n 163 | | `Illegal_literal l -> pp ppf "@[illegal@ literal@ (%s)@]" l 164 | | `Unclosed r -> ( 165 | pp ppf "@[unclosed@ "; 166 | match r with 167 | | `As -> pp ppf "array@]" 168 | | `Os -> pp ppf "object@]" 169 | | `String -> pp ppf "string@]" 170 | | `Comment -> pp ppf "comment@]") 171 | | `Expected r -> ( 172 | pp ppf "@[expected@ "; 173 | match r with 174 | | `Comment -> pp ppf "JavaScript@ comment@]" 175 | | `Value -> pp ppf "JSON@ value@]" 176 | | `Name -> pp ppf "member@ name@]" 177 | | `Name_sep -> pp ppf "name@ separator@ (':')@]" 178 | | `Aval true -> pp ppf "value@ or@ array@ end@ (value@ or@ ']')@]" 179 | | `Aval false -> 180 | pp ppf "value@ separator@ or@ array@ end@ (','@ or@ ']')@]" 181 | | `Omem true -> pp ppf "member@ name@ or@ object@ end@ ('\"'@ or@ '}')@]" 182 | | `Omem false -> 183 | pp ppf "value@ separator@ or@ object@ end@ (','@ or@ '}')@]" 184 | | `Json -> pp ppf "JSON@ text (JSON value)@]" 185 | | `Eoi -> pp ppf "end@ of@ input@]") 186 | 187 | type pos = int * int 188 | type encoding = [ `UTF_8 | `UTF_16 | `UTF_16BE | `UTF_16LE ] 189 | type src = unit -> Cstruct.t 190 | type decode = [ `Await | `End | `Lexeme of lexeme | `Error of error ] 191 | type uncut = [ `Comment of [ `M | `S ] * string | `White of string ] 192 | 193 | let pp_decode ppf = function 194 | | `Lexeme l -> pp ppf "@[`Lexeme @[(%a)@]@]" pp_lexeme l 195 | | `Await -> pp ppf "`Await" 196 | | `End -> pp ppf "`End" 197 | | `Error e -> pp ppf "@[`Error @[(%a)@]@]" pp_error e 198 | | `White s -> pp ppf "@[`White @[%S@]@]" s 199 | | `Comment (style, s) -> 200 | let pr_style ppf = function `M -> pp ppf "`M" | `S -> pp ppf "`S" in 201 | pp ppf "@[`Comment @[(%a, %S)@]@]" pr_style style s 202 | 203 | type decoder = { 204 | u : Uutfe.decoder; (* Unicode character decoder. *) 205 | buf : Buffer.t; (* string accumulation buffer. *) 206 | mutable started : bool; 207 | mutable uncut : bool; (* [true] to bufferize comments and white space. *) 208 | mutable s_line : int; (* last saved start line. *) 209 | mutable s_col : int; (* last saved start column. *) 210 | mutable e_line : int; (* last saved end line. *) 211 | mutable e_col : int; (* last saved end column. *) 212 | mutable c : int; (* character lookahead. *) 213 | mutable stack : 214 | (* stack of open arrays and objects. *) 215 | [ `As of pos | `Os of pos ] list; 216 | mutable next_name : bool; (* [true] if next decode should be [`Name]. *) 217 | mutable last_start : bool; (* [true] if last lexeme was `As or `Os. *) 218 | } 219 | 220 | let baddc d c = Uutfe.Buffer.add_utf_8 d.buf (Uchar.unsafe_of_int c) 221 | let badd d = Uutfe.Buffer.add_utf_8 d.buf (Uchar.unsafe_of_int d.c) 222 | 223 | let buf d = 224 | let t = Buffer.contents d.buf in 225 | Buffer.clear d.buf; 226 | t 227 | 228 | let dpos d = (Uutfe.decoder_line d.u, Uutfe.decoder_col d.u) 229 | 230 | let spos d = 231 | d.s_line <- Uutfe.decoder_line d.u; 232 | d.s_col <- Uutfe.decoder_col d.u 233 | 234 | let epos d = 235 | d.e_line <- Uutfe.decoder_line d.u; 236 | d.e_col <- Uutfe.decoder_col d.u 237 | 238 | let stack_range d = 239 | match d.stack with 240 | | [] -> assert false 241 | | `As (l, c) :: _ | `Os (l, c) :: _ -> 242 | d.s_line <- l; 243 | d.s_col <- c; 244 | epos d 245 | 246 | let dpop d = 247 | match 248 | spos d; 249 | epos d; 250 | d.stack 251 | with 252 | | _ :: (`Os _ :: _ as ss) -> 253 | d.next_name <- true; 254 | d.stack <- ss 255 | | _ :: (`As _ :: _ as ss) -> 256 | d.next_name <- false; 257 | d.stack <- ss 258 | | [ _ ] -> 259 | d.next_name <- false; 260 | d.stack <- [] 261 | | [] -> assert false 262 | 263 | let ret_eoi _d = `End 264 | 265 | (* let ret (v : [< decode | uncut]) k d = d.k <- k; v *) 266 | let readc d = 267 | match Uutfe.decode d.u with 268 | | `Uchar u -> d.c <- Uchar.to_int u 269 | | `End -> d.c <- ux_eoi 270 | | `Await -> assert false 271 | | `Malformed _bs -> 272 | d.c <- u_rep; 273 | epos d 274 | 275 | (* ; ret (err_bytes bs) k d *) 276 | 277 | (* let rec r_scomment d = (* single line comment. // was eaten. *) 278 | if (d.c <> u_nl && d.c <> ux_eoi) then (badd d; readc d; r_scomment d) else 279 | (epos d; (`Comment (`S, buf d))) *) 280 | 281 | (* let rec r_mcomment closing d = (* multiline comment. /* was eaten. *) 282 | if (d.c = ux_eoi) then (epos d; err_unclosed_comment ) else 283 | if closing then begin 284 | if (d.c = u_slash) then (epos d; (`Comment (`M, buf d))) else 285 | if (d.c = u_times) then (badd d; readc d; r_mcomment true d) else 286 | (baddc d u_times; badd d; readc d; r_mcomment false d) 287 | end else begin 288 | if (d.c = u_times) then (readc d; r_mcomment true d) else 289 | (badd d; readc d; r_mcomment false d) 290 | end *) 291 | 292 | (* let r_comment d = (* comment, / was eaten. *) 293 | if d.c = u_slash then (readc d; r_scomment d) else 294 | if d.c = u_times then (readc d; r_mcomment false d) else 295 | (epos d; err_exp_comment) 296 | 297 | let rec r_ws_uncut d = 298 | if (is_white d.c) then (epos d; badd d; readc d; r_ws_uncut d) else 299 | (* ret (`White (buf d)) k d *) 300 | (* `White (buf d) *) 301 | Buffer.clear d.buf; 302 | () 303 | 304 | let rec r_white_uncut d = (* {ws} / comment *) 305 | (* Not sure about this! *) 306 | if (is_white d.c) then (spos d; r_ws_uncut d; ) else 307 | if (d.c = u_slash) then (spos d; readc d; r_comment d) else 308 | (* r_white_uncut d *) () 309 | *) 310 | 311 | let rec r_ws d = 312 | while is_white d.c do 313 | readc d; 314 | r_ws d 315 | done 316 | (* {ws} *) 317 | 318 | (* TODO: Add uncut *) 319 | let r_white d = r_ws d 320 | 321 | let rec r_u_escape hi u count d = 322 | (* unicode escapes. *) 323 | let error err d = 324 | baddc d u_rep; 325 | err 326 | in 327 | if count > 0 then ( 328 | if not (is_hex_digit d.c) then ( 329 | epos d; 330 | ignore @@ error (err_not_hex d.c) d; 331 | readc d) 332 | else 333 | let u = 334 | (u * 16) 335 | + 336 | if d.c <= 0x39 (* 9 *) then d.c - 0x30 337 | else if d.c <= 0x46 (* F *) then d.c - 0x37 338 | else d.c - 0x57 339 | in 340 | epos d; 341 | readc d; 342 | ignore @@ (r_u_escape hi u (count - 1)) d) 343 | else 344 | match hi with 345 | | Some hi -> 346 | (* combine high and low surrogate into scalar value. *) 347 | if u < 0xDC00 || u > 0xDFFF then ignore @@ error (err_not_lo u) d 348 | else 349 | let u = (((hi land 0x3FF) lsl 10) lor (u land 0x3FF)) + 0x10000 in 350 | baddc d u (* error (err_lone_lo u) d *) 351 | | None -> 352 | if u < 0xD800 || u > 0xDFFF then ignore @@ error (err_lone_lo u) d 353 | else if u > 0xDBFF then ignore @@ error (err_lone_lo u) d 354 | else if d.c <> u_bslash then ignore @@ error (err_lone_hi u) d 355 | else ( 356 | readc d; 357 | (fun d -> 358 | if d.c <> 0x75 (* u *) then ignore @@ error (err_lone_hi u) d 359 | else ( 360 | readc d; 361 | (r_u_escape (Some u) 0 4) d)) 362 | d) 363 | 364 | and r_escape d = 365 | match d.c with 366 | | 0x22 (* '' *) -> 367 | baddc d u_quot; 368 | readc d 369 | | 0x5C (* \ *) -> 370 | baddc d u_bslash; 371 | readc d 372 | | 0x2F (* / *) -> 373 | baddc d u_slash; 374 | readc d 375 | | 0x62 (* b *) -> 376 | baddc d 0x08; 377 | readc d 378 | | 0x66 (* f *) -> 379 | baddc d 0x0C; 380 | readc d 381 | | 0x6E (* n *) -> 382 | baddc d u_nl; 383 | readc d 384 | | 0x72 (* r *) -> 385 | baddc d 0x0D; 386 | readc d 387 | | 0x74 (* t *) -> 388 | baddc d 0x09; 389 | readc d 390 | | 0x75 (* u *) -> 391 | readc d; 392 | r_u_escape None 0 4 d 393 | | c -> 394 | epos d; 395 | baddc d u_rep; 396 | ignore (err_not_esc c); 397 | readc d 398 | 399 | let rec r_string d = 400 | (* {string}, '' eaten. *) 401 | if d.c = ux_eoi then ( 402 | epos d; 403 | ignore err_unclosed_string; 404 | ignore @@ ret_eoi d) 405 | else if not (must_escape d.c) then ( 406 | badd d; 407 | readc d; 408 | r_string d) 409 | else if d.c = u_quot then ( 410 | epos d; 411 | readc d) 412 | else if d.c = u_bslash then ( 413 | readc d; 414 | r_escape d; 415 | r_string d) 416 | else ( 417 | epos d; 418 | baddc d u_rep; 419 | let e = err_str_char d.c in 420 | readc d; 421 | r_string d; 422 | ignore e) 423 | 424 | let rec r_float d = 425 | (* {number} *) 426 | if (not (is_val_sep d.c)) && d.c <> ux_eoi then ( 427 | epos d; 428 | badd d; 429 | readc d; 430 | r_float d) 431 | else 432 | let s = buf d in 433 | try `Lexeme (`Float (float_of_string s)) with Failure _ -> err_number s 434 | 435 | let rec r_literal d = 436 | (* {true} / {false} / {null} *) 437 | if (not (is_val_sep d.c)) && d.c <> ux_eoi then ( 438 | epos d; 439 | badd d; 440 | readc d; 441 | r_literal d) 442 | else 443 | match buf d with 444 | | "true" -> `Lexeme (`Bool true) 445 | | "false" -> `Lexeme (`Bool false) 446 | | "null" -> `Lexeme `Null 447 | | s -> err_literal s 448 | 449 | let r_value err d = 450 | match d.c with 451 | (* {value} *) 452 | | 0x5B (* [ *) -> 453 | (* {begin-array} *) 454 | spos d; 455 | epos d; 456 | d.last_start <- true; 457 | d.stack <- `As (dpos d) :: d.stack; 458 | (* ret (`Lexeme `As) (readc k) d *) 459 | let v = `Lexeme `As in 460 | readc d; 461 | v 462 | | 0x7B (* { *) -> 463 | (* {begin-object} *) 464 | spos d; 465 | epos d; 466 | d.last_start <- true; 467 | d.next_name <- true; 468 | d.stack <- `Os (dpos d) :: d.stack; 469 | (* ret (`Lexeme `Os) (readc k) d *) 470 | let v = `Lexeme `Os in 471 | readc d; 472 | v 473 | | 0x22 (* '' *) -> 474 | let lstring d = `Lexeme (`String (buf d)) in 475 | spos d; 476 | readc d; 477 | r_string d; 478 | lstring d 479 | | 0x66 (* f *) | 0x6E (* n *) | 0x74 (* t *) -> 480 | spos d; 481 | r_literal d 482 | | u when is_digit u || u = u_minus -> 483 | spos d; 484 | r_float d 485 | | _u -> err d 486 | 487 | let rec discard_to c1 c2 err d = 488 | (* Printf.printf "%c %c\n" (Char.chr c1) (Char.chr c2); *) 489 | if d.c = c1 || d.c = c2 || d.c = ux_eoi then err 490 | else ( 491 | epos d; 492 | readc d; 493 | (discard_to c1 c2 err) d) 494 | 495 | let r_arr_val d = 496 | (* [{value-separator}] {value} / {end-array} *) 497 | let nxval err d = 498 | spos d; 499 | discard_to u_comma u_rbrack err d 500 | in 501 | let last_start = d.last_start in 502 | d.last_start <- false; 503 | if d.c = ux_eoi then ( 504 | stack_range d; 505 | err_unclosed_arr) 506 | else if d.c = u_rbrack then ( 507 | dpop d; 508 | readc d; 509 | `Lexeme `Ae) 510 | else if last_start then r_value (nxval err_exp_arr_fst) d 511 | else if d.c = u_comma then ( 512 | readc d; 513 | r_white d; 514 | r_value (nxval err_exp_value) d) 515 | else nxval err_exp_arr_nxt d 516 | 517 | let nxmem err d = 518 | (* Printf.printf "NXM: %c" (Char.chr d.c); *) 519 | spos d; 520 | d.next_name <- true; 521 | discard_to u_comma u_rbrace err d 522 | 523 | let r_obj_value d = 524 | (* {name-separator} {value} *) 525 | d.next_name <- true; 526 | if d.c = u_colon then ( 527 | readc d; 528 | r_white d; 529 | r_value (nxmem err_exp_value) d) 530 | else nxmem err_exp_nsep d 531 | 532 | let r_obj_name d = 533 | (* [{value-separator}] string / end-object *) 534 | let r_name err d = 535 | let ln d = 536 | let s = buf d in 537 | `Lexeme (`Name s) 538 | in 539 | if d.c <> u_quot then nxmem err d 540 | else ( 541 | spos d; 542 | readc d; 543 | r_string d; 544 | ln d) 545 | in 546 | let last_start = d.last_start in 547 | d.last_start <- false; 548 | d.next_name <- false; 549 | if d.c = ux_eoi then ( 550 | stack_range d; 551 | err_unclosed_obj) 552 | else if d.c = u_rbrace then ( 553 | dpop d; 554 | readc d; 555 | `Lexeme `Oe) 556 | else if last_start then r_name err_exp_obj_fst d 557 | else if d.c = u_comma then ( 558 | readc d; 559 | r_white d; 560 | r_name err_exp_name d) 561 | else nxmem err_exp_obj_nxt d 562 | 563 | let r_end d = 564 | (* end of input *) 565 | if d.c = ux_eoi then ret_eoi d 566 | else 567 | let drain d = 568 | spos d; 569 | discard_to ux_eoi ux_eoi err_exp_eoi d 570 | in 571 | drain d 572 | 573 | let r_lexeme d = 574 | match d.stack with 575 | | `As _ :: _ -> 576 | r_white d; 577 | r_arr_val d 578 | | `Os (_i, _j) :: _ -> 579 | if d.next_name then ( 580 | r_white d; 581 | r_obj_name d) 582 | else ( 583 | r_white d; 584 | r_obj_value d) 585 | | [] -> 586 | r_white d; 587 | r_end d 588 | 589 | let discard_to_white d = 590 | while not (is_white d.c || d.c = ux_eoi) do 591 | epos d; 592 | readc d 593 | done 594 | 595 | let rec r_json d = 596 | (* {value} *) 597 | let err d = 598 | spos d; 599 | discard_to_white d; 600 | r_white d; 601 | r_json d 602 | in 603 | if d.c <> ux_eoi then r_value err d else err_exp_json 604 | 605 | let r_start d = 606 | (* start of input *) 607 | let bom d = 608 | if Uutfe.decoder_removed_bom d.u then err_bom 609 | else ( 610 | r_white d; 611 | r_json d) 612 | in 613 | readc d; 614 | bom d 615 | 616 | let nln = `ASCII (Uchar.unsafe_of_int 0x000A) 617 | 618 | let decoder ?encoding src = 619 | let u = Uutfe.decoder ?encoding ~nln src in 620 | { 621 | u; 622 | buf = Buffer.create 1024; 623 | uncut = false; 624 | started = false; 625 | s_line = 1; 626 | s_col = 0; 627 | e_line = 1; 628 | e_col = 0; 629 | c = ux_soi; 630 | next_name = false; 631 | last_start = false; 632 | stack = []; 633 | } 634 | 635 | let decode_uncut d = 636 | d.uncut <- true; 637 | r_start d 638 | 639 | let decode d = 640 | match 641 | d.uncut <- false; 642 | if d.started then r_lexeme d 643 | else ( 644 | d.started <- true; 645 | r_start d) 646 | with 647 | | #decode as v -> (v :> [> decode ]) 648 | | `Comment _ | `White _ -> assert false 649 | 650 | let decoder_src d = Uutfe.decoder_src d.u 651 | let decoded_range d = ((d.s_line, d.s_col), (d.e_line, d.e_col)) 652 | 653 | let decoder_encoding d = 654 | match Uutfe.decoder_encoding d.u with 655 | | #encoding as enc -> enc 656 | | `US_ASCII | `ISO_8859_1 -> assert false 657 | 658 | (* Encode *) 659 | 660 | let invalid_arg fmt = 661 | let b = Buffer.create 20 in 662 | (* for thread safety. *) 663 | let ppf = Format.formatter_of_buffer b in 664 | let k ppf = 665 | Format.pp_print_flush ppf (); 666 | invalid_arg (Buffer.contents b) 667 | in 668 | Format.kfprintf k ppf fmt 669 | 670 | let invalid_bounds j l = invalid_arg "invalid bounds (index %d, length %d)" j l 671 | let expect e v = invalid_arg "%a encoded but expected %s" pp_decode v e 672 | let expect_end l = expect "`End" (`Lexeme l) 673 | let expect_mem_value l = expect "any `Lexeme but `Name, `Oe or `Ae" (`Lexeme l) 674 | let expect_arr_value_ae l = expect "any `Lexeme but `Name or `Oe" (`Lexeme l) 675 | let expect_name_or_oe l = expect "`Lexeme (`Name _ | `Oe)" (`Lexeme l) 676 | 677 | let expect_json v = 678 | expect "`Lexeme (`Null | `Bool _ | `Float _ | `String _ | `As | `Os)" v 679 | 680 | let expect_lend lstart v = 681 | expect (if lstart = `As then "`Lexeme `Ae" else "`Lexeme `Oe") v 682 | 683 | type dst = Cstruct.t -> unit 684 | type encode = [ `Await | `End | `Lexeme of lexeme ] 685 | 686 | type encoder = { 687 | dst : dst; (* output destination. *) 688 | minify : bool; (* [true] for compact output. *) 689 | mutable encode_ : bool; 690 | mutable started : bool; 691 | mutable o : Cstruct.t; (* current output chunk. *) 692 | mutable o_pos : int; (* next output position to write. *) 693 | mutable o_max : int; (* maximal output position to write. *) 694 | buf : Buffer.t; (* buffer to format floats. *) 695 | mutable stack : [ `As | `Os ] list; (* stack of open arrays and objects. *) 696 | mutable nest : int; (* nesting level (String.length stack). *) 697 | mutable next_name : bool; (* [true] if next encode should `Name. *) 698 | mutable last_start : bool; (* [true] if last encode was [`As | `Os]. *) 699 | } 700 | 701 | let o_rem e = e.o_max - e.o_pos + 1 (* remaining bytes to write in [e.o]. *) 702 | 703 | let dst e s j l = 704 | (* set [e.o] with [s]. *) 705 | if j < 0 || l < 0 || j + l > Cstruct.length s then invalid_bounds j l; 706 | e.o <- s; 707 | e.o_pos <- j; 708 | e.o_max <- j + l - 1 709 | 710 | (* let partial k e = function `Await -> k e | v -> expect_await v *) 711 | let flush e ~stop = 712 | if stop then ( 713 | if e.o_pos <> 0 then e.dst (Cstruct.sub e.o 0 e.o_pos); 714 | e.dst Cstruct.empty) 715 | else e.dst (Cstruct.sub e.o 0 e.o_pos); 716 | e.o_pos <- 0 717 | 718 | let rec writeb b e = 719 | (* write byte [b] and [k]ontinue. *) 720 | if e.o_pos > e.o_max then ( 721 | flush e ~stop:false; 722 | writeb b e) 723 | else ( 724 | unsafe_set_byte e.o e.o_pos b; 725 | e.o_pos <- e.o_pos + 1) 726 | 727 | let rec writes s j l e = 728 | (* write [l] bytes from [s] starting at [j]. *) 729 | let rem = o_rem e in 730 | if rem >= l then ( 731 | unsafe_blit s j e.o e.o_pos l; 732 | e.o_pos <- e.o_pos + l) 733 | else ( 734 | unsafe_blit s j e.o e.o_pos rem; 735 | e.o_pos <- e.o_pos + rem; 736 | flush e ~stop:false; 737 | writes s (j + rem) (l - rem) e) 738 | 739 | let rec writebuf j l e = 740 | (* write [l] bytes from [e.buf] starting at [j]. *) 741 | let rem = o_rem e in 742 | if rem >= l then ( 743 | Cstruct.blit_from_bytes (Buffer.to_bytes e.buf) j e.o e.o_pos l; 744 | e.o_pos <- e.o_pos + l) 745 | else ( 746 | Cstruct.blit_from_bytes (Buffer.to_bytes e.buf) j e.o e.o_pos rem; 747 | e.o_pos <- e.o_pos + rem; 748 | flush e ~stop:false; 749 | writebuf (j + rem) (l - rem) e) 750 | 751 | let w_indent e = 752 | let rec loop indent e = 753 | let spaces e indent = 754 | let max = e.o_pos + indent - 1 in 755 | for j = e.o_pos to max do 756 | Cstruct.set_uint8 e.o j u_sp 757 | done; 758 | e.o_pos <- max + 1 759 | in 760 | let rem = o_rem e in 761 | if rem < indent then ( 762 | spaces e rem; 763 | flush e ~stop:false; 764 | loop (indent - rem) e) 765 | else spaces e indent 766 | in 767 | loop (e.nest * 2) e 768 | 769 | let w_json_string s e = 770 | (* escapes as mandated by the standard. *) 771 | let rec loop s j pos max e = 772 | if pos > max then if j > max then () else writes s j (pos - j) e 773 | else 774 | let next = pos + 1 in 775 | let escape esc = 776 | (* assert (String.length esc = 2 ). *) 777 | writes s j (pos - j) e; 778 | writes esc 0 2 e; 779 | loop s next next max e 780 | in 781 | match unsafe_byte s pos with 782 | | 0x22 -> escape "\\\"" 783 | | 0x5C -> escape "\\\\" 784 | | 0x0A -> escape "\\n" 785 | | c when c <= 0x1F -> 786 | let hex d = if d < 10 then 0x30 + d else 0x41 + (d - 10) in 787 | writes s j (pos - j) e; 788 | writes "\\u00" 0 4 e; 789 | writeb (hex (c lsr 4)) e; 790 | writeb (hex (c land 0xF)) e; 791 | loop s next next max e 792 | | _c -> loop s j next max e 793 | in 794 | writeb u_quot e; 795 | loop s 0 0 (String.length s - 1) e; 796 | writeb u_quot e 797 | 798 | let w_name n e = 799 | e.last_start <- false; 800 | e.next_name <- false; 801 | w_json_string n e; 802 | writeb u_colon e 803 | 804 | let w_value ~in_obj l e = 805 | match l with 806 | | `String s -> 807 | e.last_start <- false; 808 | e.next_name <- in_obj; 809 | w_json_string s e 810 | | `Bool b -> 811 | e.last_start <- false; 812 | e.next_name <- in_obj; 813 | if b then writes "true" 0 4 e else writes "false" 0 5 e 814 | | `Float f -> 815 | e.last_start <- false; 816 | e.next_name <- in_obj; 817 | Buffer.clear e.buf; 818 | Printf.bprintf e.buf "%.16g" f; 819 | writebuf 0 (Buffer.length e.buf) e 820 | | `Os -> 821 | e.last_start <- true; 822 | e.next_name <- true; 823 | e.nest <- e.nest + 1; 824 | e.stack <- `Os :: e.stack; 825 | writeb u_lbrace e 826 | | `As -> 827 | e.last_start <- true; 828 | e.next_name <- false; 829 | e.nest <- e.nest + 1; 830 | e.stack <- `As :: e.stack; 831 | writeb u_lbrack e 832 | | `Null -> 833 | e.last_start <- false; 834 | e.next_name <- in_obj; 835 | writes "null" 0 4 e 836 | | (`Oe | `Ae | `Name _) as l -> 837 | if in_obj then expect_mem_value l else expect_arr_value_ae l 838 | 839 | let w_lexeme e l = 840 | let epop e = 841 | e.last_start <- false; 842 | e.nest <- e.nest - 1; 843 | e.stack <- List.tl e.stack; 844 | match e.stack with 845 | | `Os :: _ -> e.next_name <- true 846 | | _ -> e.next_name <- false 847 | in 848 | match List.hd e.stack with 849 | | `Os -> ( 850 | if (* inside object. *) 851 | not e.next_name then w_value ~in_obj:true l e 852 | else 853 | match l with 854 | | `Name n -> 855 | let name n e = 856 | if e.minify then w_name n e 857 | else ( 858 | writeb u_nl e; 859 | w_indent e; 860 | w_name n e; 861 | writeb u_sp e) 862 | in 863 | if e.last_start then name n e 864 | else ( 865 | writeb u_comma e; 866 | name n e) 867 | | `Oe -> 868 | if e.minify || e.last_start then ( 869 | epop e; 870 | writeb u_rbrace e) 871 | else ( 872 | epop e; 873 | writeb u_nl e; 874 | w_indent e; 875 | writeb u_rbrace e) 876 | | _v -> expect_name_or_oe l) 877 | | `As -> ( 878 | (* inside array. *) 879 | match l with 880 | | `Ae -> 881 | if e.minify || e.last_start then ( 882 | epop e; 883 | writeb u_rbrack e) 884 | else ( 885 | epop e; 886 | writeb u_nl e; 887 | w_indent e; 888 | writeb u_rbrack e) 889 | | l -> 890 | let value l e = 891 | if e.minify then w_value ~in_obj:false l e 892 | else ( 893 | writeb u_nl e; 894 | w_indent e; 895 | w_value ~in_obj:false l e) 896 | in 897 | if e.last_start then value l e 898 | else ( 899 | writeb u_comma e; 900 | value l e)) 901 | 902 | let encode_ e = function 903 | | `Lexeme l -> if e.stack = [] then expect_end l else w_lexeme e l 904 | | `End as v -> 905 | if e.stack = [] then flush e ~stop:true 906 | else expect_lend (List.hd e.stack) v 907 | | `White w -> writes w 0 (String.length w) e 908 | | `Comment (`S, c) -> 909 | writes "//" 0 2 e; 910 | writes c 0 (String.length c) e; 911 | writeb u_nl e 912 | | `Comment (`M, c) -> 913 | writes "/*" 0 2 e; 914 | writes c 0 (String.length c) e; 915 | writes "*/" 0 2 e 916 | | `Await -> () 917 | 918 | (* let rec encode_loop e = e.k <- encode_ encode_loop; `Ok *) 919 | let encode_json e = function 920 | (* first [k] to start with [`Os] or [`As]. *) 921 | | `Lexeme ((`Null | `Bool _ | `Float _ | `String _ | `As | `Os) as l) -> 922 | w_value ~in_obj:false l e; 923 | e.encode_ <- true 924 | | (`End | `Lexeme _) as v -> expect_json v 925 | | (`White _ | `Comment _) as v -> 926 | encode_ e v; 927 | e.encode_ <- false 928 | | `Await -> () 929 | 930 | let encoder ?(minify = true) dst = 931 | let o = Cstruct.create io_buffer_size in 932 | let o_max = Cstruct.length o - 1 in 933 | if o_max = 0 then invalid_arg "buf's length is empty" 934 | else 935 | { 936 | dst; 937 | minify; 938 | o; 939 | o_pos = 0; 940 | o_max; 941 | buf = Buffer.create 30; 942 | encode_ = false; 943 | started = false; 944 | stack = []; 945 | nest = 0; 946 | next_name = false; 947 | last_start = false; 948 | } 949 | 950 | let encode e v = 951 | if (not e.started) || not e.encode_ then ( 952 | e.started <- true; 953 | encode_json e (v :> [ encode | uncut ]); 954 | `Ok) 955 | else ( 956 | encode_ e v; 957 | `Ok) 958 | 959 | let encoder_dst e = e.dst 960 | let encoder_minify e = e.minify 961 | 962 | (* Manual *) 963 | 964 | module Manual = struct 965 | let src d = Uutfe.Manual.src d.u 966 | let dst = dst 967 | let dst_rem = o_rem 968 | end 969 | 970 | (* Uncut *) 971 | 972 | module Uncut = struct 973 | let decode = decode_uncut 974 | let pp_decode = pp_decode 975 | let encode e v = encode e (v :> [ encode | uncut ]) 976 | end 977 | 978 | (*--------------------------------------------------------------------------- 979 | Copyright (c) 2012 The jsonm programmers 980 | 981 | Permission to use, copy, modify, and/or distribute this software for any 982 | purpose with or without fee is hereby granted, provided that the above 983 | copyright notice and this permission notice appear in all copies. 984 | 985 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 986 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 987 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 988 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 989 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 990 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 991 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 992 | ---------------------------------------------------------------------------*) 993 | -------------------------------------------------------------------------------- /test/geojson/dune: -------------------------------------------------------------------------------- 1 | (test 2 | (name test) 3 | (package geojson) 4 | (deps 5 | (source_tree files)) 6 | (libraries alcotest ezjsonm geojson)) 7 | -------------------------------------------------------------------------------- /test/geojson/files/valid/3d_featurecollection.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "geometry": { 7 | "type": "MultiPoint", 8 | "coordinates": [[130.1, 40.0, 33.3], [143.7, 22.5, 15.0]] 9 | }, 10 | "properties": { 11 | "name": "Dinagat Islands" 12 | } 13 | }, 14 | { 15 | "type": "Feature", 16 | "geometry": { 17 | "type": "MultiLineString", 18 | "coordinates": [ 19 | [ 20 | [170.0, 45.0, 60.2], [180.0, 45.0, 35.0] 21 | ], [ 22 | [-180.0, 45.0, 35.0], [-170.0, 45.0, 60.2] 23 | ] 24 | ] 25 | }, 26 | "properties": { 27 | "name": "Martha", 28 | "country": "Jamaica" 29 | } 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /test/geojson/files/valid/feature.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "geometry": { 4 | "type": "MultiPoint", 5 | "coordinates": [[125.1, 40.0], [155.9, 22.5]] 6 | }, 7 | "properties": { 8 | "name": "Dinagat Islands" 9 | }, 10 | "title": "Some Islands" 11 | } 12 | -------------------------------------------------------------------------------- /test/geojson/files/valid/featurecollection.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "geometry": { 7 | "type": "MultiPoint", 8 | "coordinates": [[125.1, 40.0], [155.9, 22.5]] 9 | }, 10 | "properties": { 11 | "name": "Dinagat Islands" 12 | } 13 | }, 14 | { 15 | "type": "Feature", 16 | "geometry": { 17 | "type": "MultiLineString", 18 | "coordinates": [ 19 | [ 20 | [170.0, 45.0], [180.0, 45.0] 21 | ], [ 22 | [-180.0, 45.0], [-170.0, 45.0] 23 | ] 24 | ] 25 | }, 26 | "properties": { 27 | "name": "Martha", 28 | "country": "Jamaica" 29 | } 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /test/geojson/files/valid/geo_with_bbox.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Polygon", 3 | "coordinates": [ 4 | [ 5 | [100.0, 0.0], 6 | [101.0, 0.0], 7 | [101.0, 1.0], 8 | [100.0, 1.0], 9 | [100.0, 0.0] 10 | ] 11 | ], 12 | "bbox": [100.0, 0.0, 101.0, 1.0] 13 | } -------------------------------------------------------------------------------- /test/geojson/files/valid/linestring.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "LineString", 3 | "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] 4 | } 5 | -------------------------------------------------------------------------------- /test/geojson/files/valid/multi_polygon.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "MultiPolygon", 3 | "coordinates": [ 4 | [ 5 | [ 6 | [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0] 7 | ] 8 | ], 9 | [ 10 | [ 11 | [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] 12 | ], 13 | [ 14 | [100.2, 0.2], [100.2, 0.8], [100.8, 0.8], [100.8, 0.2], [100.2, 0.2] 15 | ] 16 | ] 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/geojson/files/valid/multilinestring.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "MultiLineString", 3 | "coordinates": [ 4 | [ 5 | [170.0, 45.0], [180.0, 45.0] 6 | ], [ 7 | [-180.0, 45.0], [-170.0, 45.0] 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /test/geojson/files/valid/multipoint.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "MultiPoint", 3 | "coordinates": [ 4 | [100.0, 0.0], 5 | [101.0, 1.0] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /test/geojson/files/valid/point.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Point", 3 | "coordinates": [125.6, 10.1] 4 | } -------------------------------------------------------------------------------- /test/geojson/files/valid/polygon.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Polygon", 3 | "coordinates": [ 4 | [ 5 | [100.0, 0.0], 6 | [101.0, 0.0], 7 | [101.0, 1.0], 8 | [100.0, 1.0], 9 | [100.0, 0.0] 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /test/geojson/files/valid/prop1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dinagat Islands" 3 | } -------------------------------------------------------------------------------- /test/geojson/files/valid/prop2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Martha", 3 | "country": "Jamaica" 4 | } -------------------------------------------------------------------------------- /test/geojson/test.ml: -------------------------------------------------------------------------------- 1 | let read_file f = 2 | let ic = open_in f in 3 | let rec loop acc = 4 | try loop (input_line ic :: acc) 5 | with End_of_file -> 6 | close_in ic; 7 | List.rev acc 8 | in 9 | let lines = 10 | try loop [] 11 | with _ -> 12 | close_in ic; 13 | failwith "Something went wrong reading file" 14 | in 15 | String.concat "\n" lines 16 | 17 | module Ezjsonm_parser = struct 18 | type t = Ezjsonm.value 19 | 20 | let catch_err f v = 21 | try Ok (f v) with Ezjsonm.Parse_error (_, s) -> Error (`Msg s) 22 | 23 | let find = Ezjsonm.find_opt 24 | let to_string t = catch_err Ezjsonm.get_string t 25 | let string = Ezjsonm.string 26 | let to_float t = catch_err Ezjsonm.get_float t 27 | let float = Ezjsonm.float 28 | let to_int t = catch_err Ezjsonm.get_int t 29 | let int = Ezjsonm.int 30 | let to_list f t = catch_err (Ezjsonm.get_list f) t 31 | let list f t = Ezjsonm.list f t 32 | let to_array f t = Result.map Array.of_list @@ to_list f t 33 | let array f t = list f (Array.to_list t) 34 | let to_obj t = catch_err Ezjsonm.get_dict t 35 | let obj = Ezjsonm.dict 36 | let null = `Null 37 | let is_null = function `Null -> true | _ -> false 38 | end 39 | 40 | module Geojson = Geojson.Make (Ezjsonm_parser) 41 | 42 | let ezjsonm = 43 | Alcotest.of_pp (fun ppf t -> Fmt.pf ppf "%s" (Ezjsonm.value_to_string t)) 44 | 45 | let (msg : [ `Msg of string ] Alcotest.testable) = 46 | Alcotest.of_pp (fun ppf (`Msg m) -> Fmt.pf ppf "%s" m) 47 | 48 | let _get_all_props s = 49 | let json = Ezjsonm.value_from_string s in 50 | let geo = Geojson.of_json json in 51 | let open Geojson in 52 | match geo with 53 | | Ok v -> ( 54 | match geojson v with 55 | | Feature f -> Option.to_list @@ Geojson.Feature.properties f 56 | | FeatureCollection f -> 57 | let fs = Geojson.Feature.Collection.features f in 58 | List.filter_map Geojson.Feature.properties fs 59 | | _ -> []) 60 | | _ -> [] 61 | 62 | let geometry_accessor kind = 63 | Geojson.Accessor.(geojson >& geometry &> Geometry.geometry $> kind) 64 | 65 | let test_multi_line () = 66 | let s = read_file "files/valid/multilinestring.json" in 67 | let json = Ezjsonm.value_from_string s in 68 | let geo = Geojson.of_json json |> Result.get_ok in 69 | let coords = 70 | match 71 | Geojson.Accessor.(get (geometry_accessor Geometry.multilinestring)) geo 72 | with 73 | | Some m -> m 74 | | None -> assert false 75 | in 76 | let json' = Geojson.to_json geo in 77 | let t = 78 | Geojson.Geometry.( 79 | Array.map (fun v -> 80 | Array.map (fun l -> [| Position.lng l; Position.lat l |]) 81 | @@ LineString.coordinates v) 82 | @@ MultiLineString.lines coords) 83 | in 84 | 85 | Alcotest.(check (array @@ array @@ array (float 0.))) 86 | "same multi_line_string" 87 | [| 88 | [| [| 170.0; 45.0 |]; [| 180.0; 45.0 |] |]; 89 | [| [| -180.0; 45.0 |]; [| -170.0; 45.0 |] |]; 90 | |] 91 | t; 92 | Alcotest.(check ezjsonm) "same json" json json' 93 | 94 | let test_multi_point () = 95 | let s = read_file "files/valid/multipoint.json" in 96 | let json = Ezjsonm.value_from_string s in 97 | let geo = Geojson.of_json json |> Result.get_ok in 98 | let coords = 99 | match 100 | Geojson.Accessor.(get (geometry_accessor Geometry.multipoint)) geo 101 | with 102 | | Some m -> m 103 | | None -> assert false 104 | in 105 | let json' = Geojson.to_json geo in 106 | let t = 107 | Geojson.Geometry.( 108 | Array.map (fun l -> [| Position.lng l; Position.lat l |]) 109 | @@ MultiPoint.coordinates coords) 110 | in 111 | 112 | Alcotest.(check (array @@ array (float 0.))) 113 | "same point" 114 | [| [| 100.0; 0.0 |]; [| 101.0; 1.0 |] |] 115 | t; 116 | Alcotest.(check ezjsonm) "same json" json json' 117 | 118 | let test_point () = 119 | let s = read_file "files/valid/point.json" in 120 | let json = Ezjsonm.value_from_string s in 121 | let geo = Geojson.of_json json |> Result.get_ok in 122 | let coords = 123 | match Geojson.Accessor.(get (geometry_accessor Geometry.point)) geo with 124 | | Some m -> m 125 | | None -> assert false 126 | in 127 | let json' = Geojson.to_json geo in 128 | let open Geojson.Geometry in 129 | let pos = Point.position coords in 130 | let p = [| Position.lng pos; Position.lat pos |] in 131 | 132 | Alcotest.(check (array (float 0.))) "same point" [| 125.6; 10.1 |] p; 133 | Alcotest.(check ezjsonm) "same json" json json' 134 | 135 | let test_linestring () = 136 | let s = read_file "files/valid/linestring.json" in 137 | let json = Ezjsonm.value_from_string s in 138 | let geo = Geojson.of_json json |> Result.get_ok in 139 | let coords = 140 | match 141 | Geojson.Accessor.(get (geometry_accessor Geometry.linestring)) geo 142 | with 143 | | Some m -> m 144 | | None -> assert false 145 | in 146 | let json' = Geojson.to_json geo in 147 | let l = 148 | Geojson.Geometry.( 149 | Array.map (fun l -> [| Position.lng l; Position.lat l |]) 150 | @@ LineString.coordinates coords) 151 | in 152 | 153 | Alcotest.(check (array @@ array (float 0.))) 154 | "same linestring" 155 | [| [| 100.0; 0. |]; [| 101.0; 1.0 |] |] 156 | l; 157 | Alcotest.(check ezjsonm) "same json" json json' 158 | 159 | let test_polygon () = 160 | let s = read_file "files/valid/polygon.json" in 161 | let json = Ezjsonm.value_from_string s in 162 | let geo = Geojson.of_json json |> Result.get_ok in 163 | let coords = 164 | match Geojson.Accessor.(get (geometry_accessor Geometry.polygon)) geo with 165 | | Some m -> m 166 | | None -> assert false 167 | in 168 | 169 | let json' = Geojson.to_json geo in 170 | let t = 171 | Geojson.Geometry.( 172 | Array.map (fun v -> 173 | Array.map (fun l -> [| Position.lng l; Position.lat l |]) 174 | @@ LineString.coordinates v) 175 | @@ Polygon.rings coords) 176 | in 177 | 178 | Alcotest.(check (array @@ array @@ array (float 0.))) 179 | "same polygon" 180 | [| 181 | [| 182 | [| 100.0; 0.0 |]; 183 | [| 101.0; 0.0 |]; 184 | [| 101.0; 1.0 |]; 185 | [| 100.0; 1.0 |]; 186 | [| 100.0; 0.0 |]; 187 | |]; 188 | |] 189 | t; 190 | Alcotest.(check ezjsonm) "same json" json json' 191 | 192 | let test_multi_polygon () = 193 | let s = read_file "files/valid/multi_polygon.json" in 194 | let json = Ezjsonm.value_from_string s in 195 | let geo = Geojson.of_json json |> Result.get_ok in 196 | let coords = 197 | match 198 | Geojson.Accessor.(get (geometry_accessor Geometry.multipolygon)) geo 199 | with 200 | | Some m -> m 201 | | None -> assert false 202 | in 203 | 204 | let json' = Geojson.to_json geo in 205 | let t = 206 | Geojson.Geometry.( 207 | Array.map (fun v -> 208 | Array.map (fun n -> 209 | Array.map (fun l -> [| Position.lng l; Position.lat l |]) 210 | @@ LineString.coordinates n) 211 | @@ Polygon.rings v) 212 | @@ MultiPolygon.polygons coords) 213 | in 214 | 215 | Alcotest.(check (array @@ array @@ array @@ array (float 0.))) 216 | "same multi-polygon" 217 | [| 218 | [| 219 | [| 220 | [| 102.0; 2.0 |]; 221 | [| 103.0; 2.0 |]; 222 | [| 103.0; 3.0 |]; 223 | [| 102.0; 3.0 |]; 224 | [| 102.0; 2.0 |]; 225 | |]; 226 | |]; 227 | [| 228 | [| 229 | [| 100.0; 0.0 |]; 230 | [| 101.0; 0.0 |]; 231 | [| 101.0; 1.0 |]; 232 | [| 100.0; 1.0 |]; 233 | [| 100.0; 0.0 |]; 234 | |]; 235 | [| 236 | [| 100.2; 0.2 |]; 237 | [| 100.2; 0.8 |]; 238 | [| 100.8; 0.8 |]; 239 | [| 100.8; 0.2 |]; 240 | [| 100.2; 0.2 |]; 241 | |]; 242 | |]; 243 | |] 244 | t; 245 | Alcotest.(check ezjsonm) "same json" json json' 246 | 247 | let test_feature () = 248 | let s = read_file "files/valid/feature.json" in 249 | let json = Ezjsonm.value_from_string s in 250 | let feature = Geojson.of_json json in 251 | let prop_from_file = 252 | Ezjsonm.value_from_string @@ read_file "files/valid/prop1.json" 253 | in 254 | let property = match _get_all_props s with [ x ] -> x | _ -> assert false in 255 | let f, coord, foreign_members = 256 | match feature with 257 | | Ok v -> ( 258 | match Geojson.geojson v with 259 | | Feature t -> ( 260 | let foreign_members = Geojson.Feature.foreign_members t in 261 | match 262 | Geojson.Feature.geometry t 263 | |> Option.get 264 | |> Geojson.Accessor.( 265 | get (Geometry.geometry >& Geometry.multipoint)) 266 | with 267 | | Some p -> (v, p, foreign_members) 268 | | _ -> assert false) 269 | | _ -> assert false) 270 | | _ -> assert false 271 | in 272 | 273 | let json' = Geojson.to_json f in 274 | let t = 275 | Geojson.Geometry.( 276 | Array.map (fun l -> [| Position.lng l; Position.lat l |]) 277 | @@ MultiPoint.coordinates coord) 278 | in 279 | Alcotest.(check (array @@ array (float 0.))) 280 | "same point" 281 | [| [| 125.1; 40.0 |]; [| 155.9; 22.5 |] |] 282 | t; 283 | Alcotest.(check (list (pair string ezjsonm))) 284 | "same string" 285 | [ ("title", `String "Some Islands") ] 286 | foreign_members; 287 | Alcotest.(check ezjsonm) "same json" prop_from_file property; 288 | Alcotest.(check ezjsonm) "same json" json json' 289 | 290 | let test_feature_collection () = 291 | let s = read_file "files/valid/featurecollection.json" in 292 | let json = Ezjsonm.value_from_string s in 293 | let feature_collection = 294 | match Geojson.of_json json with Ok v -> v | _ -> assert false 295 | in 296 | let prop_from_file1 = 297 | Ezjsonm.value_from_string @@ read_file "files/valid/prop1.json" 298 | in 299 | let prop_from_file2 = 300 | Ezjsonm.value_from_string @@ read_file "files/valid/prop2.json" 301 | in 302 | let prop1, prop2 = 303 | match _get_all_props s with [ x; y ] -> (x, y) | _ -> assert false 304 | in 305 | let c1, c2 = 306 | match Geojson.geojson feature_collection with 307 | | FeatureCollection fc -> ( 308 | match Geojson.Feature.Collection.features fc with 309 | | [ x; y ] -> ( 310 | match 311 | ( Geojson.Feature.geometry x 312 | |> Option.map Geojson.Geometry.geometry, 313 | Geojson.Feature.geometry y 314 | |> Option.map Geojson.Geometry.geometry ) 315 | with 316 | | Some (MultiPoint a), Some (MultiLineString b) -> (a, b) 317 | | _, _ -> assert false) 318 | | _ -> assert false) 319 | | _ -> assert false 320 | in 321 | let json' = Geojson.to_json feature_collection in 322 | let mp = 323 | Geojson.Geometry.( 324 | Array.map (fun l -> [| Position.lng l; Position.lat l |]) 325 | @@ MultiPoint.coordinates c1) 326 | in 327 | let ml = 328 | Geojson.Geometry.( 329 | Array.map (fun v -> 330 | Array.map (fun l -> [| Position.lng l; Position.lat l |]) 331 | @@ LineString.coordinates v) 332 | @@ MultiLineString.lines c2) 333 | in 334 | 335 | Alcotest.(check (array @@ array (float 0.))) 336 | "same point" 337 | [| [| 125.1; 40.0 |]; [| 155.9; 22.5 |] |] 338 | mp; 339 | Alcotest.(check (array @@ array @@ array (float 0.))) 340 | "same multi_line_string" 341 | [| 342 | [| [| 170.0; 45.0 |]; [| 180.0; 45.0 |] |]; 343 | [| [| -180.0; 45.0 |]; [| -170.0; 45.0 |] |]; 344 | |] 345 | ml; 346 | Alcotest.(check ezjsonm) "same json" prop_from_file1 prop1; 347 | Alcotest.(check ezjsonm) "same json" prop_from_file2 prop2; 348 | Alcotest.(check ezjsonm) "same json" json json' 349 | 350 | let test_bbox () = 351 | let s = read_file "files/valid/geo_with_bbox.json" in 352 | let json = Ezjsonm.value_from_string s in 353 | let geojson_obj = Geojson.of_json json in 354 | let bbox = 355 | match geojson_obj with 356 | | Ok v -> ( match Geojson.bbox v with Some x -> x | _ -> assert false) 357 | | _ -> assert false 358 | in 359 | let json' = Geojson.to_json @@ Result.get_ok geojson_obj in 360 | 361 | Alcotest.(check (array (float 0.))) 362 | "same bbox" 363 | [| 100.0; 0.0; 101.0; 1.0 |] 364 | bbox; 365 | Alcotest.(check ezjsonm) "same json" json json' 366 | 367 | let test_3d_feature_collection () = 368 | let s = read_file "files/valid/3d_featurecollection.json" in 369 | let json = Ezjsonm.value_from_string s in 370 | let feature_collection = 371 | match Geojson.of_json json with Ok v -> v | _ -> assert false 372 | in 373 | let prop_from_file1 = 374 | Ezjsonm.value_from_string @@ read_file "files/valid/prop1.json" 375 | in 376 | let prop_from_file2 = 377 | Ezjsonm.value_from_string @@ read_file "files/valid/prop2.json" 378 | in 379 | let prop1, prop2 = 380 | match _get_all_props s with [ x; y ] -> (x, y) | _ -> assert false 381 | in 382 | let c1, c2 = 383 | match Geojson.geojson feature_collection with 384 | | FeatureCollection fc -> ( 385 | match Geojson.Feature.Collection.features fc with 386 | | [ x; y ] -> ( 387 | match 388 | ( Geojson.Feature.geometry x 389 | |> Option.map Geojson.Geometry.geometry, 390 | Geojson.Feature.geometry y 391 | |> Option.map Geojson.Geometry.geometry ) 392 | with 393 | | Some (MultiPoint a), Some (MultiLineString b) -> (a, b) 394 | | _, _ -> assert false) 395 | | _ -> assert false) 396 | | _ -> assert false 397 | in 398 | let json' = Geojson.to_json feature_collection in 399 | let mp = 400 | Geojson.Geometry.( 401 | Array.map (fun l -> 402 | [| 403 | Position.lng l; Position.lat l; Option.get @@ Position.altitude l; 404 | |]) 405 | @@ MultiPoint.coordinates c1) 406 | in 407 | let ml = 408 | Geojson.Geometry.( 409 | Array.map (fun v -> 410 | Array.map (fun l -> 411 | [| 412 | Position.lng l; 413 | Position.lat l; 414 | Option.get @@ Position.altitude l; 415 | |]) 416 | @@ LineString.coordinates v) 417 | @@ MultiLineString.lines c2) 418 | in 419 | 420 | Alcotest.(check (array @@ array (float 0.))) 421 | "same point" 422 | [| [| 130.1; 40.0; 33.3 |]; [| 143.7; 22.5; 15.0 |] |] 423 | mp; 424 | Alcotest.(check (array @@ array @@ array (float 0.))) 425 | "same multi_line_string" 426 | [| 427 | [| [| 170.0; 45.0; 60.2 |]; [| 180.0; 45.0; 35.0 |] |]; 428 | [| [| -180.0; 45.0; 35.0 |]; [| -170.0; 45.0; 60.2 |] |]; 429 | |] 430 | ml; 431 | Alcotest.(check ezjsonm) "same json" prop_from_file1 prop1; 432 | Alcotest.(check ezjsonm) "same json" prop_from_file2 prop2; 433 | Alcotest.(check ezjsonm) "same json" json json' 434 | 435 | let geojson = 436 | Alcotest.testable 437 | (fun ppf p -> Fmt.pf ppf "%s" (Ezjsonm.value_to_string (Geojson.to_json p))) 438 | (fun a b -> Geojson.to_json a = Geojson.to_json b) 439 | 440 | let test_random () = 441 | let open Geojson.Random in 442 | let r = 443 | FC 444 | [ 445 | { properties = None; geometry = Point }; 446 | { properties = None; geometry = LineString 2 }; 447 | { properties = None; geometry = Polygon 2 }; 448 | { 449 | properties = Some (`O [ ("name", `String "abcd") ]); 450 | geometry = MultiPolygon (3, 3); 451 | }; 452 | ] 453 | in 454 | let expect = random ~f:(fun () -> Random.float 100.) r in 455 | let actual = Geojson.to_json expect |> Geojson.of_json in 456 | Alcotest.(check (result geojson msg)) "same geojson" (Ok expect) actual 457 | 458 | let () = 459 | Alcotest.run "geojson" 460 | [ 461 | ( "geometry", 462 | [ 463 | Alcotest.test_case "multi-line" `Quick test_multi_line; 464 | Alcotest.test_case "multi-point" `Quick test_multi_point; 465 | Alcotest.test_case "point" `Quick test_point; 466 | Alcotest.test_case "linestring" `Quick test_linestring; 467 | Alcotest.test_case "polygon" `Quick test_polygon; 468 | Alcotest.test_case "multi_polygon" `Quick test_multi_polygon; 469 | ] ); 470 | ("feature", [ Alcotest.test_case "feature" `Quick test_feature ]); 471 | ( "feature-collection", 472 | [ 473 | Alcotest.test_case "feature-collection" `Quick test_feature_collection; 474 | ] ); 475 | ("random", [ Alcotest.test_case "simple-random" `Quick test_random ]); 476 | ("bbox", [ Alcotest.test_case "bbox" `Quick test_bbox ]); 477 | ("3D", [ Alcotest.test_case "3D" `Quick test_3d_feature_collection ]); 478 | ] 479 | -------------------------------------------------------------------------------- /test/geojsonm/expect/dune: -------------------------------------------------------------------------------- 1 | (tests 2 | (names test test_iters) 3 | (package geojsone) 4 | (deps 5 | (source_tree input)) 6 | (libraries eio_main geojsone geojsone.eio)) 7 | -------------------------------------------------------------------------------- /test/geojsonm/expect/input/simple.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[1.49886,43.09493],[1.50194,43.09495],[1.50487,43.09542],[1.50774,43.09653],[1.51541,43.09749],[1.51878,43.09719],[1.52344,43.09858],[1.5239,43.10001],[1.52728,43.10058],[1.52862,43.10136],[1.53029,43.10047],[1.53139,43.09638],[1.53073,43.09502],[1.52915,43.09403],[1.52906,43.09177],[1.52817,43.08966],[1.52867,43.08782],[1.53017,43.08665],[1.53063,43.08391],[1.53336,43.08186],[1.528,43.07899],[1.52657,43.07627],[1.52457,43.07388],[1.5244,43.07221],[1.52517,43.07046],[1.52672,43.0689],[1.52764,43.06459],[1.52944,43.06266],[1.53187,43.06096],[1.53532,43.05982],[1.54835,43.07031],[1.54656,43.07094],[1.5471,43.07336],[1.55135,43.07628],[1.55274,43.07872],[1.55414,43.07954],[1.55531,43.07858],[1.55383,43.07648],[1.55628,43.0764],[1.56261,43.0796],[1.56186,43.08062],[1.56405,43.08355],[1.56301,43.08779],[1.56615,43.09109],[1.57137,43.09144],[1.57617,43.09111],[1.57821,43.09032],[1.58135,43.08976],[1.58284,43.08864],[1.58627,43.08758],[1.59249,43.08623],[1.59508,43.08683],[1.59888,43.08663],[1.59964,43.08524],[1.59897,43.08416],[1.5992,43.08087],[1.598,43.07866],[1.59861,43.07438],[1.59591,43.07342],[1.60246,43.07058],[1.6038,43.07028],[1.61314,43.07088],[1.61493,43.06927],[1.61659,43.0691],[1.62389,43.06942],[1.62796,43.06837],[1.62903,43.07058],[1.62815,43.07305],[1.62936,43.07427],[1.63009,43.07963],[1.6313,43.08171],[1.63566,43.0865],[1.63559,43.08786],[1.63832,43.0899],[1.63843,43.09215],[1.64021,43.09185],[1.64155,43.09299],[1.64682,43.09396],[1.64894,43.09154],[1.65376,43.09301],[1.65439,43.09225],[1.66009,43.09309],[1.66273,43.09284],[1.66493,43.09448],[1.66998,43.09234],[1.67203,43.09181],[1.68038,43.09097],[1.68235,43.09021],[1.6854,43.09333],[1.69332,43.09306],[1.69358,43.09199],[1.69193,43.09049],[1.69,43.08742],[1.68891,43.08642],[1.68959,43.08494],[1.69276,43.08325],[1.69337,43.082],[1.69289,43.08063],[1.69519,43.07935],[1.69669,43.07719],[1.69768,43.07477],[1.70005,43.07231],[1.70511,43.07464],[1.70886,43.07488],[1.71229,43.07449],[1.71092,43.07097],[1.71175,43.06846],[1.71043,43.06599],[1.71119,43.06395],[1.7107,43.06238],[1.71141,43.05964],[1.70751,43.05665],[1.70607,43.05371],[1.70799,43.0521],[1.71453,43.04894],[1.71821,43.04897],[1.71822,43.0478],[1.71947,43.04645],[1.72131,43.04642],[1.72329,43.0449],[1.72809,43.04312],[1.72957,43.04187],[1.73253,43.04135],[1.73446,43.04333],[1.7377,43.04542],[1.74275,43.04973],[1.74468,43.05334],[1.75072,43.06331],[1.75594,43.0619],[1.76385,43.05811],[1.7673,43.05683],[1.77117,43.05582],[1.77162,43.05411],[1.77058,43.05243],[1.77161,43.04771],[1.77088,43.04614],[1.7672,43.04008],[1.76593,43.03864],[1.76656,43.03608],[1.76553,43.03554],[1.75905,43.03361],[1.75659,43.03346],[1.75204,43.03435],[1.75033,43.0332],[1.75116,43.03132],[1.75056,43.02852],[1.75114,43.02538],[1.75105,43.02299],[1.74862,43.0206],[1.74612,43.01919],[1.74558,43.01729],[1.74756,43.01609],[1.74907,43.01173],[1.74811,43.00938],[1.74598,43.00806],[1.74368,43.00486],[1.74264,43.00436],[1.74273,43.00116],[1.74409,43.00073],[1.74509,42.99888],[1.74849,42.99692],[1.74746,42.99453],[1.75087,42.99166],[1.75357,42.99015],[1.75335,42.98862],[1.75624,42.98746],[1.7564,42.98341],[1.75724,42.98022],[1.75452,42.98084],[1.74996,42.98261],[1.74574,42.9835],[1.74458,42.98336],[1.74219,42.98177],[1.74227,42.98015],[1.74002,42.97941],[1.73708,42.97773],[1.73463,42.97763],[1.73348,42.97541],[1.73104,42.975],[1.72387,42.97569],[1.72175,42.97243],[1.71917,42.97308],[1.7142,42.97379],[1.71239,42.97084],[1.71247,42.9695],[1.71634,42.9679],[1.71619,42.9652],[1.71823,42.96385],[1.71701,42.96241],[1.71874,42.96039],[1.71617,42.96052],[1.71479,42.95884],[1.71252,42.95824],[1.70955,42.95611],[1.70648,42.95573],[1.70538,42.95488],[1.70073,42.9548],[1.7051,42.95343],[1.70834,42.95291],[1.71125,42.95158],[1.71347,42.94933],[1.71531,42.94845],[1.71774,42.94607],[1.71899,42.94317],[1.72114,42.94227],[1.72538,42.9359],[1.72655,42.93524],[1.72584,42.9328],[1.72436,42.93207],[1.72186,42.93219],[1.72268,42.92982],[1.72011,42.92845],[1.71922,42.9262],[1.72189,42.92224],[1.72106,42.9203],[1.72018,42.91625],[1.71875,42.91377],[1.72203,42.91522],[1.72323,42.91708],[1.72453,42.91749],[1.72685,42.91711],[1.72889,42.91473],[1.7305,42.91388],[1.73527,42.91278],[1.74378,42.91459],[1.74715,42.91378],[1.75125,42.9139],[1.7538,42.91553],[1.75596,42.91451],[1.75834,42.91493],[1.76222,42.91366],[1.76508,42.91442],[1.76754,42.91403],[1.77072,42.91414],[1.77368,42.91235],[1.77168,42.90919],[1.77129,42.90635],[1.77031,42.90496],[1.76824,42.90354],[1.76857,42.90135],[1.76694,42.89962],[1.76658,42.89809],[1.76224,42.89775],[1.75959,42.89239],[1.76061,42.8896],[1.76314,42.88754],[1.76254,42.88565],[1.75974,42.88406],[1.7562,42.88279],[1.75504,42.88174],[1.74883,42.87896],[1.74708,42.87711],[1.73649,42.87085],[1.73284,42.86911],[1.72863,42.86827],[1.72747,42.86764],[1.72614,42.8657],[1.72616,42.8644],[1.72464,42.86281],[1.72149,42.86108],[1.71936,42.8581],[1.71622,42.85543],[1.71624,42.854],[1.71851,42.85256],[1.71717,42.85165],[1.71701,42.84999],[1.71602,42.8494],[1.71611,42.84787],[1.71467,42.84493],[1.71579,42.84274],[1.71546,42.83958],[1.7221,42.83597],[1.72378,42.83323],[1.72666,42.83352],[1.73039,42.83215],[1.73205,42.83063],[1.73525,42.83042],[1.73991,42.82896],[1.7423,42.82777],[1.74403,42.82606],[1.74832,42.82509],[1.75608,42.81945],[1.7612,42.82048],[1.76407,42.82063],[1.76922,42.81921],[1.77332,42.81888],[1.77755,42.81799],[1.78115,42.81805],[1.78306,42.81743],[1.78481,42.81915],[1.78603,42.81957],[1.78775,42.81832],[1.79154,42.81779],[1.79748,42.818],[1.80101,42.81883],[1.80334,42.81857],[1.80495,42.81646],[1.80661,42.8153],[1.80838,42.81508],[1.81039,42.81564],[1.81553,42.8148],[1.81725,42.81373],[1.82204,42.81281],[1.82685,42.81468],[1.82917,42.816],[1.83282,42.81673],[1.83441,42.81638],[1.83655,42.81761],[1.83984,42.81739],[1.84473,42.818],[1.84997,42.81805],[1.8545,42.81921],[1.85938,42.82081],[1.85994,42.81959],[1.86215,42.81762],[1.86589,42.81728],[1.86816,42.81553],[1.87123,42.81455],[1.87399,42.81411],[1.87795,42.81413],[1.88163,42.81246],[1.88414,42.81245],[1.88578,42.81167],[1.88807,42.81147],[1.89394,42.80965],[1.89609,42.80867],[1.89775,42.80715],[1.89856,42.80431],[1.89839,42.80269],[1.90096,42.80077],[1.90612,42.79841],[1.90761,42.79566],[1.90731,42.79436],[1.90823,42.79292],[1.9134,42.78893],[1.91482,42.78593],[1.91963,42.78081],[1.92134,42.77992],[1.92338,42.77769],[1.92884,42.77495],[1.9235,42.77151],[1.9217,42.76996],[1.91788,42.76992],[1.91324,42.76918],[1.90938,42.76921],[1.90818,42.76767],[1.90899,42.76525],[1.91139,42.76251],[1.91361,42.75788],[1.91979,42.75719],[1.92207,42.75517],[1.92982,42.7552],[1.93179,42.75345],[1.93094,42.75115],[1.9334,42.75031],[1.9367,42.74996],[1.93861,42.74794],[1.94129,42.74688],[1.94644,42.74576],[1.94717,42.74437],[1.94689,42.74172],[1.94775,42.73915],[1.9488,42.73799],[1.95163,42.73669],[1.95949,42.73686],[1.96249,42.73771],[1.96365,42.73696],[1.9681,42.7363],[1.97025,42.73535],[1.97585,42.73668],[1.98178,42.73602],[1.98452,42.73675],[1.98686,42.73519],[1.99144,42.7348],[1.99724,42.73594],[2.00009,42.73595],[2.00323,42.73457],[2.00384,42.73348],[2.00793,42.73412],[2.0117,42.73648],[2.01448,42.73973],[2.01998,42.74002],[2.02346,42.73903],[2.02543,42.73899],[2.02681,42.73999],[2.03078,42.74148],[2.03142,42.74652],[2.03472,42.74739],[2.03918,42.74614],[2.04163,42.74633],[2.04327,42.74737],[2.04784,42.74904],[2.04978,42.75139],[2.05234,42.75373],[2.05641,42.756],[2.05769,42.75614],[2.0632,42.75507],[2.06613,42.75412],[2.06997,42.75432],[2.07181,42.75364],[2.07505,42.7537],[2.07791,42.75312],[2.07976,42.75214],[2.0814,42.75228],[2.0839,42.75161],[2.08629,42.7518],[2.08594,42.7494],[2.08501,42.74851],[2.08638,42.74594],[2.08639,42.7441],[2.08749,42.74122],[2.08744,42.73901],[2.08892,42.73717],[2.09217,42.73466],[2.09358,42.73448],[2.09638,42.73314],[2.09883,42.73328],[2.10084,42.73148],[2.10206,42.73148],[2.10471,42.7292],[2.10806,42.72722],[2.11155,42.72678],[2.11381,42.7245],[2.11723,42.72381],[2.11925,42.72153],[2.12084,42.7214],[2.12072,42.71995],[2.1233,42.71857],[2.12506,42.71925],[2.12678,42.71683],[2.13155,42.71494],[2.13356,42.71471],[2.13595,42.71324],[2.13955,42.7123],[2.14078,42.71046],[2.14061,42.70582],[2.14415,42.70448],[2.14666,42.70138],[2.14788,42.70097],[2.15203,42.70156],[2.15466,42.70135],[2.15971,42.70221],[2.16211,42.69951],[2.16528,42.69821],[2.16786,42.69618],[2.17133,42.69111],[2.1714,42.6895],[2.17587,42.6828],[2.176,42.68145],[2.17308,42.67797],[2.17242,42.67576],[2.16748,42.67338],[2.16532,42.66585],[2.16605,42.66392],[2.15965,42.66435],[2.15386,42.66224],[2.15129,42.66196],[2.14898,42.66277],[2.14337,42.6637],[2.13262,42.66836],[2.1301,42.67115],[2.12784,42.67227],[2.12388,42.67168],[2.11798,42.66847],[2.11201,42.66643],[2.10897,42.66386],[2.10678,42.66525],[2.10428,42.66506],[2.09824,42.66402],[2.09611,42.66402],[2.09189,42.66567],[2.08659,42.66544],[2.0853,42.66678],[2.08378,42.66669],[2.0811,42.66465],[2.07634,42.66477],[2.07049,42.66639],[2.06976,42.66495],[2.0677,42.66412],[2.06458,42.66426],[2.06088,42.66168],[2.05452,42.66306],[2.04702,42.66359],[2.0443,42.66016],[2.04327,42.6575],[2.02763,42.65251],[2.02256,42.65339],[2.01879,42.65342],[2.01725,42.65472],[2.01353,42.65611],[2.01225,42.65695],[2.01248,42.65944],[2.007,42.65861],[2.00455,42.65936],[2.00369,42.66022],[2.00051,42.66156],[1.99856,42.66133],[1.99692,42.66007],[1.99736,42.65719],[1.997,42.65611],[1.9953,42.65506],[1.99392,42.65268],[1.99257,42.65232],[1.99271,42.65083],[1.99077,42.64664],[1.98793,42.64479],[1.98635,42.64086],[1.98668,42.64032],[1.98528,42.63722],[1.98257,42.63347],[1.97995,42.63166],[1.97978,42.63039],[1.97759,42.62927],[1.97583,42.62693],[1.97597,42.62481],[1.97726,42.62355],[1.97636,42.62107],[1.97413,42.61941],[1.97176,42.61692],[1.96829,42.61637],[1.96525,42.61631],[1.96311,42.61712],[1.96043,42.61693],[1.9581,42.61746],[1.95602,42.61871],[1.95401,42.61889],[1.95293,42.61816],[1.95012,42.61774],[1.94722,42.61468],[1.94363,42.61314],[1.94451,42.61003],[1.94379,42.60855],[1.93887,42.60686],[1.93497,42.60492],[1.93339,42.60482],[1.92991,42.60611],[1.92666,42.60943],[1.92477,42.60879],[1.92228,42.60932],[1.91467,42.60763],[1.9115,42.60761],[1.90729,42.60863],[1.90368,42.61154],[1.90018,42.6149],[1.89748,42.60905],[1.89305,42.60633],[1.89361,42.60476],[1.89546,42.60233],[1.89413,42.60016],[1.89318,42.59616],[1.89127,42.59331],[1.88709,42.59055],[1.88385,42.58987],[1.88019,42.59007],[1.87423,42.59],[1.8735,42.58942],[1.87317,42.58541],[1.87449,42.58141],[1.87015,42.58143],[1.86938,42.57999],[1.86432,42.57899],[1.8617,42.58135],[1.86038,42.58401],[1.85724,42.58278],[1.85617,42.5816],[1.8539,42.58129],[1.85365,42.58232],[1.85157,42.58433],[1.84821,42.58372],[1.84707,42.58305],[1.84176,42.58303],[1.83907,42.58441],[1.83706,42.58454],[1.83131,42.58149],[1.82473,42.58124],[1.82157,42.57956],[1.81768,42.57954],[1.81599,42.57818],[1.80634,42.57458],[1.801,42.5724],[1.79874,42.57239],[1.79601,42.57148],[1.79039,42.574],[1.78766,42.573],[1.78613,42.57362],[1.78661,42.57431],[1.78441,42.57587],[1.78313,42.58012],[1.78094,42.5826],[1.77741,42.58205],[1.77316,42.58068],[1.76805,42.58052],[1.76074,42.58092],[1.75859,42.58195],[1.75475,42.58292],[1.75286,42.58264],[1.74961,42.58469],[1.74846,42.58468],[1.74197,42.58775],[1.73539,42.58791],[1.73375,42.58853],[1.73173,42.58806],[1.72826,42.58841],[1.72612,42.58969],[1.72631,42.60031],[1.72899,42.60123],[1.73127,42.60399],[1.73114,42.60569],[1.7319,42.60741],[1.73519,42.60851],[1.73663,42.61077],[1.73796,42.61131],[1.73727,42.61424],[1.73622,42.61503],[1.73607,42.61715],[1.73353,42.61579],[1.72951,42.61482],[1.72817,42.61563],[1.72628,42.61548],[1.72281,42.61636],[1.7202,42.61504],[1.71411,42.61461],[1.7102,42.61633],[1.70705,42.6187],[1.70063,42.62119],[1.69818,42.62274],[1.69204,42.62195],[1.69031,42.62325],[1.68671,42.6239],[1.6836,42.62486],[1.68056,42.62481],[1.67838,42.6225],[1.67577,42.6223],[1.67346,42.62081],[1.67205,42.62125],[1.66687,42.62153],[1.6644,42.61904],[1.66203,42.61948],[1.66086,42.62037],[1.65689,42.62115],[1.65553,42.62255],[1.65496,42.62447],[1.65372,42.62631],[1.63883,42.62842],[1.63716,42.6303],[1.63072,42.62779],[1.62788,42.62633],[1.62232,42.62728],[1.61953,42.62583],[1.61691,42.62662],[1.61149,42.62667],[1.60934,42.62855],[1.60762,42.62832],[1.60357,42.62585],[1.60205,42.62548],[1.59974,42.62619],[1.59527,42.63179],[1.59313,42.63313],[1.58568,42.63357],[1.5842,42.6341],[1.58236,42.63589],[1.57978,42.63753],[1.57921,42.63902],[1.57944,42.64117],[1.57845,42.64229],[1.57599,42.64336],[1.57589,42.6462],[1.57489,42.64803],[1.57192,42.64796],[1.56879,42.64843],[1.56707,42.64973],[1.56607,42.65148],[1.56239,42.65348],[1.5605,42.65333],[1.55832,42.65215],[1.55562,42.6533],[1.5533,42.65368],[1.54925,42.65578],[1.54396,42.6542],[1.54093,42.65284],[1.53832,42.65255],[1.53548,42.65009],[1.53384,42.64968],[1.53083,42.65124],[1.52864,42.65144],[1.52638,42.65084],[1.52458,42.64956],[1.52215,42.64918],[1.52143,42.64743],[1.51891,42.64548],[1.51262,42.64615],[1.5058,42.6452],[1.50288,42.64544],[1.50061,42.64515],[1.49925,42.6474],[1.49758,42.64873],[1.49375,42.65321],[1.49106,42.65297],[1.48814,42.65194],[1.48474,42.65206],[1.4801,42.65139],[1.47877,42.65165],[1.47874,42.6494],[1.47809,42.64675],[1.47702,42.64524],[1.47796,42.6435],[1.47691,42.64074],[1.4757,42.63957],[1.46991,42.63565],[1.46993,42.63385],[1.46834,42.63082],[1.47098,42.62918],[1.47142,42.62824],[1.47053,42.62626],[1.47105,42.62467],[1.47382,42.62141],[1.47428,42.61939],[1.47573,42.61656],[1.47727,42.61497],[1.47656,42.61302],[1.47423,42.61089],[1.47042,42.60869],[1.46946,42.60689],[1.46777,42.60638],[1.46478,42.60685],[1.46339,42.60562],[1.46175,42.60562],[1.45981,42.60475],[1.45704,42.60229],[1.4504,42.6026],[1.44782,42.60379],[1.44282,42.60366],[1.43783,42.60321],[1.43567,42.60581],[1.4334,42.60692],[1.43302,42.60831],[1.43085,42.61136],[1.42923,42.61269],[1.42933,42.61444],[1.43056,42.61733],[1.4281,42.61845],[1.42728,42.62095],[1.42379,42.62201],[1.42206,42.62397],[1.4207,42.62473],[1.42019,42.62607],[1.41996,42.62949],[1.41872,42.63069],[1.41974,42.63223],[1.41807,42.63348],[1.41652,42.63568],[1.41593,42.63764],[1.41617,42.63913],[1.4173,42.64],[1.41691,42.64184],[1.41444,42.64407],[1.41366,42.64766],[1.41466,42.6496],[1.41359,42.65145],[1.41441,42.65402],[1.41366,42.65552],[1.40837,42.65759],[1.40537,42.65721],[1.40315,42.65887],[1.40377,42.66002],[1.40196,42.66188],[1.40132,42.66457],[1.39664,42.66805],[1.39424,42.66839],[1.38986,42.66804],[1.38778,42.66837],[1.38773,42.67099],[1.38888,42.67289],[1.38891,42.67427],[1.38703,42.67741],[1.38876,42.67991],[1.38752,42.68151],[1.3896,42.68508],[1.38933,42.68679],[1.3866,42.68952],[1.38469,42.69067],[1.381,42.69181],[1.37737,42.69452],[1.37248,42.69475],[1.36955,42.69563],[1.3651,42.69451],[1.3617,42.69776],[1.35693,42.69867],[1.35317,42.70035],[1.35124,42.70266],[1.35103,42.7046],[1.35235,42.70623],[1.35195,42.70835],[1.35303,42.70971],[1.35288,42.71128],[1.35528,42.71355],[1.35801,42.71493],[1.35803,42.71694],[1.35738,42.71941],[1.35914,42.72407],[1.36029,42.72485],[1.36344,42.72559],[1.36479,42.72898],[1.36627,42.73147],[1.37193,42.73291],[1.37452,42.73136],[1.37759,42.73017],[1.37978,42.73086],[1.38295,42.73112],[1.38521,42.73182],[1.38703,42.73159],[1.39139,42.73465],[1.38908,42.74134],[1.39178,42.74478],[1.39149,42.74766],[1.39682,42.7541],[1.39696,42.75689],[1.39955,42.75939],[1.40014,42.76461],[1.40384,42.76644],[1.40497,42.76829],[1.40506,42.77028],[1.40595,42.77208],[1.40408,42.77464],[1.40287,42.77775],[1.40431,42.77954],[1.40491,42.78333],[1.40721,42.78595],[1.40696,42.79027],[1.40786,42.79078],[1.40635,42.79328],[1.40737,42.79518],[1.40458,42.79727],[1.4064,42.80246],[1.40847,42.80298],[1.41046,42.80493],[1.41424,42.80929],[1.41397,42.81126],[1.41435,42.81419],[1.41602,42.81654],[1.41521,42.8178],[1.4158,42.81933],[1.41852,42.82142],[1.4211,42.82491],[1.42772,42.82699],[1.43373,42.82997],[1.43552,42.8297],[1.43752,42.83036],[1.43996,42.83032],[1.4405,42.83178],[1.44291,42.83445],[1.4417,42.83705],[1.44016,42.83865],[1.44043,42.84065],[1.43945,42.84428],[1.43942,42.84639],[1.44185,42.8479],[1.44326,42.85128],[1.44991,42.85741],[1.45112,42.85814],[1.4501,42.8611],[1.45084,42.86458],[1.45031,42.86722],[1.45033,42.87096],[1.45225,42.8726],[1.45275,42.87593],[1.45364,42.87873],[1.45471,42.87986],[1.45293,42.88106],[1.45192,42.8834],[1.44927,42.88418],[1.44838,42.88621],[1.44579,42.88802],[1.45102,42.88992],[1.45154,42.89199],[1.45114,42.8942],[1.45148,42.89617],[1.4531,42.89926],[1.45759,42.90563],[1.45494,42.90632],[1.45376,42.9074],[1.44947,42.90822],[1.44645,42.90937],[1.44458,42.91165],[1.44268,42.91186],[1.438,42.9139],[1.43731,42.91488],[1.43413,42.91503],[1.42954,42.91436],[1.42695,42.91515],[1.42224,42.91498],[1.41285,42.91665],[1.41063,42.91781],[1.41093,42.92146],[1.41213,42.92268],[1.41198,42.92965],[1.41113,42.93352],[1.41104,42.93856],[1.41332,42.94245],[1.41632,42.94558],[1.42123,42.94917],[1.42446,42.95059],[1.42656,42.95394],[1.42723,42.95778],[1.43062,42.96032],[1.43398,42.96084],[1.43643,42.96181],[1.44481,42.96237],[1.45186,42.96207],[1.45674,42.96391],[1.45845,42.96397],[1.46125,42.96538],[1.46072,42.96804],[1.46185,42.97016],[1.46308,42.97053],[1.46749,42.97034],[1.47232,42.97114],[1.47567,42.97346],[1.4806,42.97584],[1.48615,42.97714],[1.48502,42.97943],[1.48524,42.98177],[1.48704,42.98408],[1.48898,42.98535],[1.4945,42.98499],[1.49615,42.98554],[1.49551,42.99143],[1.49765,42.99221],[1.49974,42.99169],[1.50341,42.99248],[1.50771,42.99211],[1.50919,42.99109],[1.51213,42.99112],[1.51268,42.9917],[1.51431,42.99729],[1.51879,42.99723],[1.52023,42.99922],[1.52051,43.0017],[1.519,43.00448],[1.51744,43.00632],[1.51178,43.00852],[1.5114,43.00964],[1.51414,43.00967],[1.51722,43.00888],[1.5222,43.00864],[1.52095,43.00994],[1.51897,43.01587],[1.52028,43.01853],[1.51887,43.01865],[1.52074,43.02146],[1.51851,43.02234],[1.51668,43.02201],[1.51567,43.02382],[1.51356,43.0256],[1.51281,43.02734],[1.51133,43.02715],[1.50719,43.02974],[1.50686,43.03054],[1.50392,43.03098],[1.49948,43.03251],[1.50031,43.03567],[1.50127,43.0367],[1.50401,43.03768],[1.50465,43.03975],[1.50683,43.04265],[1.49874,43.04628],[1.49644,43.04751],[1.49629,43.05004],[1.45429,43.0684],[1.45395,43.07078],[1.45576,43.07281],[1.4578,43.07594],[1.45913,43.07703],[1.463,43.07755],[1.46623,43.0787],[1.46866,43.0816],[1.46917,43.08336],[1.47078,43.08257],[1.47352,43.08466],[1.47585,43.08476],[1.48344,43.08748],[1.48582,43.08794],[1.49246,43.08754],[1.49682,43.09217],[1.49779,43.09236],[1.49806,43.09443],[1.49886,43.09493]]]},"properties":{"code":"09001","nom":"Foix"}}]} -------------------------------------------------------------------------------- /test/geojsonm/expect/test.expected: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[1.49886,43.09493],[1.50194,43.09495],[1.50487,43.09542],[1.50774,43.09653],[1.51541,43.09749],[1.51878,43.09719],[1.52344,43.09858],[1.5239,43.10001],[1.52728,43.10058],[1.52862,43.10136],[1.53029,43.10047],[1.53139,43.09638],[1.53073,43.09502],[1.52915,43.09403],[1.52906,43.09177],[1.52817,43.08966],[1.52867,43.08782],[1.53017,43.08665],[1.53063,43.08391],[1.53336,43.08186],[1.528,43.07899],[1.52657,43.07627],[1.52457,43.07388],[1.5244,43.07221],[1.52517,43.07046],[1.52672,43.0689],[1.52764,43.06459],[1.52944,43.06266],[1.53187,43.06096],[1.53532,43.05982],[1.54835,43.07031],[1.54656,43.07094],[1.5471,43.07336],[1.55135,43.07628],[1.55274,43.07872],[1.55414,43.07954],[1.55531,43.07858],[1.55383,43.07648],[1.55628,43.0764],[1.56261,43.0796],[1.56186,43.08062],[1.56405,43.08355],[1.56301,43.08779],[1.56615,43.09109],[1.57137,43.09144],[1.57617,43.09111],[1.57821,43.09032],[1.58135,43.08976],[1.58284,43.08864],[1.58627,43.08758],[1.59249,43.08623],[1.59508,43.08683],[1.59888,43.08663],[1.59964,43.08524],[1.59897,43.08416],[1.5992,43.08087],[1.598,43.07866],[1.59861,43.07438],[1.59591,43.07342],[1.60246,43.07058],[1.6038,43.07028],[1.61314,43.07088],[1.61493,43.06927],[1.61659,43.0691],[1.62389,43.06942],[1.62796,43.06837],[1.62903,43.07058],[1.62815,43.07305],[1.62936,43.07427],[1.63009,43.07963],[1.6313,43.08171],[1.63566,43.0865],[1.63559,43.08786],[1.63832,43.0899],[1.63843,43.09215],[1.64021,43.09185],[1.64155,43.09299],[1.64682,43.09396],[1.64894,43.09154],[1.65376,43.09301],[1.65439,43.09225],[1.66009,43.09309],[1.66273,43.09284],[1.66493,43.09448],[1.66998,43.09234],[1.67203,43.09181],[1.68038,43.09097],[1.68235,43.09021],[1.6854,43.09333],[1.69332,43.09306],[1.69358,43.09199],[1.69193,43.09049],[1.69,43.08742],[1.68891,43.08642],[1.68959,43.08494],[1.69276,43.08325],[1.69337,43.082],[1.69289,43.08063],[1.69519,43.07935],[1.69669,43.07719],[1.69768,43.07477],[1.70005,43.07231],[1.70511,43.07464],[1.70886,43.07488],[1.71229,43.07449],[1.71092,43.07097],[1.71175,43.06846],[1.71043,43.06599],[1.71119,43.06395],[1.7107,43.06238],[1.71141,43.05964],[1.70751,43.05665],[1.70607,43.05371],[1.70799,43.0521],[1.71453,43.04894],[1.71821,43.04897],[1.71822,43.0478],[1.71947,43.04645],[1.72131,43.04642],[1.72329,43.0449],[1.72809,43.04312],[1.72957,43.04187],[1.73253,43.04135],[1.73446,43.04333],[1.7377,43.04542],[1.74275,43.04973],[1.74468,43.05334],[1.75072,43.06331],[1.75594,43.0619],[1.76385,43.05811],[1.7673,43.05683],[1.77117,43.05582],[1.77162,43.05411],[1.77058,43.05243],[1.77161,43.04771],[1.77088,43.04614],[1.7672,43.04008],[1.76593,43.03864],[1.76656,43.03608],[1.76553,43.03554],[1.75905,43.03361],[1.75659,43.03346],[1.75204,43.03435],[1.75033,43.0332],[1.75116,43.03132],[1.75056,43.02852],[1.75114,43.02538],[1.75105,43.02299],[1.74862,43.0206],[1.74612,43.01919],[1.74558,43.01729],[1.74756,43.01609],[1.74907,43.01173],[1.74811,43.00938],[1.74598,43.00806],[1.74368,43.00486],[1.74264,43.00436],[1.74273,43.00116],[1.74409,43.00073],[1.74509,42.99888],[1.74849,42.99692],[1.74746,42.99453],[1.75087,42.99166],[1.75357,42.99015],[1.75335,42.98862],[1.75624,42.98746],[1.7564,42.98341],[1.75724,42.98022],[1.75452,42.98084],[1.74996,42.98261],[1.74574,42.9835],[1.74458,42.98336],[1.74219,42.98177],[1.74227,42.98015],[1.74002,42.97941],[1.73708,42.97773],[1.73463,42.97763],[1.73348,42.97541],[1.73104,42.975],[1.72387,42.97569],[1.72175,42.97243],[1.71917,42.97308],[1.7142,42.97379],[1.71239,42.97084],[1.71247,42.9695],[1.71634,42.9679],[1.71619,42.9652],[1.71823,42.96385],[1.71701,42.96241],[1.71874,42.96039],[1.71617,42.96052],[1.71479,42.95884],[1.71252,42.95824],[1.70955,42.95611],[1.70648,42.95573],[1.70538,42.95488],[1.70073,42.9548],[1.7051,42.95343],[1.70834,42.95291],[1.71125,42.95158],[1.71347,42.94933],[1.71531,42.94845],[1.71774,42.94607],[1.71899,42.94317],[1.72114,42.94227],[1.72538,42.9359],[1.72655,42.93524],[1.72584,42.9328],[1.72436,42.93207],[1.72186,42.93219],[1.72268,42.92982],[1.72011,42.92845],[1.71922,42.9262],[1.72189,42.92224],[1.72106,42.9203],[1.72018,42.91625],[1.71875,42.91377],[1.72203,42.91522],[1.72323,42.91708],[1.72453,42.91749],[1.72685,42.91711],[1.72889,42.91473],[1.7305,42.91388],[1.73527,42.91278],[1.74378,42.91459],[1.74715,42.91378],[1.75125,42.9139],[1.7538,42.91553],[1.75596,42.91451],[1.75834,42.91493],[1.76222,42.91366],[1.76508,42.91442],[1.76754,42.91403],[1.77072,42.91414],[1.77368,42.91235],[1.77168,42.90919],[1.77129,42.90635],[1.77031,42.90496],[1.76824,42.90354],[1.76857,42.90135],[1.76694,42.89962],[1.76658,42.89809],[1.76224,42.89775],[1.75959,42.89239],[1.76061,42.8896],[1.76314,42.88754],[1.76254,42.88565],[1.75974,42.88406],[1.7562,42.88279],[1.75504,42.88174],[1.74883,42.87896],[1.74708,42.87711],[1.73649,42.87085],[1.73284,42.86911],[1.72863,42.86827],[1.72747,42.86764],[1.72614,42.8657],[1.72616,42.8644],[1.72464,42.86281],[1.72149,42.86108],[1.71936,42.8581],[1.71622,42.85543],[1.71624,42.854],[1.71851,42.85256],[1.71717,42.85165],[1.71701,42.84999],[1.71602,42.8494],[1.71611,42.84787],[1.71467,42.84493],[1.71579,42.84274],[1.71546,42.83958],[1.7221,42.83597],[1.72378,42.83323],[1.72666,42.83352],[1.73039,42.83215],[1.73205,42.83063],[1.73525,42.83042],[1.73991,42.82896],[1.7423,42.82777],[1.74403,42.82606],[1.74832,42.82509],[1.75608,42.81945],[1.7612,42.82048],[1.76407,42.82063],[1.76922,42.81921],[1.77332,42.81888],[1.77755,42.81799],[1.78115,42.81805],[1.78306,42.81743],[1.78481,42.81915],[1.78603,42.81957],[1.78775,42.81832],[1.79154,42.81779],[1.79748,42.818],[1.80101,42.81883],[1.80334,42.81857],[1.80495,42.81646],[1.80661,42.8153],[1.80838,42.81508],[1.81039,42.81564],[1.81553,42.8148],[1.81725,42.81373],[1.82204,42.81281],[1.82685,42.81468],[1.82917,42.816],[1.83282,42.81673],[1.83441,42.81638],[1.83655,42.81761],[1.83984,42.81739],[1.84473,42.818],[1.84997,42.81805],[1.8545,42.81921],[1.85938,42.82081],[1.85994,42.81959],[1.86215,42.81762],[1.86589,42.81728],[1.86816,42.81553],[1.87123,42.81455],[1.87399,42.81411],[1.87795,42.81413],[1.88163,42.81246],[1.88414,42.81245],[1.88578,42.81167],[1.88807,42.81147],[1.89394,42.80965],[1.89609,42.80867],[1.89775,42.80715],[1.89856,42.80431],[1.89839,42.80269],[1.90096,42.80077],[1.90612,42.79841],[1.90761,42.79566],[1.90731,42.79436],[1.90823,42.79292],[1.9134,42.78893],[1.91482,42.78593],[1.91963,42.78081],[1.92134,42.77992],[1.92338,42.77769],[1.92884,42.77495],[1.9235,42.77151],[1.9217,42.76996],[1.91788,42.76992],[1.91324,42.76918],[1.90938,42.76921],[1.90818,42.76767],[1.90899,42.76525],[1.91139,42.76251],[1.91361,42.75788],[1.91979,42.75719],[1.92207,42.75517],[1.92982,42.7552],[1.93179,42.75345],[1.93094,42.75115],[1.9334,42.75031],[1.9367,42.74996],[1.93861,42.74794],[1.94129,42.74688],[1.94644,42.74576],[1.94717,42.74437],[1.94689,42.74172],[1.94775,42.73915],[1.9488,42.73799],[1.95163,42.73669],[1.95949,42.73686],[1.96249,42.73771],[1.96365,42.73696],[1.9681,42.7363],[1.97025,42.73535],[1.97585,42.73668],[1.98178,42.73602],[1.98452,42.73675],[1.98686,42.73519],[1.99144,42.7348],[1.99724,42.73594],[2.00009,42.73595],[2.00323,42.73457],[2.00384,42.73348],[2.00793,42.73412],[2.0117,42.73648],[2.01448,42.73973],[2.01998,42.74002],[2.02346,42.73903],[2.02543,42.73899],[2.02681,42.73999],[2.03078,42.74148],[2.03142,42.74652],[2.03472,42.74739],[2.03918,42.74614],[2.04163,42.74633],[2.04327,42.74737],[2.04784,42.74904],[2.04978,42.75139],[2.05234,42.75373],[2.05641,42.756],[2.05769,42.75614],[2.0632,42.75507],[2.06613,42.75412],[2.06997,42.75432],[2.07181,42.75364],[2.07505,42.7537],[2.07791,42.75312],[2.07976,42.75214],[2.0814,42.75228],[2.0839,42.75161],[2.08629,42.7518],[2.08594,42.7494],[2.08501,42.74851],[2.08638,42.74594],[2.08639,42.7441],[2.08749,42.74122],[2.08744,42.73901],[2.08892,42.73717],[2.09217,42.73466],[2.09358,42.73448],[2.09638,42.73314],[2.09883,42.73328],[2.10084,42.73148],[2.10206,42.73148],[2.10471,42.7292],[2.10806,42.72722],[2.11155,42.72678],[2.11381,42.7245],[2.11723,42.72381],[2.11925,42.72153],[2.12084,42.7214],[2.12072,42.71995],[2.1233,42.71857],[2.12506,42.71925],[2.12678,42.71683],[2.13155,42.71494],[2.13356,42.71471],[2.13595,42.71324],[2.13955,42.7123],[2.14078,42.71046],[2.14061,42.70582],[2.14415,42.70448],[2.14666,42.70138],[2.14788,42.70097],[2.15203,42.70156],[2.15466,42.70135],[2.15971,42.70221],[2.16211,42.69951],[2.16528,42.69821],[2.16786,42.69618],[2.17133,42.69111],[2.1714,42.6895],[2.17587,42.6828],[2.176,42.68145],[2.17308,42.67797],[2.17242,42.67576],[2.16748,42.67338],[2.16532,42.66585],[2.16605,42.66392],[2.15965,42.66435],[2.15386,42.66224],[2.15129,42.66196],[2.14898,42.66277],[2.14337,42.6637],[2.13262,42.66836],[2.1301,42.67115],[2.12784,42.67227],[2.12388,42.67168],[2.11798,42.66847],[2.11201,42.66643],[2.10897,42.66386],[2.10678,42.66525],[2.10428,42.66506],[2.09824,42.66402],[2.09611,42.66402],[2.09189,42.66567],[2.08659,42.66544],[2.0853,42.66678],[2.08378,42.66669],[2.0811,42.66465],[2.07634,42.66477],[2.07049,42.66639],[2.06976,42.66495],[2.0677,42.66412],[2.06458,42.66426],[2.06088,42.66168],[2.05452,42.66306],[2.04702,42.66359],[2.0443,42.66016],[2.04327,42.6575],[2.02763,42.65251],[2.02256,42.65339],[2.01879,42.65342],[2.01725,42.65472],[2.01353,42.65611],[2.01225,42.65695],[2.01248,42.65944],[2.007,42.65861],[2.00455,42.65936],[2.00369,42.66022],[2.00051,42.66156],[1.99856,42.66133],[1.99692,42.66007],[1.99736,42.65719],[1.997,42.65611],[1.9953,42.65506],[1.99392,42.65268],[1.99257,42.65232],[1.99271,42.65083],[1.99077,42.64664],[1.98793,42.64479],[1.98635,42.64086],[1.98668,42.64032],[1.98528,42.63722],[1.98257,42.63347],[1.97995,42.63166],[1.97978,42.63039],[1.97759,42.62927],[1.97583,42.62693],[1.97597,42.62481],[1.97726,42.62355],[1.97636,42.62107],[1.97413,42.61941],[1.97176,42.61692],[1.96829,42.61637],[1.96525,42.61631],[1.96311,42.61712],[1.96043,42.61693],[1.9581,42.61746],[1.95602,42.61871],[1.95401,42.61889],[1.95293,42.61816],[1.95012,42.61774],[1.94722,42.61468],[1.94363,42.61314],[1.94451,42.61003],[1.94379,42.60855],[1.93887,42.60686],[1.93497,42.60492],[1.93339,42.60482],[1.92991,42.60611],[1.92666,42.60943],[1.92477,42.60879],[1.92228,42.60932],[1.91467,42.60763],[1.9115,42.60761],[1.90729,42.60863],[1.90368,42.61154],[1.90018,42.6149],[1.89748,42.60905],[1.89305,42.60633],[1.89361,42.60476],[1.89546,42.60233],[1.89413,42.60016],[1.89318,42.59616],[1.89127,42.59331],[1.88709,42.59055],[1.88385,42.58987],[1.88019,42.59007],[1.87423,42.59],[1.8735,42.58942],[1.87317,42.58541],[1.87449,42.58141],[1.87015,42.58143],[1.86938,42.57999],[1.86432,42.57899],[1.8617,42.58135],[1.86038,42.58401],[1.85724,42.58278],[1.85617,42.5816],[1.8539,42.58129],[1.85365,42.58232],[1.85157,42.58433],[1.84821,42.58372],[1.84707,42.58305],[1.84176,42.58303],[1.83907,42.58441],[1.83706,42.58454],[1.83131,42.58149],[1.82473,42.58124],[1.82157,42.57956],[1.81768,42.57954],[1.81599,42.57818],[1.80634,42.57458],[1.801,42.5724],[1.79874,42.57239],[1.79601,42.57148],[1.79039,42.574],[1.78766,42.573],[1.78613,42.57362],[1.78661,42.57431],[1.78441,42.57587],[1.78313,42.58012],[1.78094,42.5826],[1.77741,42.58205],[1.77316,42.58068],[1.76805,42.58052],[1.76074,42.58092],[1.75859,42.58195],[1.75475,42.58292],[1.75286,42.58264],[1.74961,42.58469],[1.74846,42.58468],[1.74197,42.58775],[1.73539,42.58791],[1.73375,42.58853],[1.73173,42.58806],[1.72826,42.58841],[1.72612,42.58969],[1.72631,42.60031],[1.72899,42.60123],[1.73127,42.60399],[1.73114,42.60569],[1.7319,42.60741],[1.73519,42.60851],[1.73663,42.61077],[1.73796,42.61131],[1.73727,42.61424],[1.73622,42.61503],[1.73607,42.61715],[1.73353,42.61579],[1.72951,42.61482],[1.72817,42.61563],[1.72628,42.61548],[1.72281,42.61636],[1.7202,42.61504],[1.71411,42.61461],[1.7102,42.61633],[1.70705,42.6187],[1.70063,42.62119],[1.69818,42.62274],[1.69204,42.62195],[1.69031,42.62325],[1.68671,42.6239],[1.6836,42.62486],[1.68056,42.62481],[1.67838,42.6225],[1.67577,42.6223],[1.67346,42.62081],[1.67205,42.62125],[1.66687,42.62153],[1.6644,42.61904],[1.66203,42.61948],[1.66086,42.62037],[1.65689,42.62115],[1.65553,42.62255],[1.65496,42.62447],[1.65372,42.62631],[1.63883,42.62842],[1.63716,42.6303],[1.63072,42.62779],[1.62788,42.62633],[1.62232,42.62728],[1.61953,42.62583],[1.61691,42.62662],[1.61149,42.62667],[1.60934,42.62855],[1.60762,42.62832],[1.60357,42.62585],[1.60205,42.62548],[1.59974,42.62619],[1.59527,42.63179],[1.59313,42.63313],[1.58568,42.63357],[1.5842,42.6341],[1.58236,42.63589],[1.57978,42.63753],[1.57921,42.63902],[1.57944,42.64117],[1.57845,42.64229],[1.57599,42.64336],[1.57589,42.6462],[1.57489,42.64803],[1.57192,42.64796],[1.56879,42.64843],[1.56707,42.64973],[1.56607,42.65148],[1.56239,42.65348],[1.5605,42.65333],[1.55832,42.65215],[1.55562,42.6533],[1.5533,42.65368],[1.54925,42.65578],[1.54396,42.6542],[1.54093,42.65284],[1.53832,42.65255],[1.53548,42.65009],[1.53384,42.64968],[1.53083,42.65124],[1.52864,42.65144],[1.52638,42.65084],[1.52458,42.64956],[1.52215,42.64918],[1.52143,42.64743],[1.51891,42.64548],[1.51262,42.64615],[1.5058,42.6452],[1.50288,42.64544],[1.50061,42.64515],[1.49925,42.6474],[1.49758,42.64873],[1.49375,42.65321],[1.49106,42.65297],[1.48814,42.65194],[1.48474,42.65206],[1.4801,42.65139],[1.47877,42.65165],[1.47874,42.6494],[1.47809,42.64675],[1.47702,42.64524],[1.47796,42.6435],[1.47691,42.64074],[1.4757,42.63957],[1.46991,42.63565],[1.46993,42.63385],[1.46834,42.63082],[1.47098,42.62918],[1.47142,42.62824],[1.47053,42.62626],[1.47105,42.62467],[1.47382,42.62141],[1.47428,42.61939],[1.47573,42.61656],[1.47727,42.61497],[1.47656,42.61302],[1.47423,42.61089],[1.47042,42.60869],[1.46946,42.60689],[1.46777,42.60638],[1.46478,42.60685],[1.46339,42.60562],[1.46175,42.60562],[1.45981,42.60475],[1.45704,42.60229],[1.4504,42.6026],[1.44782,42.60379],[1.44282,42.60366],[1.43783,42.60321],[1.43567,42.60581],[1.4334,42.60692],[1.43302,42.60831],[1.43085,42.61136],[1.42923,42.61269],[1.42933,42.61444],[1.43056,42.61733],[1.4281,42.61845],[1.42728,42.62095],[1.42379,42.62201],[1.42206,42.62397],[1.4207,42.62473],[1.42019,42.62607],[1.41996,42.62949],[1.41872,42.63069],[1.41974,42.63223],[1.41807,42.63348],[1.41652,42.63568],[1.41593,42.63764],[1.41617,42.63913],[1.4173,42.64],[1.41691,42.64184],[1.41444,42.64407],[1.41366,42.64766],[1.41466,42.6496],[1.41359,42.65145],[1.41441,42.65402],[1.41366,42.65552],[1.40837,42.65759],[1.40537,42.65721],[1.40315,42.65887],[1.40377,42.66002],[1.40196,42.66188],[1.40132,42.66457],[1.39664,42.66805],[1.39424,42.66839],[1.38986,42.66804],[1.38778,42.66837],[1.38773,42.67099],[1.38888,42.67289],[1.38891,42.67427],[1.38703,42.67741],[1.38876,42.67991],[1.38752,42.68151],[1.3896,42.68508],[1.38933,42.68679],[1.3866,42.68952],[1.38469,42.69067],[1.381,42.69181],[1.37737,42.69452],[1.37248,42.69475],[1.36955,42.69563],[1.3651,42.69451],[1.3617,42.69776],[1.35693,42.69867],[1.35317,42.70035],[1.35124,42.70266],[1.35103,42.7046],[1.35235,42.70623],[1.35195,42.70835],[1.35303,42.70971],[1.35288,42.71128],[1.35528,42.71355],[1.35801,42.71493],[1.35803,42.71694],[1.35738,42.71941],[1.35914,42.72407],[1.36029,42.72485],[1.36344,42.72559],[1.36479,42.72898],[1.36627,42.73147],[1.37193,42.73291],[1.37452,42.73136],[1.37759,42.73017],[1.37978,42.73086],[1.38295,42.73112],[1.38521,42.73182],[1.38703,42.73159],[1.39139,42.73465],[1.38908,42.74134],[1.39178,42.74478],[1.39149,42.74766],[1.39682,42.7541],[1.39696,42.75689],[1.39955,42.75939],[1.40014,42.76461],[1.40384,42.76644],[1.40497,42.76829],[1.40506,42.77028],[1.40595,42.77208],[1.40408,42.77464],[1.40287,42.77775],[1.40431,42.77954],[1.40491,42.78333],[1.40721,42.78595],[1.40696,42.79027],[1.40786,42.79078],[1.40635,42.79328],[1.40737,42.79518],[1.40458,42.79727],[1.4064,42.80246],[1.40847,42.80298],[1.41046,42.80493],[1.41424,42.80929],[1.41397,42.81126],[1.41435,42.81419],[1.41602,42.81654],[1.41521,42.8178],[1.4158,42.81933],[1.41852,42.82142],[1.4211,42.82491],[1.42772,42.82699],[1.43373,42.82997],[1.43552,42.8297],[1.43752,42.83036],[1.43996,42.83032],[1.4405,42.83178],[1.44291,42.83445],[1.4417,42.83705],[1.44016,42.83865],[1.44043,42.84065],[1.43945,42.84428],[1.43942,42.84639],[1.44185,42.8479],[1.44326,42.85128],[1.44991,42.85741],[1.45112,42.85814],[1.4501,42.8611],[1.45084,42.86458],[1.45031,42.86722],[1.45033,42.87096],[1.45225,42.8726],[1.45275,42.87593],[1.45364,42.87873],[1.45471,42.87986],[1.45293,42.88106],[1.45192,42.8834],[1.44927,42.88418],[1.44838,42.88621],[1.44579,42.88802],[1.45102,42.88992],[1.45154,42.89199],[1.45114,42.8942],[1.45148,42.89617],[1.4531,42.89926],[1.45759,42.90563],[1.45494,42.90632],[1.45376,42.9074],[1.44947,42.90822],[1.44645,42.90937],[1.44458,42.91165],[1.44268,42.91186],[1.438,42.9139],[1.43731,42.91488],[1.43413,42.91503],[1.42954,42.91436],[1.42695,42.91515],[1.42224,42.91498],[1.41285,42.91665],[1.41063,42.91781],[1.41093,42.92146],[1.41213,42.92268],[1.41198,42.92965],[1.41113,42.93352],[1.41104,42.93856],[1.41332,42.94245],[1.41632,42.94558],[1.42123,42.94917],[1.42446,42.95059],[1.42656,42.95394],[1.42723,42.95778],[1.43062,42.96032],[1.43398,42.96084],[1.43643,42.96181],[1.44481,42.96237],[1.45186,42.96207],[1.45674,42.96391],[1.45845,42.96397],[1.46125,42.96538],[1.46072,42.96804],[1.46185,42.97016],[1.46308,42.97053],[1.46749,42.97034],[1.47232,42.97114],[1.47567,42.97346],[1.4806,42.97584],[1.48615,42.97714],[1.48502,42.97943],[1.48524,42.98177],[1.48704,42.98408],[1.48898,42.98535],[1.4945,42.98499],[1.49615,42.98554],[1.49551,42.99143],[1.49765,42.99221],[1.49974,42.99169],[1.50341,42.99248],[1.50771,42.99211],[1.50919,42.99109],[1.51213,42.99112],[1.51268,42.9917],[1.51431,42.99729],[1.51879,42.99723],[1.52023,42.99922],[1.52051,43.0017],[1.519,43.00448],[1.51744,43.00632],[1.51178,43.00852],[1.5114,43.00964],[1.51414,43.00967],[1.51722,43.00888],[1.5222,43.00864],[1.52095,43.00994],[1.51897,43.01587],[1.52028,43.01853],[1.51887,43.01865],[1.52074,43.02146],[1.51851,43.02234],[1.51668,43.02201],[1.51567,43.02382],[1.51356,43.0256],[1.51281,43.02734],[1.51133,43.02715],[1.50719,43.02974],[1.50686,43.03054],[1.50392,43.03098],[1.49948,43.03251],[1.50031,43.03567],[1.50127,43.0367],[1.50401,43.03768],[1.50465,43.03975],[1.50683,43.04265],[1.49874,43.04628],[1.49644,43.04751],[1.49629,43.05004],[1.45429,43.0684],[1.45395,43.07078],[1.45576,43.07281],[1.4578,43.07594],[1.45913,43.07703],[1.463,43.07755],[1.46623,43.0787],[1.46866,43.0816],[1.46917,43.08336],[1.47078,43.08257],[1.47352,43.08466],[1.47585,43.08476],[1.48344,43.08748],[1.48582,43.08794],[1.49246,43.08754],[1.49682,43.09217],[1.49779,43.09236],[1.49806,43.09443],[1.49886,43.09493]]]},"properties":{"code":"09001","nom":"FOIX"}}]} 2 | 3 | {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[]},"properties":{"code":"09001","nom":"Foix"}}]} 4 | 5 | Places: Foix -------------------------------------------------------------------------------- /test/geojsonm/expect/test.ml: -------------------------------------------------------------------------------- 1 | let capitalise_nom obj = 2 | let rec capitalise_nom acc = function 3 | | [] -> List.rev acc 4 | | ("nom", `String nom) :: xs -> 5 | capitalise_nom (("nom", `String (String.uppercase_ascii nom)) :: acc) xs 6 | | x :: xs -> capitalise_nom (x :: acc) xs 7 | in 8 | match obj with `O assoc -> `O (capitalise_nom [] assoc) | x -> x 9 | 10 | let remove_all_coords t = 11 | let open Geojsone in 12 | match (G.Geometry.geometry t, G.Geometry.foreign_members t) with 13 | | G.Geometry.Polygon _, fm -> 14 | G.Geometry.(v ~foreign_members:fm (Polygon (Polygon.v [||]))) 15 | | _ -> t 16 | 17 | let get_string_exn = function `String s -> s | _ -> failwith "err" 18 | 19 | let get_name = function 20 | | `O assoc -> List.assoc "nom" assoc |> get_string_exn 21 | | _ -> failwith "err" 22 | 23 | let src_of_flow flow = 24 | let buff = Cstruct.create 2048 in 25 | fun () -> 26 | let got = Eio.Flow.(single_read flow buff) in 27 | let t = Cstruct.sub buff 0 got in 28 | t 29 | 30 | let with_src cwd f func = 31 | Eio.Path.(with_open_in (cwd / f)) @@ fun ic -> func @@ src_of_flow ic 32 | 33 | let buffer_to_dst buf bs = 34 | Eio.Flow.(copy (cstruct_source [ bs ]) (Eio.Flow.buffer_sink buf)) 35 | 36 | let () = 37 | Eio_main.run @@ fun env -> 38 | let dst = Buffer.create 1000 in 39 | let print_or_fail = function 40 | | Ok () -> Format.printf "%s\n\n" @@ Buffer.contents dst 41 | | Error e -> 42 | Geojsone.Err.pp Format.err_formatter e; 43 | failwith "Internal err" 44 | in 45 | let cwd = Eio.Stdenv.cwd env in 46 | print_or_fail 47 | ( with_src cwd "./input/simple.geojson" @@ fun src -> 48 | Geojsone.map_props capitalise_nom src (buffer_to_dst dst) ); 49 | Buffer.clear dst; 50 | print_or_fail 51 | ( with_src cwd "./input/simple.geojson" @@ fun src -> 52 | Geojsone.map_geometry remove_all_coords src (buffer_to_dst dst) ); 53 | Buffer.clear dst; 54 | match 55 | with_src cwd "./input/simple.geojson" @@ fun src -> 56 | Geojsone.fold_props (fun acc p -> get_name p :: acc) [] src 57 | with 58 | | Ok lst -> 59 | Format.printf "Places: %a" Format.(pp_print_list pp_print_string) lst 60 | | Error e -> 61 | Geojsone.Err.pp Format.err_formatter e; 62 | failwith "Internal err" 63 | -------------------------------------------------------------------------------- /test/geojsonm/expect/test_iters.expected: -------------------------------------------------------------------------------- 1 | {"type":"Polygon","coordinates":[[[1.49886,43.09493],[1.50194,43.09495],[1.50487,43.09542],[1.50774,43.09653],[1.51541,43.09749],[1.51878,43.09719],[1.52344,43.09858],[1.5239,43.10001],[1.52728,43.10058],[1.52862,43.10136],[1.53029,43.10047],[1.53139,43.09638],[1.53073,43.09502],[1.52915,43.09403],[1.52906,43.09177],[1.52817,43.08966],[1.52867,43.08782],[1.53017,43.08665],[1.53063,43.08391],[1.53336,43.08186],[1.528,43.07899],[1.52657,43.07627],[1.52457,43.07388],[1.5244,43.07221],[1.52517,43.07046],[1.52672,43.0689],[1.52764,43.06459],[1.52944,43.06266],[1.53187,43.06096],[1.53532,43.05982],[1.54835,43.07031],[1.54656,43.07094],[1.5471,43.07336],[1.55135,43.07628],[1.55274,43.07872],[1.55414,43.07954],[1.55531,43.07858],[1.55383,43.07648],[1.55628,43.0764],[1.56261,43.0796],[1.56186,43.08062],[1.56405,43.08355],[1.56301,43.08779],[1.56615,43.09109],[1.57137,43.09144],[1.57617,43.09111],[1.57821,43.09032],[1.58135,43.08976],[1.58284,43.08864],[1.58627,43.08758],[1.59249,43.08623],[1.59508,43.08683],[1.59888,43.08663],[1.59964,43.08524],[1.59897,43.08416],[1.5992,43.08087],[1.598,43.07866],[1.59861,43.07438],[1.59591,43.07342],[1.60246,43.07058],[1.6038,43.07028],[1.61314,43.07088],[1.61493,43.06927],[1.61659,43.0691],[1.62389,43.06942],[1.62796,43.06837],[1.62903,43.07058],[1.62815,43.07305],[1.62936,43.07427],[1.63009,43.07963],[1.6313,43.08171],[1.63566,43.0865],[1.63559,43.08786],[1.63832,43.0899],[1.63843,43.09215],[1.64021,43.09185],[1.64155,43.09299],[1.64682,43.09396],[1.64894,43.09154],[1.65376,43.09301],[1.65439,43.09225],[1.66009,43.09309],[1.66273,43.09284],[1.66493,43.09448],[1.66998,43.09234],[1.67203,43.09181],[1.68038,43.09097],[1.68235,43.09021],[1.6854,43.09333],[1.69332,43.09306],[1.69358,43.09199],[1.69193,43.09049],[1.69,43.08742],[1.68891,43.08642],[1.68959,43.08494],[1.69276,43.08325],[1.69337,43.082],[1.69289,43.08063],[1.69519,43.07935],[1.69669,43.07719],[1.69768,43.07477],[1.70005,43.07231],[1.70511,43.07464],[1.70886,43.07488],[1.71229,43.07449],[1.71092,43.07097],[1.71175,43.06846],[1.71043,43.06599],[1.71119,43.06395],[1.7107,43.06238],[1.71141,43.05964],[1.70751,43.05665],[1.70607,43.05371],[1.70799,43.0521],[1.71453,43.04894],[1.71821,43.04897],[1.71822,43.0478],[1.71947,43.04645],[1.72131,43.04642],[1.72329,43.0449],[1.72809,43.04312],[1.72957,43.04187],[1.73253,43.04135],[1.73446,43.04333],[1.7377,43.04542],[1.74275,43.04973],[1.74468,43.05334],[1.75072,43.06331],[1.75594,43.0619],[1.76385,43.05811],[1.7673,43.05683],[1.77117,43.05582],[1.77162,43.05411],[1.77058,43.05243],[1.77161,43.04771],[1.77088,43.04614],[1.7672,43.04008],[1.76593,43.03864],[1.76656,43.03608],[1.76553,43.03554],[1.75905,43.03361],[1.75659,43.03346],[1.75204,43.03435],[1.75033,43.0332],[1.75116,43.03132],[1.75056,43.02852],[1.75114,43.02538],[1.75105,43.02299],[1.74862,43.0206],[1.74612,43.01919],[1.74558,43.01729],[1.74756,43.01609],[1.74907,43.01173],[1.74811,43.00938],[1.74598,43.00806],[1.74368,43.00486],[1.74264,43.00436],[1.74273,43.00116],[1.74409,43.00073],[1.74509,42.99888],[1.74849,42.99692],[1.74746,42.99453],[1.75087,42.99166],[1.75357,42.99015],[1.75335,42.98862],[1.75624,42.98746],[1.7564,42.98341],[1.75724,42.98022],[1.75452,42.98084],[1.74996,42.98261],[1.74574,42.9835],[1.74458,42.98336],[1.74219,42.98177],[1.74227,42.98015],[1.74002,42.97941],[1.73708,42.97773],[1.73463,42.97763],[1.73348,42.97541],[1.73104,42.975],[1.72387,42.97569],[1.72175,42.97243],[1.71917,42.97308],[1.7142,42.97379],[1.71239,42.97084],[1.71247,42.9695],[1.71634,42.9679],[1.71619,42.9652],[1.71823,42.96385],[1.71701,42.96241],[1.71874,42.96039],[1.71617,42.96052],[1.71479,42.95884],[1.71252,42.95824],[1.70955,42.95611],[1.70648,42.95573],[1.70538,42.95488],[1.70073,42.9548],[1.7051,42.95343],[1.70834,42.95291],[1.71125,42.95158],[1.71347,42.94933],[1.71531,42.94845],[1.71774,42.94607],[1.71899,42.94317],[1.72114,42.94227],[1.72538,42.9359],[1.72655,42.93524],[1.72584,42.9328],[1.72436,42.93207],[1.72186,42.93219],[1.72268,42.92982],[1.72011,42.92845],[1.71922,42.9262],[1.72189,42.92224],[1.72106,42.9203],[1.72018,42.91625],[1.71875,42.91377],[1.72203,42.91522],[1.72323,42.91708],[1.72453,42.91749],[1.72685,42.91711],[1.72889,42.91473],[1.7305,42.91388],[1.73527,42.91278],[1.74378,42.91459],[1.74715,42.91378],[1.75125,42.9139],[1.7538,42.91553],[1.75596,42.91451],[1.75834,42.91493],[1.76222,42.91366],[1.76508,42.91442],[1.76754,42.91403],[1.77072,42.91414],[1.77368,42.91235],[1.77168,42.90919],[1.77129,42.90635],[1.77031,42.90496],[1.76824,42.90354],[1.76857,42.90135],[1.76694,42.89962],[1.76658,42.89809],[1.76224,42.89775],[1.75959,42.89239],[1.76061,42.8896],[1.76314,42.88754],[1.76254,42.88565],[1.75974,42.88406],[1.7562,42.88279],[1.75504,42.88174],[1.74883,42.87896],[1.74708,42.87711],[1.73649,42.87085],[1.73284,42.86911],[1.72863,42.86827],[1.72747,42.86764],[1.72614,42.8657],[1.72616,42.8644],[1.72464,42.86281],[1.72149,42.86108],[1.71936,42.8581],[1.71622,42.85543],[1.71624,42.854],[1.71851,42.85256],[1.71717,42.85165],[1.71701,42.84999],[1.71602,42.8494],[1.71611,42.84787],[1.71467,42.84493],[1.71579,42.84274],[1.71546,42.83958],[1.7221,42.83597],[1.72378,42.83323],[1.72666,42.83352],[1.73039,42.83215],[1.73205,42.83063],[1.73525,42.83042],[1.73991,42.82896],[1.7423,42.82777],[1.74403,42.82606],[1.74832,42.82509],[1.75608,42.81945],[1.7612,42.82048],[1.76407,42.82063],[1.76922,42.81921],[1.77332,42.81888],[1.77755,42.81799],[1.78115,42.81805],[1.78306,42.81743],[1.78481,42.81915],[1.78603,42.81957],[1.78775,42.81832],[1.79154,42.81779],[1.79748,42.818],[1.80101,42.81883],[1.80334,42.81857],[1.80495,42.81646],[1.80661,42.8153],[1.80838,42.81508],[1.81039,42.81564],[1.81553,42.8148],[1.81725,42.81373],[1.82204,42.81281],[1.82685,42.81468],[1.82917,42.816],[1.83282,42.81673],[1.83441,42.81638],[1.83655,42.81761],[1.83984,42.81739],[1.84473,42.818],[1.84997,42.81805],[1.8545,42.81921],[1.85938,42.82081],[1.85994,42.81959],[1.86215,42.81762],[1.86589,42.81728],[1.86816,42.81553],[1.87123,42.81455],[1.87399,42.81411],[1.87795,42.81413],[1.88163,42.81246],[1.88414,42.81245],[1.88578,42.81167],[1.88807,42.81147],[1.89394,42.80965],[1.89609,42.80867],[1.89775,42.80715],[1.89856,42.80431],[1.89839,42.80269],[1.90096,42.80077],[1.90612,42.79841],[1.90761,42.79566],[1.90731,42.79436],[1.90823,42.79292],[1.9134,42.78893],[1.91482,42.78593],[1.91963,42.78081],[1.92134,42.77992],[1.92338,42.77769],[1.92884,42.77495],[1.9235,42.77151],[1.9217,42.76996],[1.91788,42.76992],[1.91324,42.76918],[1.90938,42.76921],[1.90818,42.76767],[1.90899,42.76525],[1.91139,42.76251],[1.91361,42.75788],[1.91979,42.75719],[1.92207,42.75517],[1.92982,42.7552],[1.93179,42.75345],[1.93094,42.75115],[1.9334,42.75031],[1.9367,42.74996],[1.93861,42.74794],[1.94129,42.74688],[1.94644,42.74576],[1.94717,42.74437],[1.94689,42.74172],[1.94775,42.73915],[1.9488,42.73799],[1.95163,42.73669],[1.95949,42.73686],[1.96249,42.73771],[1.96365,42.73696],[1.9681,42.7363],[1.97025,42.73535],[1.97585,42.73668],[1.98178,42.73602],[1.98452,42.73675],[1.98686,42.73519],[1.99144,42.7348],[1.99724,42.73594],[2.00009,42.73595],[2.00323,42.73457],[2.00384,42.73348],[2.00793,42.73412],[2.0117,42.73648],[2.01448,42.73973],[2.01998,42.74002],[2.02346,42.73903],[2.02543,42.73899],[2.02681,42.73999],[2.03078,42.74148],[2.03142,42.74652],[2.03472,42.74739],[2.03918,42.74614],[2.04163,42.74633],[2.04327,42.74737],[2.04784,42.74904],[2.04978,42.75139],[2.05234,42.75373],[2.05641,42.756],[2.05769,42.75614],[2.0632,42.75507],[2.06613,42.75412],[2.06997,42.75432],[2.07181,42.75364],[2.07505,42.7537],[2.07791,42.75312],[2.07976,42.75214],[2.0814,42.75228],[2.0839,42.75161],[2.08629,42.7518],[2.08594,42.7494],[2.08501,42.74851],[2.08638,42.74594],[2.08639,42.7441],[2.08749,42.74122],[2.08744,42.73901],[2.08892,42.73717],[2.09217,42.73466],[2.09358,42.73448],[2.09638,42.73314],[2.09883,42.73328],[2.10084,42.73148],[2.10206,42.73148],[2.10471,42.7292],[2.10806,42.72722],[2.11155,42.72678],[2.11381,42.7245],[2.11723,42.72381],[2.11925,42.72153],[2.12084,42.7214],[2.12072,42.71995],[2.1233,42.71857],[2.12506,42.71925],[2.12678,42.71683],[2.13155,42.71494],[2.13356,42.71471],[2.13595,42.71324],[2.13955,42.7123],[2.14078,42.71046],[2.14061,42.70582],[2.14415,42.70448],[2.14666,42.70138],[2.14788,42.70097],[2.15203,42.70156],[2.15466,42.70135],[2.15971,42.70221],[2.16211,42.69951],[2.16528,42.69821],[2.16786,42.69618],[2.17133,42.69111],[2.1714,42.6895],[2.17587,42.6828],[2.176,42.68145],[2.17308,42.67797],[2.17242,42.67576],[2.16748,42.67338],[2.16532,42.66585],[2.16605,42.66392],[2.15965,42.66435],[2.15386,42.66224],[2.15129,42.66196],[2.14898,42.66277],[2.14337,42.6637],[2.13262,42.66836],[2.1301,42.67115],[2.12784,42.67227],[2.12388,42.67168],[2.11798,42.66847],[2.11201,42.66643],[2.10897,42.66386],[2.10678,42.66525],[2.10428,42.66506],[2.09824,42.66402],[2.09611,42.66402],[2.09189,42.66567],[2.08659,42.66544],[2.0853,42.66678],[2.08378,42.66669],[2.0811,42.66465],[2.07634,42.66477],[2.07049,42.66639],[2.06976,42.66495],[2.0677,42.66412],[2.06458,42.66426],[2.06088,42.66168],[2.05452,42.66306],[2.04702,42.66359],[2.0443,42.66016],[2.04327,42.6575],[2.02763,42.65251],[2.02256,42.65339],[2.01879,42.65342],[2.01725,42.65472],[2.01353,42.65611],[2.01225,42.65695],[2.01248,42.65944],[2.007,42.65861],[2.00455,42.65936],[2.00369,42.66022],[2.00051,42.66156],[1.99856,42.66133],[1.99692,42.66007],[1.99736,42.65719],[1.997,42.65611],[1.9953,42.65506],[1.99392,42.65268],[1.99257,42.65232],[1.99271,42.65083],[1.99077,42.64664],[1.98793,42.64479],[1.98635,42.64086],[1.98668,42.64032],[1.98528,42.63722],[1.98257,42.63347],[1.97995,42.63166],[1.97978,42.63039],[1.97759,42.62927],[1.97583,42.62693],[1.97597,42.62481],[1.97726,42.62355],[1.97636,42.62107],[1.97413,42.61941],[1.97176,42.61692],[1.96829,42.61637],[1.96525,42.61631],[1.96311,42.61712],[1.96043,42.61693],[1.9581,42.61746],[1.95602,42.61871],[1.95401,42.61889],[1.95293,42.61816],[1.95012,42.61774],[1.94722,42.61468],[1.94363,42.61314],[1.94451,42.61003],[1.94379,42.60855],[1.93887,42.60686],[1.93497,42.60492],[1.93339,42.60482],[1.92991,42.60611],[1.92666,42.60943],[1.92477,42.60879],[1.92228,42.60932],[1.91467,42.60763],[1.9115,42.60761],[1.90729,42.60863],[1.90368,42.61154],[1.90018,42.6149],[1.89748,42.60905],[1.89305,42.60633],[1.89361,42.60476],[1.89546,42.60233],[1.89413,42.60016],[1.89318,42.59616],[1.89127,42.59331],[1.88709,42.59055],[1.88385,42.58987],[1.88019,42.59007],[1.87423,42.59],[1.8735,42.58942],[1.87317,42.58541],[1.87449,42.58141],[1.87015,42.58143],[1.86938,42.57999],[1.86432,42.57899],[1.8617,42.58135],[1.86038,42.58401],[1.85724,42.58278],[1.85617,42.5816],[1.8539,42.58129],[1.85365,42.58232],[1.85157,42.58433],[1.84821,42.58372],[1.84707,42.58305],[1.84176,42.58303],[1.83907,42.58441],[1.83706,42.58454],[1.83131,42.58149],[1.82473,42.58124],[1.82157,42.57956],[1.81768,42.57954],[1.81599,42.57818],[1.80634,42.57458],[1.801,42.5724],[1.79874,42.57239],[1.79601,42.57148],[1.79039,42.574],[1.78766,42.573],[1.78613,42.57362],[1.78661,42.57431],[1.78441,42.57587],[1.78313,42.58012],[1.78094,42.5826],[1.77741,42.58205],[1.77316,42.58068],[1.76805,42.58052],[1.76074,42.58092],[1.75859,42.58195],[1.75475,42.58292],[1.75286,42.58264],[1.74961,42.58469],[1.74846,42.58468],[1.74197,42.58775],[1.73539,42.58791],[1.73375,42.58853],[1.73173,42.58806],[1.72826,42.58841],[1.72612,42.58969],[1.72631,42.60031],[1.72899,42.60123],[1.73127,42.60399],[1.73114,42.60569],[1.7319,42.60741],[1.73519,42.60851],[1.73663,42.61077],[1.73796,42.61131],[1.73727,42.61424],[1.73622,42.61503],[1.73607,42.61715],[1.73353,42.61579],[1.72951,42.61482],[1.72817,42.61563],[1.72628,42.61548],[1.72281,42.61636],[1.7202,42.61504],[1.71411,42.61461],[1.7102,42.61633],[1.70705,42.6187],[1.70063,42.62119],[1.69818,42.62274],[1.69204,42.62195],[1.69031,42.62325],[1.68671,42.6239],[1.6836,42.62486],[1.68056,42.62481],[1.67838,42.6225],[1.67577,42.6223],[1.67346,42.62081],[1.67205,42.62125],[1.66687,42.62153],[1.6644,42.61904],[1.66203,42.61948],[1.66086,42.62037],[1.65689,42.62115],[1.65553,42.62255],[1.65496,42.62447],[1.65372,42.62631],[1.63883,42.62842],[1.63716,42.6303],[1.63072,42.62779],[1.62788,42.62633],[1.62232,42.62728],[1.61953,42.62583],[1.61691,42.62662],[1.61149,42.62667],[1.60934,42.62855],[1.60762,42.62832],[1.60357,42.62585],[1.60205,42.62548],[1.59974,42.62619],[1.59527,42.63179],[1.59313,42.63313],[1.58568,42.63357],[1.5842,42.6341],[1.58236,42.63589],[1.57978,42.63753],[1.57921,42.63902],[1.57944,42.64117],[1.57845,42.64229],[1.57599,42.64336],[1.57589,42.6462],[1.57489,42.64803],[1.57192,42.64796],[1.56879,42.64843],[1.56707,42.64973],[1.56607,42.65148],[1.56239,42.65348],[1.5605,42.65333],[1.55832,42.65215],[1.55562,42.6533],[1.5533,42.65368],[1.54925,42.65578],[1.54396,42.6542],[1.54093,42.65284],[1.53832,42.65255],[1.53548,42.65009],[1.53384,42.64968],[1.53083,42.65124],[1.52864,42.65144],[1.52638,42.65084],[1.52458,42.64956],[1.52215,42.64918],[1.52143,42.64743],[1.51891,42.64548],[1.51262,42.64615],[1.5058,42.6452],[1.50288,42.64544],[1.50061,42.64515],[1.49925,42.6474],[1.49758,42.64873],[1.49375,42.65321],[1.49106,42.65297],[1.48814,42.65194],[1.48474,42.65206],[1.4801,42.65139],[1.47877,42.65165],[1.47874,42.6494],[1.47809,42.64675],[1.47702,42.64524],[1.47796,42.6435],[1.47691,42.64074],[1.4757,42.63957],[1.46991,42.63565],[1.46993,42.63385],[1.46834,42.63082],[1.47098,42.62918],[1.47142,42.62824],[1.47053,42.62626],[1.47105,42.62467],[1.47382,42.62141],[1.47428,42.61939],[1.47573,42.61656],[1.47727,42.61497],[1.47656,42.61302],[1.47423,42.61089],[1.47042,42.60869],[1.46946,42.60689],[1.46777,42.60638],[1.46478,42.60685],[1.46339,42.60562],[1.46175,42.60562],[1.45981,42.60475],[1.45704,42.60229],[1.4504,42.6026],[1.44782,42.60379],[1.44282,42.60366],[1.43783,42.60321],[1.43567,42.60581],[1.4334,42.60692],[1.43302,42.60831],[1.43085,42.61136],[1.42923,42.61269],[1.42933,42.61444],[1.43056,42.61733],[1.4281,42.61845],[1.42728,42.62095],[1.42379,42.62201],[1.42206,42.62397],[1.4207,42.62473],[1.42019,42.62607],[1.41996,42.62949],[1.41872,42.63069],[1.41974,42.63223],[1.41807,42.63348],[1.41652,42.63568],[1.41593,42.63764],[1.41617,42.63913],[1.4173,42.64],[1.41691,42.64184],[1.41444,42.64407],[1.41366,42.64766],[1.41466,42.6496],[1.41359,42.65145],[1.41441,42.65402],[1.41366,42.65552],[1.40837,42.65759],[1.40537,42.65721],[1.40315,42.65887],[1.40377,42.66002],[1.40196,42.66188],[1.40132,42.66457],[1.39664,42.66805],[1.39424,42.66839],[1.38986,42.66804],[1.38778,42.66837],[1.38773,42.67099],[1.38888,42.67289],[1.38891,42.67427],[1.38703,42.67741],[1.38876,42.67991],[1.38752,42.68151],[1.3896,42.68508],[1.38933,42.68679],[1.3866,42.68952],[1.38469,42.69067],[1.381,42.69181],[1.37737,42.69452],[1.37248,42.69475],[1.36955,42.69563],[1.3651,42.69451],[1.3617,42.69776],[1.35693,42.69867],[1.35317,42.70035],[1.35124,42.70266],[1.35103,42.7046],[1.35235,42.70623],[1.35195,42.70835],[1.35303,42.70971],[1.35288,42.71128],[1.35528,42.71355],[1.35801,42.71493],[1.35803,42.71694],[1.35738,42.71941],[1.35914,42.72407],[1.36029,42.72485],[1.36344,42.72559],[1.36479,42.72898],[1.36627,42.73147],[1.37193,42.73291],[1.37452,42.73136],[1.37759,42.73017],[1.37978,42.73086],[1.38295,42.73112],[1.38521,42.73182],[1.38703,42.73159],[1.39139,42.73465],[1.38908,42.74134],[1.39178,42.74478],[1.39149,42.74766],[1.39682,42.7541],[1.39696,42.75689],[1.39955,42.75939],[1.40014,42.76461],[1.40384,42.76644],[1.40497,42.76829],[1.40506,42.77028],[1.40595,42.77208],[1.40408,42.77464],[1.40287,42.77775],[1.40431,42.77954],[1.40491,42.78333],[1.40721,42.78595],[1.40696,42.79027],[1.40786,42.79078],[1.40635,42.79328],[1.40737,42.79518],[1.40458,42.79727],[1.4064,42.80246],[1.40847,42.80298],[1.41046,42.80493],[1.41424,42.80929],[1.41397,42.81126],[1.41435,42.81419],[1.41602,42.81654],[1.41521,42.8178],[1.4158,42.81933],[1.41852,42.82142],[1.4211,42.82491],[1.42772,42.82699],[1.43373,42.82997],[1.43552,42.8297],[1.43752,42.83036],[1.43996,42.83032],[1.4405,42.83178],[1.44291,42.83445],[1.4417,42.83705],[1.44016,42.83865],[1.44043,42.84065],[1.43945,42.84428],[1.43942,42.84639],[1.44185,42.8479],[1.44326,42.85128],[1.44991,42.85741],[1.45112,42.85814],[1.4501,42.8611],[1.45084,42.86458],[1.45031,42.86722],[1.45033,42.87096],[1.45225,42.8726],[1.45275,42.87593],[1.45364,42.87873],[1.45471,42.87986],[1.45293,42.88106],[1.45192,42.8834],[1.44927,42.88418],[1.44838,42.88621],[1.44579,42.88802],[1.45102,42.88992],[1.45154,42.89199],[1.45114,42.8942],[1.45148,42.89617],[1.4531,42.89926],[1.45759,42.90563],[1.45494,42.90632],[1.45376,42.9074],[1.44947,42.90822],[1.44645,42.90937],[1.44458,42.91165],[1.44268,42.91186],[1.438,42.9139],[1.43731,42.91488],[1.43413,42.91503],[1.42954,42.91436],[1.42695,42.91515],[1.42224,42.91498],[1.41285,42.91665],[1.41063,42.91781],[1.41093,42.92146],[1.41213,42.92268],[1.41198,42.92965],[1.41113,42.93352],[1.41104,42.93856],[1.41332,42.94245],[1.41632,42.94558],[1.42123,42.94917],[1.42446,42.95059],[1.42656,42.95394],[1.42723,42.95778],[1.43062,42.96032],[1.43398,42.96084],[1.43643,42.96181],[1.44481,42.96237],[1.45186,42.96207],[1.45674,42.96391],[1.45845,42.96397],[1.46125,42.96538],[1.46072,42.96804],[1.46185,42.97016],[1.46308,42.97053],[1.46749,42.97034],[1.47232,42.97114],[1.47567,42.97346],[1.4806,42.97584],[1.48615,42.97714],[1.48502,42.97943],[1.48524,42.98177],[1.48704,42.98408],[1.48898,42.98535],[1.4945,42.98499],[1.49615,42.98554],[1.49551,42.99143],[1.49765,42.99221],[1.49974,42.99169],[1.50341,42.99248],[1.50771,42.99211],[1.50919,42.99109],[1.51213,42.99112],[1.51268,42.9917],[1.51431,42.99729],[1.51879,42.99723],[1.52023,42.99922],[1.52051,43.0017],[1.519,43.00448],[1.51744,43.00632],[1.51178,43.00852],[1.5114,43.00964],[1.51414,43.00967],[1.51722,43.00888],[1.5222,43.00864],[1.52095,43.00994],[1.51897,43.01587],[1.52028,43.01853],[1.51887,43.01865],[1.52074,43.02146],[1.51851,43.02234],[1.51668,43.02201],[1.51567,43.02382],[1.51356,43.0256],[1.51281,43.02734],[1.51133,43.02715],[1.50719,43.02974],[1.50686,43.03054],[1.50392,43.03098],[1.49948,43.03251],[1.50031,43.03567],[1.50127,43.0367],[1.50401,43.03768],[1.50465,43.03975],[1.50683,43.04265],[1.49874,43.04628],[1.49644,43.04751],[1.49629,43.05004],[1.45429,43.0684],[1.45395,43.07078],[1.45576,43.07281],[1.4578,43.07594],[1.45913,43.07703],[1.463,43.07755],[1.46623,43.0787],[1.46866,43.0816],[1.46917,43.08336],[1.47078,43.08257],[1.47352,43.08466],[1.47585,43.08476],[1.48344,43.08748],[1.48582,43.08794],[1.49246,43.08754],[1.49682,43.09217],[1.49779,43.09236],[1.49806,43.09443],[1.49886,43.09493]]]} 2 | {"code":"09001","nom":"Foix"} 3 | -------------------------------------------------------------------------------- /test/geojsonm/expect/test_iters.ml: -------------------------------------------------------------------------------- 1 | open Eio 2 | 3 | let buffer_to_dst buf bs = 4 | Flow.(copy (cstruct_source [ bs ]) (Flow.buffer_sink buf)) 5 | 6 | let value_to_buffer ?minify buf json = 7 | Geojsone.Ezjsone.value_to_dst ?minify (buffer_to_dst buf) json 8 | 9 | let value_to_string ?minify json = 10 | let buf = Buffer.create 1024 in 11 | value_to_buffer ?minify buf json; 12 | Buffer.contents buf 13 | 14 | let print_geometry g = print_endline @@ value_to_string (Geojsone.G.to_json g) 15 | let print_property prop = print_endline @@ value_to_string prop 16 | 17 | let with_src cwd f func = 18 | Eio.Path.(with_open_in (cwd / f)) @@ fun ic -> 19 | func @@ Geojsone_eio.src_of_flow ic 20 | 21 | let () = 22 | Eio_main.run @@ fun env -> 23 | let or_fail = function 24 | | Ok () -> () 25 | | Error e -> 26 | Geojsone.Err.pp Format.err_formatter e; 27 | failwith "Internal err" 28 | in 29 | let cwd = Eio.Stdenv.cwd env in 30 | or_fail 31 | ( with_src cwd "./input/simple.geojson" @@ fun src -> 32 | Geojsone.iter_geometry print_geometry src ); 33 | or_fail 34 | ( with_src cwd "./input/simple.geojson" @@ fun src -> 35 | Geojsone.iter_props print_property src ) 36 | -------------------------------------------------------------------------------- /test/prelude.txt: -------------------------------------------------------------------------------- 1 | #require "geojson";; 2 | #require "ezjsonm";; 3 | #require "geojsone";; 4 | module Ezjsonm_parser = struct 5 | type t = Ezjsonm.value 6 | 7 | let catch_err f v = 8 | try Ok (f v) with Ezjsonm.Parse_error (_, s) -> Error (`Msg s) 9 | 10 | let find = Ezjsonm.find_opt 11 | let to_string t = catch_err Ezjsonm.get_string t 12 | let string = Ezjsonm.string 13 | let to_float t = catch_err Ezjsonm.get_float t 14 | let float = Ezjsonm.float 15 | let to_int t = catch_err Ezjsonm.get_int t 16 | let int = Ezjsonm.int 17 | let to_list f t = catch_err (Ezjsonm.get_list f) t 18 | let list f t = Ezjsonm.list f t 19 | let to_array f t = Result.map Array.of_list @@ to_list f t 20 | let array f t = list f (Array.to_list t) 21 | let to_obj t = catch_err Ezjsonm.get_dict t 22 | let obj = Ezjsonm.dict 23 | let null = `Null 24 | let is_null = function `Null -> true | _ -> false 25 | end 26 | 27 | let feature_example = {| 28 | { 29 | "type": "Feature", 30 | "geometry": { 31 | "type": "MultiPoint", 32 | "coordinates": [[125.1, 40.0], [155.9, 22.5]] 33 | }, 34 | "properties": { 35 | "name": "Dinagat Islands" 36 | }, 37 | "title": "Some Islands" 38 | } 39 | |} --------------------------------------------------------------------------------