From 06c18df56162442d1358232bce0114337f21af49 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Sun, 4 Aug 2024 14:04:48 +0000 Subject: [PATCH] build based on d8c8460 --- previews/PR135/404.html | 4 +-- previews/PR135/api.html | 10 +++--- ...{api.md.BkdCNSpp.js => api.md.9bKu-Iv9.js} | 2 +- ...dCNSpp.lean.js => api.md.9bKu-Iv9.lean.js} | 0 .../{app.okziLJoA.js => app.CNviBygr.js} | 2 +- ...vlaw.CgiryX2p.png => aqvzuly.CgiryX2p.png} | Bin ...vvce.DeeQUply.png => auepdne.DeeQUply.png} | Bin previews/PR135/assets/briiybs.Cy4gx45x.png | Bin 73994 -> 0 bytes ...jlyd.lu4jwpi-.png => byynyll.lu4jwpi-.png} | Bin previews/PR135/assets/cezjmfi.BcI7dJNv.png | Bin 206800 -> 0 bytes .../chunks/@localSearchIndexroot.CbpULWWL.js | 1 + .../chunks/@localSearchIndexroot.DOIebHWk.js | 1 - ...n4sKrv.js => VPLocalSearchBox.BBhHuDzv.js} | 2 +- .../{theme.D6VlRy8Q.js => theme.Cfpb4cN3.js} | 4 +-- previews/PR135/assets/dbqxaoe.DstoYpf0.png | Bin 0 -> 77840 bytes previews/PR135/assets/dkqnojp.DvNFs9H2.png | Bin 0 -> 63505 bytes previews/PR135/assets/dkyghoj.DJXxaGlq.png | Bin 0 -> 229282 bytes ...tupt.DuBHk1fh.png => dvzgimn.DuBHk1fh.png} | Bin ... => experiments_predicates.md.DJ9haq0_.js} | 2 +- ...xperiments_predicates.md.DJ9haq0_.lean.js} | 2 +- previews/PR135/assets/fntoqls.y5I2w7nz.png | Bin 63270 -> 0 bytes previews/PR135/assets/fpvgaeb.C33-xzEN.png | Bin 0 -> 59906 bytes ...gerj.mCtKcWOr.png => gabfqot.mCtKcWOr.png} | Bin ...vojo.DiwGEg2f.png => gasrsxh.DiwGEg2f.png} | Bin ...fpgl.DHcwB147.png => gfmgmdt.DHcwB147.png} | Bin ...hltv.C3SxJ3x-.png => hwbtkcd.C3SxJ3x-.png} | Bin ...zyqr.CG4dr3Lx.png => ifbqouh.CG4dr3Lx.png} | Bin ...iovt.BEFUMtlf.png => iitfiec.BEFUMtlf.png} | Bin ...dbdt.Cb0_DiYE.png => iogmviv.Cb0_DiYE.png} | Bin ...qkyr.Danh069g.png => itchqsx.Danh069g.png} | Bin ...mhqq.Bglvb-jp.png => jdjmpmf.Bglvb-jp.png} | Bin ...eegt.DwqDxlAG.png => jescohr.DwqDxlAG.png} | Bin ...sfis.BD0hVfse.png => jrfxtvf.BD0hVfse.png} | Bin previews/PR135/assets/leqplng.DT95HOc4.png | Bin 228635 -> 0 bytes ...vpyq.0OJvb21A.png => leylmwm.0OJvb21A.png} | Bin ...elii.B94PsR1K.png => lleqqls.B94PsR1K.png} | Bin ...mudq.-VpeHhXX.png => mcgsyqs.-VpeHhXX.png} | Bin ...wmlk.3sfpQl2i.png => ooyleff.3sfpQl2i.png} | Bin ...mtly.Dab1-ETk.png => oqljina.Dab1-ETk.png} | Bin ...hokg.CULn5saZ.png => orytpqi.CULn5saZ.png} | Bin previews/PR135/assets/oylwyba.CuwrHkOd.png | Bin 59413 -> 0 bytes ...rfjx.Dz86q2IX.png => oyrrvdx.Dz86q2IX.png} | Bin ...dutf.Cx40vhB3.png => pcobipb.Cx40vhB3.png} | Bin previews/PR135/assets/pqnppem.D2RAc4b6.png | Bin 0 -> 75447 bytes previews/PR135/assets/qfveqll.DPlfA76p.png | Bin 63261 -> 0 bytes ...iyfz._0R9BbFk.png => qmgjswp._0R9BbFk.png} | Bin ...waxi.CZy9YIUA.png => rnrcnyd.CZy9YIUA.png} | Bin ...ztmc.DC3TvBOO.png => scxzlgy.DC3TvBOO.png} | Bin ...s => source_methods_angles.md.DIrieyKk.js} | 2 +- ...source_methods_angles.md.DIrieyKk.lean.js} | 2 +- ....js => source_methods_area.md.TI4YB_Sz.js} | 2 +- ...> source_methods_area.md.TI4YB_Sz.lean.js} | 2 +- ...source_methods_barycentric.md.CkNqARiG.js} | 2 +- ...e_methods_barycentric.md.CkNqARiG.lean.js} | 2 +- ...=> source_methods_centroid.md.CLmopaiG.js} | 4 +-- ...urce_methods_centroid.md.CLmopaiG.lean.js} | 2 +- ..._methods_clipping_coverage.md.6uKDiP9y.js} | 2 +- ...ods_clipping_coverage.md.6uKDiP9y.lean.js} | 2 +- ...ource_methods_clipping_cut.md.CdaQF2b4.js} | 2 +- ..._methods_clipping_cut.md.CdaQF2b4.lean.js} | 2 +- ...source_methods_convex_hull.md.C5LkdFR_.js} | 2 +- ...e_methods_convex_hull.md.C5LkdFR_.lean.js} | 2 +- ...=> source_methods_distance.md.B9pnbS2m.js} | 2 +- ...urce_methods_distance.md.B9pnbS2m.lean.js} | 2 +- ...s => source_methods_equals.md.C_dUmjSr.js} | 2 +- ...source_methods_equals.md.C_dUmjSr.lean.js} | 2 +- ...ds_geom_relations_contains.md.7FBluCIz.js} | 2 +- ...om_relations_contains.md.7FBluCIz.lean.js} | 2 +- ...s_geom_relations_coveredby.md.CpSZpBMD.js} | 2 +- ...m_relations_coveredby.md.CpSZpBMD.lean.js} | 2 +- ...hods_geom_relations_covers.md.DU7RNv3T.js} | 2 +- ...geom_relations_covers.md.DU7RNv3T.lean.js} | 2 +- ...ds_geom_relations_disjoint.md.CdOMOhdn.js} | 2 +- ...om_relations_disjoint.md.CdOMOhdn.lean.js} | 2 +- ..._geom_relations_intersects.md.-qwBxS9g.js} | 2 +- ..._relations_intersects.md.-qwBxS9g.lean.js} | 2 +- ...ds_geom_relations_overlaps.md.4HmWrCsY.js} | 2 +- ...om_relations_overlaps.md.4HmWrCsY.lean.js} | 2 +- ...ods_geom_relations_touches.md.DMienZ7T.js} | 2 +- ...eom_relations_touches.md.DMienZ7T.lean.js} | 2 +- ...hods_geom_relations_within.md.CpoApCgK.js} | 2 +- ...geom_relations_within.md.CpoApCgK.lean.js} | 2 +- ...transformations_segmentize.md.RPr3lout.js} | 2 +- ...formations_segmentize.md.RPr3lout.lean.js} | 2 +- ...e_transformations_simplify.md.Dco3qKQU.js} | 2 +- ...nsformations_simplify.md.Dco3qKQU.lean.js} | 2 +- ...xqux.Dig-DWOQ.png => stwzrcs.Dig-DWOQ.png} | Bin ...chbn.3UVIT8DR.png => tnpgawm.3UVIT8DR.png} | Bin previews/PR135/assets/tpzfygj.BZeu3Tcc.png | Bin 0 -> 68861 bytes ...ials_creating_geometry.md.CQNXRFYE.lean.js | 1 - ...utorials_creating_geometry.md.EcNPbH1J.js} | 2 +- ...ials_creating_geometry.md.EcNPbH1J.lean.js | 1 + ...> tutorials_geodesic_paths.md.B4I7D6HJ.js} | 4 +-- ...orials_geodesic_paths.md.B4I7D6HJ.lean.js} | 2 +- ...=> tutorials_spatial_joins.md.XXlENBfc.js} | 2 +- ...torials_spatial_joins.md.XXlENBfc.lean.js} | 2 +- previews/PR135/assets/tuvqaxg.9h_7Tsnr.png | Bin 0 -> 207721 bytes previews/PR135/assets/tzjojwg.DvOK4aMR.png | Bin 79050 -> 0 bytes ...jhcv.DaovVbE6.png => ufohoag.DaovVbE6.png} | Bin ...wanf.CLtpJ5Wb.png => vvbiyov.CLtpJ5Wb.png} | Bin ...yzdr.BOOG5oTW.png => vxuhcsb.BOOG5oTW.png} | Bin previews/PR135/assets/xqlfxjv.AayN5YeI.png | Bin 63300 -> 0 bytes ...urnn.B9NpLJr_.png => znwtliz.B9NpLJr_.png} | Bin previews/PR135/assets/zstdwff.C3FlfbBb.png | Bin 0 -> 64848 bytes previews/PR135/call_notes.html | 6 ++-- .../experiments/accurate_accumulators.html | 6 ++-- previews/PR135/experiments/predicates.html | 10 +++--- previews/PR135/explanations/crs.html | 6 ++-- previews/PR135/explanations/paradigms.html | 6 ++-- .../PR135/explanations/peculiarities.html | 6 ++-- .../PR135/explanations/winding_order.html | 6 ++-- previews/PR135/hashmap.json | 2 +- previews/PR135/index.html | 6 ++-- previews/PR135/introduction.html | 6 ++-- previews/PR135/source/GeometryOps.html | 6 ++-- previews/PR135/source/methods/angles.html | 10 +++--- previews/PR135/source/methods/area.html | 12 +++---- .../PR135/source/methods/barycentric.html | 10 +++--- previews/PR135/source/methods/buffer.html | 6 ++-- previews/PR135/source/methods/centroid.html | 12 +++---- .../methods/clipping/clipping_processor.html | 6 ++-- .../source/methods/clipping/coverage.html | 10 +++--- .../PR135/source/methods/clipping/cut.html | 10 +++--- .../source/methods/clipping/difference.html | 6 ++-- .../source/methods/clipping/intersection.html | 6 ++-- .../source/methods/clipping/predicates.html | 6 ++-- .../PR135/source/methods/clipping/union.html | 6 ++-- .../PR135/source/methods/convex_hull.html | 14 ++++---- previews/PR135/source/methods/distance.html | 12 +++---- previews/PR135/source/methods/equals.html | 10 +++--- .../methods/geom_relations/contains.html | 10 +++--- .../methods/geom_relations/coveredby.html | 10 +++--- .../source/methods/geom_relations/covers.html | 10 +++--- .../methods/geom_relations/crosses.html | 6 ++-- .../methods/geom_relations/disjoint.html | 10 +++--- .../geom_relations/geom_geom_processors.html | 6 ++-- .../methods/geom_relations/intersects.html | 10 +++--- .../methods/geom_relations/overlaps.html | 10 +++--- .../methods/geom_relations/touches.html | 10 +++--- .../source/methods/geom_relations/within.html | 10 +++--- .../PR135/source/methods/orientation.html | 6 ++-- previews/PR135/source/methods/polygonize.html | 6 ++-- .../PR135/source/not_implemented_yet.html | 6 ++-- previews/PR135/source/primitives.html | 6 ++-- .../correction/closed_ring.html | 6 ++-- .../correction/geometry_correction.html | 6 ++-- .../correction/intersecting_polygons.html | 6 ++-- .../PR135/source/transformations/extent.html | 6 ++-- .../PR135/source/transformations/flip.html | 6 ++-- .../source/transformations/reproject.html | 6 ++-- .../source/transformations/segmentize.html | 12 +++---- .../source/transformations/simplify.html | 16 +++++----- .../source/transformations/transform.html | 6 ++-- .../PR135/source/transformations/tuples.html | 6 ++-- previews/PR135/source/types.html | 6 ++-- previews/PR135/source/utils.html | 6 ++-- .../PR135/tutorials/creating_geometry.html | 30 +++++++++--------- previews/PR135/tutorials/geodesic_paths.html | 10 +++--- previews/PR135/tutorials/spatial_joins.html | 14 ++++---- 159 files changed, 291 insertions(+), 291 deletions(-) rename previews/PR135/assets/{api.md.BkdCNSpp.js => api.md.9bKu-Iv9.js} (99%) rename previews/PR135/assets/{api.md.BkdCNSpp.lean.js => api.md.9bKu-Iv9.lean.js} (100%) rename previews/PR135/assets/{app.okziLJoA.js => app.CNviBygr.js} (95%) rename previews/PR135/assets/{ftxvlaw.CgiryX2p.png => aqvzuly.CgiryX2p.png} (100%) rename previews/PR135/assets/{rezvvce.DeeQUply.png => auepdne.DeeQUply.png} (100%) delete mode 100644 previews/PR135/assets/briiybs.Cy4gx45x.png rename previews/PR135/assets/{ovfjlyd.lu4jwpi-.png => byynyll.lu4jwpi-.png} (100%) delete mode 100644 previews/PR135/assets/cezjmfi.BcI7dJNv.png create mode 100644 previews/PR135/assets/chunks/@localSearchIndexroot.CbpULWWL.js delete mode 100644 previews/PR135/assets/chunks/@localSearchIndexroot.DOIebHWk.js rename previews/PR135/assets/chunks/{VPLocalSearchBox.DLn4sKrv.js => VPLocalSearchBox.BBhHuDzv.js} (99%) rename previews/PR135/assets/chunks/{theme.D6VlRy8Q.js => theme.Cfpb4cN3.js} (99%) create mode 100644 previews/PR135/assets/dbqxaoe.DstoYpf0.png create mode 100644 previews/PR135/assets/dkqnojp.DvNFs9H2.png create mode 100644 previews/PR135/assets/dkyghoj.DJXxaGlq.png rename previews/PR135/assets/{ugotupt.DuBHk1fh.png => dvzgimn.DuBHk1fh.png} (100%) rename previews/PR135/assets/{experiments_predicates.md.Cb_olK_t.js => experiments_predicates.md.DJ9haq0_.js} (99%) rename previews/PR135/assets/{experiments_predicates.md.Cb_olK_t.lean.js => experiments_predicates.md.DJ9haq0_.lean.js} (74%) delete mode 100644 previews/PR135/assets/fntoqls.y5I2w7nz.png create mode 100644 previews/PR135/assets/fpvgaeb.C33-xzEN.png rename previews/PR135/assets/{weygerj.mCtKcWOr.png => gabfqot.mCtKcWOr.png} (100%) rename previews/PR135/assets/{pthvojo.DiwGEg2f.png => gasrsxh.DiwGEg2f.png} (100%) rename previews/PR135/assets/{nmofpgl.DHcwB147.png => gfmgmdt.DHcwB147.png} (100%) rename previews/PR135/assets/{aithltv.C3SxJ3x-.png => hwbtkcd.C3SxJ3x-.png} (100%) rename previews/PR135/assets/{ejkzyqr.CG4dr3Lx.png => ifbqouh.CG4dr3Lx.png} (100%) rename previews/PR135/assets/{bhjiovt.BEFUMtlf.png => iitfiec.BEFUMtlf.png} (100%) rename previews/PR135/assets/{pzrdbdt.Cb0_DiYE.png => iogmviv.Cb0_DiYE.png} (100%) rename previews/PR135/assets/{erkqkyr.Danh069g.png => itchqsx.Danh069g.png} (100%) rename previews/PR135/assets/{limmhqq.Bglvb-jp.png => jdjmpmf.Bglvb-jp.png} (100%) rename previews/PR135/assets/{kgneegt.DwqDxlAG.png => jescohr.DwqDxlAG.png} (100%) rename previews/PR135/assets/{jphsfis.BD0hVfse.png => jrfxtvf.BD0hVfse.png} (100%) delete mode 100644 previews/PR135/assets/leqplng.DT95HOc4.png rename previews/PR135/assets/{gobvpyq.0OJvb21A.png => leylmwm.0OJvb21A.png} (100%) rename previews/PR135/assets/{yygelii.B94PsR1K.png => lleqqls.B94PsR1K.png} (100%) rename previews/PR135/assets/{prhmudq.-VpeHhXX.png => mcgsyqs.-VpeHhXX.png} (100%) rename previews/PR135/assets/{ltowmlk.3sfpQl2i.png => ooyleff.3sfpQl2i.png} (100%) rename previews/PR135/assets/{mqcmtly.Dab1-ETk.png => oqljina.Dab1-ETk.png} (100%) rename previews/PR135/assets/{kbchokg.CULn5saZ.png => orytpqi.CULn5saZ.png} (100%) delete mode 100644 previews/PR135/assets/oylwyba.CuwrHkOd.png rename previews/PR135/assets/{iijrfjx.Dz86q2IX.png => oyrrvdx.Dz86q2IX.png} (100%) rename previews/PR135/assets/{ztkdutf.Cx40vhB3.png => pcobipb.Cx40vhB3.png} (100%) create mode 100644 previews/PR135/assets/pqnppem.D2RAc4b6.png delete mode 100644 previews/PR135/assets/qfveqll.DPlfA76p.png rename previews/PR135/assets/{noqiyfz._0R9BbFk.png => qmgjswp._0R9BbFk.png} (100%) rename previews/PR135/assets/{prtwaxi.CZy9YIUA.png => rnrcnyd.CZy9YIUA.png} (100%) rename previews/PR135/assets/{nlfztmc.DC3TvBOO.png => scxzlgy.DC3TvBOO.png} (100%) rename previews/PR135/assets/{source_methods_angles.md.BGpTF9uY.js => source_methods_angles.md.DIrieyKk.js} (99%) rename previews/PR135/assets/{source_methods_angles.md.BGpTF9uY.lean.js => source_methods_angles.md.DIrieyKk.lean.js} (87%) rename previews/PR135/assets/{source_methods_area.md.CXVG-VeW.js => source_methods_area.md.TI4YB_Sz.js} (99%) rename previews/PR135/assets/{source_methods_area.md.CXVG-VeW.lean.js => source_methods_area.md.TI4YB_Sz.lean.js} (77%) rename previews/PR135/assets/{source_methods_barycentric.md.DiCxsA-F.js => source_methods_barycentric.md.CkNqARiG.js} (99%) rename previews/PR135/assets/{source_methods_barycentric.md.DiCxsA-F.lean.js => source_methods_barycentric.md.CkNqARiG.lean.js} (99%) rename previews/PR135/assets/{source_methods_centroid.md.CICsk7_Q.js => source_methods_centroid.md.CLmopaiG.js} (99%) rename previews/PR135/assets/{source_methods_centroid.md.CICsk7_Q.lean.js => source_methods_centroid.md.CLmopaiG.lean.js} (54%) rename previews/PR135/assets/{source_methods_clipping_coverage.md.8uUWzwQ-.js => source_methods_clipping_coverage.md.6uKDiP9y.js} (99%) rename previews/PR135/assets/{source_methods_clipping_coverage.md.8uUWzwQ-.lean.js => source_methods_clipping_coverage.md.6uKDiP9y.lean.js} (87%) rename previews/PR135/assets/{source_methods_clipping_cut.md.DC5HX6wc.js => source_methods_clipping_cut.md.CdaQF2b4.js} (99%) rename previews/PR135/assets/{source_methods_clipping_cut.md.DC5HX6wc.lean.js => source_methods_clipping_cut.md.CdaQF2b4.lean.js} (87%) rename previews/PR135/assets/{source_methods_convex_hull.md.C926w3uT.js => source_methods_convex_hull.md.C5LkdFR_.js} (99%) rename previews/PR135/assets/{source_methods_convex_hull.md.C926w3uT.lean.js => source_methods_convex_hull.md.C5LkdFR_.lean.js} (59%) rename previews/PR135/assets/{source_methods_distance.md.BNGMDaam.js => source_methods_distance.md.B9pnbS2m.js} (99%) rename previews/PR135/assets/{source_methods_distance.md.BNGMDaam.lean.js => source_methods_distance.md.B9pnbS2m.lean.js} (78%) rename previews/PR135/assets/{source_methods_equals.md.CInRsM-y.js => source_methods_equals.md.C_dUmjSr.js} (99%) rename previews/PR135/assets/{source_methods_equals.md.CInRsM-y.lean.js => source_methods_equals.md.C_dUmjSr.lean.js} (87%) rename previews/PR135/assets/{source_methods_geom_relations_contains.md.CT7VUSG_.js => source_methods_geom_relations_contains.md.7FBluCIz.js} (99%) rename previews/PR135/assets/{source_methods_geom_relations_contains.md.CT7VUSG_.lean.js => source_methods_geom_relations_contains.md.7FBluCIz.lean.js} (88%) rename previews/PR135/assets/{source_methods_geom_relations_coveredby.md.t8U8nVnZ.js => source_methods_geom_relations_coveredby.md.CpSZpBMD.js} (99%) rename previews/PR135/assets/{source_methods_geom_relations_coveredby.md.t8U8nVnZ.lean.js => source_methods_geom_relations_coveredby.md.CpSZpBMD.lean.js} (88%) rename previews/PR135/assets/{source_methods_geom_relations_covers.md.DG_3zZRD.js => source_methods_geom_relations_covers.md.DU7RNv3T.js} (99%) rename previews/PR135/assets/{source_methods_geom_relations_covers.md.DG_3zZRD.lean.js => source_methods_geom_relations_covers.md.DU7RNv3T.lean.js} (88%) rename previews/PR135/assets/{source_methods_geom_relations_disjoint.md.Cp4OC057.js => source_methods_geom_relations_disjoint.md.CdOMOhdn.js} (99%) rename previews/PR135/assets/{source_methods_geom_relations_disjoint.md.Cp4OC057.lean.js => source_methods_geom_relations_disjoint.md.CdOMOhdn.lean.js} (88%) rename previews/PR135/assets/{source_methods_geom_relations_intersects.md.DH-bN_kE.js => source_methods_geom_relations_intersects.md.-qwBxS9g.js} (99%) rename previews/PR135/assets/{source_methods_geom_relations_intersects.md.DH-bN_kE.lean.js => source_methods_geom_relations_intersects.md.-qwBxS9g.lean.js} (88%) rename previews/PR135/assets/{source_methods_geom_relations_overlaps.md.DaCX808s.js => source_methods_geom_relations_overlaps.md.4HmWrCsY.js} (99%) rename previews/PR135/assets/{source_methods_geom_relations_overlaps.md.DaCX808s.lean.js => source_methods_geom_relations_overlaps.md.4HmWrCsY.lean.js} (88%) rename previews/PR135/assets/{source_methods_geom_relations_touches.md.z4ceia7e.js => source_methods_geom_relations_touches.md.DMienZ7T.js} (99%) rename previews/PR135/assets/{source_methods_geom_relations_touches.md.z4ceia7e.lean.js => source_methods_geom_relations_touches.md.DMienZ7T.lean.js} (88%) rename previews/PR135/assets/{source_methods_geom_relations_within.md.DNx_0bB6.js => source_methods_geom_relations_within.md.CpoApCgK.js} (99%) rename previews/PR135/assets/{source_methods_geom_relations_within.md.DNx_0bB6.lean.js => source_methods_geom_relations_within.md.CpoApCgK.lean.js} (88%) rename previews/PR135/assets/{source_transformations_segmentize.md.DnUWdzb5.js => source_transformations_segmentize.md.RPr3lout.js} (99%) rename previews/PR135/assets/{source_transformations_segmentize.md.DnUWdzb5.lean.js => source_transformations_segmentize.md.RPr3lout.lean.js} (67%) rename previews/PR135/assets/{source_transformations_simplify.md.k2EFW9YY.js => source_transformations_simplify.md.Dco3qKQU.js} (99%) rename previews/PR135/assets/{source_transformations_simplify.md.k2EFW9YY.lean.js => source_transformations_simplify.md.Dco3qKQU.lean.js} (56%) rename previews/PR135/assets/{fsdxqux.Dig-DWOQ.png => stwzrcs.Dig-DWOQ.png} (100%) rename previews/PR135/assets/{zghchbn.3UVIT8DR.png => tnpgawm.3UVIT8DR.png} (100%) create mode 100644 previews/PR135/assets/tpzfygj.BZeu3Tcc.png delete mode 100644 previews/PR135/assets/tutorials_creating_geometry.md.CQNXRFYE.lean.js rename previews/PR135/assets/{tutorials_creating_geometry.md.CQNXRFYE.js => tutorials_creating_geometry.md.EcNPbH1J.js} (99%) create mode 100644 previews/PR135/assets/tutorials_creating_geometry.md.EcNPbH1J.lean.js rename previews/PR135/assets/{tutorials_geodesic_paths.md.B5A7YX9J.js => tutorials_geodesic_paths.md.B4I7D6HJ.js} (95%) rename previews/PR135/assets/{tutorials_geodesic_paths.md.B5A7YX9J.lean.js => tutorials_geodesic_paths.md.B4I7D6HJ.lean.js} (50%) rename previews/PR135/assets/{tutorials_spatial_joins.md.BU34-gKB.js => tutorials_spatial_joins.md.XXlENBfc.js} (99%) rename previews/PR135/assets/{tutorials_spatial_joins.md.BU34-gKB.lean.js => tutorials_spatial_joins.md.XXlENBfc.lean.js} (92%) create mode 100644 previews/PR135/assets/tuvqaxg.9h_7Tsnr.png delete mode 100644 previews/PR135/assets/tzjojwg.DvOK4aMR.png rename previews/PR135/assets/{piejhcv.DaovVbE6.png => ufohoag.DaovVbE6.png} (100%) rename previews/PR135/assets/{hsawanf.CLtpJ5Wb.png => vvbiyov.CLtpJ5Wb.png} (100%) rename previews/PR135/assets/{afsyzdr.BOOG5oTW.png => vxuhcsb.BOOG5oTW.png} (100%) delete mode 100644 previews/PR135/assets/xqlfxjv.AayN5YeI.png rename previews/PR135/assets/{xheurnn.B9NpLJr_.png => znwtliz.B9NpLJr_.png} (100%) create mode 100644 previews/PR135/assets/zstdwff.C3FlfbBb.png diff --git a/previews/PR135/404.html b/previews/PR135/404.html index 1e7f79a52..69b9677f0 100644 --- a/previews/PR135/404.html +++ b/previews/PR135/404.html @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@
- + \ No newline at end of file diff --git a/previews/PR135/api.html b/previews/PR135/api.html index 7fb6d0aac..1ffdf9a02 100644 --- a/previews/PR135/api.html +++ b/previews/PR135/api.html @@ -8,17 +8,17 @@ - + - - + + -
Skip to content

Full GeometryOps API documentation

Warning

This page is still very much WIP!

Documentation for GeometryOps's full API (only for reference!).

apply and associated functions

# GeometryOps.applyFunction.
julia
apply(f, target::Union{TraitTarget, GI.AbstractTrait}, obj; kw...)

Reconstruct a geometry, feature, feature collection, or nested vectors of either using the function f on the target trait.

f(target_geom) => x where x also has the target trait, or a trait that can be substituted. For example, swapping PolgonTrait to MultiPointTrait will fail if the outer object has MultiPolygonTrait, but should work if it has FeatureTrait.

Objects "shallower" than the target trait are always completely rebuilt, like a Vector of FeatureCollectionTrait of FeatureTrait when the target has PolygonTrait and is held in the features. These will always be GeoInterface geometries/feature/feature collections. But "deeper" objects may remain unchanged or be whatever GeoInterface compatible objects f returns.

The result is a functionally similar geometry with values depending on f.

  • threaded: true or false. Whether to use multithreading. Defaults to false.

  • crs: The CRS to attach to geometries. Defaults to nothing.

  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.

Example

Flipped point the order in any feature or geometry, or iterables of either:

julia
import GeoInterface as GI
+    
Skip to content

Full GeometryOps API documentation

Warning

This page is still very much WIP!

Documentation for GeometryOps's full API (only for reference!).

apply and associated functions

# GeometryOps.applyFunction.
julia
apply(f, target::Union{TraitTarget, GI.AbstractTrait}, obj; kw...)

Reconstruct a geometry, feature, feature collection, or nested vectors of either using the function f on the target trait.

f(target_geom) => x where x also has the target trait, or a trait that can be substituted. For example, swapping PolgonTrait to MultiPointTrait will fail if the outer object has MultiPolygonTrait, but should work if it has FeatureTrait.

Objects "shallower" than the target trait are always completely rebuilt, like a Vector of FeatureCollectionTrait of FeatureTrait when the target has PolygonTrait and is held in the features. These will always be GeoInterface geometries/feature/feature collections. But "deeper" objects may remain unchanged or be whatever GeoInterface compatible objects f returns.

The result is a functionally similar geometry with values depending on f.

  • threaded: true or false. Whether to use multithreading. Defaults to false.

  • crs: The CRS to attach to geometries. Defaults to nothing.

  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.

Example

Flipped point the order in any feature or geometry, or iterables of either:

julia
import GeoInterface as GI
 import GeometryOps as GO
 geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]),
                    GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])])
@@ -416,7 +416,7 @@
 
 # output
 true

source



  1. K. Hormann and N. Sukumar. Generalized Barycentric Coordinates in Computer Graphics and Computational Mechanics. Taylor & Fancis, CRC Press, 2017. ↩︎

- + \ No newline at end of file diff --git a/previews/PR135/assets/api.md.BkdCNSpp.js b/previews/PR135/assets/api.md.9bKu-Iv9.js similarity index 99% rename from previews/PR135/assets/api.md.BkdCNSpp.js rename to previews/PR135/assets/api.md.9bKu-Iv9.js index 3c39cf685..494a10a0c 100644 --- a/previews/PR135/assets/api.md.BkdCNSpp.js +++ b/previews/PR135/assets/api.md.9bKu-Iv9.js @@ -1,4 +1,4 @@ -import{_ as n,c as e,j as s,a,a7 as i,o as t}from"./chunks/framework.CONf0Rze.js";const q=JSON.parse('{"title":"Full GeometryOps API documentation","description":"","frontmatter":{},"headers":[],"relativePath":"api.md","filePath":"api.md","lastUpdated":null}'),l={name:"api.md"},h=i(`

Full GeometryOps API documentation

Warning

This page is still very much WIP!

Documentation for GeometryOps's full API (only for reference!).

apply and associated functions

# GeometryOps.applyFunction.
julia
apply(f, target::Union{TraitTarget, GI.AbstractTrait}, obj; kw...)

Reconstruct a geometry, feature, feature collection, or nested vectors of either using the function f on the target trait.

f(target_geom) => x where x also has the target trait, or a trait that can be substituted. For example, swapping PolgonTrait to MultiPointTrait will fail if the outer object has MultiPolygonTrait, but should work if it has FeatureTrait.

Objects "shallower" than the target trait are always completely rebuilt, like a Vector of FeatureCollectionTrait of FeatureTrait when the target has PolygonTrait and is held in the features. These will always be GeoInterface geometries/feature/feature collections. But "deeper" objects may remain unchanged or be whatever GeoInterface compatible objects f returns.

The result is a functionally similar geometry with values depending on f.

  • threaded: true or false. Whether to use multithreading. Defaults to false.

  • crs: The CRS to attach to geometries. Defaults to nothing.

  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.

Example

Flipped point the order in any feature or geometry, or iterables of either:

julia
import GeoInterface as GI
+import{_ as n,c as e,j as s,a,a7 as i,o as t}from"./chunks/framework.CONf0Rze.js";const q=JSON.parse('{"title":"Full GeometryOps API documentation","description":"","frontmatter":{},"headers":[],"relativePath":"api.md","filePath":"api.md","lastUpdated":null}'),l={name:"api.md"},h=i(`

Full GeometryOps API documentation

Warning

This page is still very much WIP!

Documentation for GeometryOps's full API (only for reference!).

apply and associated functions

# GeometryOps.applyFunction.
julia
apply(f, target::Union{TraitTarget, GI.AbstractTrait}, obj; kw...)

Reconstruct a geometry, feature, feature collection, or nested vectors of either using the function f on the target trait.

f(target_geom) => x where x also has the target trait, or a trait that can be substituted. For example, swapping PolgonTrait to MultiPointTrait will fail if the outer object has MultiPolygonTrait, but should work if it has FeatureTrait.

Objects "shallower" than the target trait are always completely rebuilt, like a Vector of FeatureCollectionTrait of FeatureTrait when the target has PolygonTrait and is held in the features. These will always be GeoInterface geometries/feature/feature collections. But "deeper" objects may remain unchanged or be whatever GeoInterface compatible objects f returns.

The result is a functionally similar geometry with values depending on f.

  • threaded: true or false. Whether to use multithreading. Defaults to false.

  • crs: The CRS to attach to geometries. Defaults to nothing.

  • calc_extent: true or false. Whether to calculate the extent. Defaults to false.

Example

Flipped point the order in any feature or geometry, or iterables of either:

julia
import GeoInterface as GI
 import GeometryOps as GO
 geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]),
                    GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])])
diff --git a/previews/PR135/assets/api.md.BkdCNSpp.lean.js b/previews/PR135/assets/api.md.9bKu-Iv9.lean.js
similarity index 100%
rename from previews/PR135/assets/api.md.BkdCNSpp.lean.js
rename to previews/PR135/assets/api.md.9bKu-Iv9.lean.js
diff --git a/previews/PR135/assets/app.okziLJoA.js b/previews/PR135/assets/app.CNviBygr.js
similarity index 95%
rename from previews/PR135/assets/app.okziLJoA.js
rename to previews/PR135/assets/app.CNviBygr.js
index 9f514c9f8..f0ff81d99 100644
--- a/previews/PR135/assets/app.okziLJoA.js
+++ b/previews/PR135/assets/app.CNviBygr.js
@@ -1 +1 @@
-import{U as o,a8 as p,a9 as u,aa as l,ab as c,ac as f,ad as d,ae as m,af as h,ag as g,ah as A,d as P,u as v,y,x as w,ai as C,aj as R,ak as b,a6 as E}from"./chunks/framework.CONf0Rze.js";import{R as S}from"./chunks/theme.D6VlRy8Q.js";function i(e){if(e.extends){const a=i(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const s=i(S),T=P({name:"VitePressApp",setup(){const{site:e,lang:a,dir:t}=v();return y(()=>{w(()=>{document.documentElement.lang=a.value,document.documentElement.dir=t.value})}),e.value.router.prefetchLinks&&C(),R(),b(),s.setup&&s.setup(),()=>E(s.Layout)}});async function _(){globalThis.__VITEPRESS__=!0;const e=x(),a=j();a.provide(u,e);const t=l(e.route);return a.provide(c,t),a.component("Content",f),a.component("ClientOnly",d),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),s.enhanceApp&&await s.enhanceApp({app:a,router:e,siteData:m}),{app:a,router:e,data:t}}function j(){return h(T)}function x(){let e=o,a;return g(t=>{let n=A(t),r=null;return n&&(e&&(a=n),(e||a===n)&&(n=n.replace(/\.js$/,".lean.js")),r=import(n)),o&&(e=!1),r},s.NotFound)}o&&_().then(({app:e,router:a,data:t})=>{a.go().then(()=>{p(a.route,t.site),e.mount("#app")})});export{_ as createApp};
+import{U as o,a8 as p,a9 as u,aa as l,ab as c,ac as f,ad as d,ae as m,af as h,ag as g,ah as A,d as P,u as v,y,x as w,ai as C,aj as R,ak as b,a6 as E}from"./chunks/framework.CONf0Rze.js";import{R as S}from"./chunks/theme.Cfpb4cN3.js";function i(e){if(e.extends){const a=i(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const s=i(S),T=P({name:"VitePressApp",setup(){const{site:e,lang:a,dir:t}=v();return y(()=>{w(()=>{document.documentElement.lang=a.value,document.documentElement.dir=t.value})}),e.value.router.prefetchLinks&&C(),R(),b(),s.setup&&s.setup(),()=>E(s.Layout)}});async function _(){globalThis.__VITEPRESS__=!0;const e=x(),a=j();a.provide(u,e);const t=l(e.route);return a.provide(c,t),a.component("Content",f),a.component("ClientOnly",d),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),s.enhanceApp&&await s.enhanceApp({app:a,router:e,siteData:m}),{app:a,router:e,data:t}}function j(){return h(T)}function x(){let e=o,a;return g(t=>{let n=A(t),r=null;return n&&(e&&(a=n),(e||a===n)&&(n=n.replace(/\.js$/,".lean.js")),r=import(n)),o&&(e=!1),r},s.NotFound)}o&&_().then(({app:e,router:a,data:t})=>{a.go().then(()=>{p(a.route,t.site),e.mount("#app")})});export{_ as createApp};
diff --git a/previews/PR135/assets/ftxvlaw.CgiryX2p.png b/previews/PR135/assets/aqvzuly.CgiryX2p.png
similarity index 100%
rename from previews/PR135/assets/ftxvlaw.CgiryX2p.png
rename to previews/PR135/assets/aqvzuly.CgiryX2p.png
diff --git a/previews/PR135/assets/rezvvce.DeeQUply.png b/previews/PR135/assets/auepdne.DeeQUply.png
similarity index 100%
rename from previews/PR135/assets/rezvvce.DeeQUply.png
rename to previews/PR135/assets/auepdne.DeeQUply.png
diff --git a/previews/PR135/assets/briiybs.Cy4gx45x.png b/previews/PR135/assets/briiybs.Cy4gx45x.png
deleted file mode 100644
index 74d2b4aec55c84d6d9c7e9c7e0f28760425000fa..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 73994
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
- + \ No newline at end of file diff --git a/previews/PR135/experiments/predicates.html b/previews/PR135/experiments/predicates.html index ce399af96..2c0cdc160 100644 --- a/previews/PR135/experiments/predicates.html +++ b/previews/PR135/experiments/predicates.html @@ -8,11 +8,11 @@ - + - - + + @@ -59,7 +59,7 @@ display(fig) end resize!(fig, 1000, 450) -fig

Dashboard

julia
using WGLMakie
+fig

Dashboard

julia
using WGLMakie
 import GeometryOps as GO, GeoInterface as GI, LibGEOS as LG
 import ExactPredicates
 using MultiFloats
@@ -116,7 +116,7 @@
 
 GO.intersection(p1_m, p2_m; target = GI.PolygonTrait(), fix_multipoly = nothing)
 LG.intersection(p1_m, p2_m)

Incircle

- + \ No newline at end of file diff --git a/previews/PR135/explanations/crs.html b/previews/PR135/explanations/crs.html index ed1af7c86..506d7f418 100644 --- a/previews/PR135/explanations/crs.html +++ b/previews/PR135/explanations/crs.html @@ -8,10 +8,10 @@ - + - + @@ -19,7 +19,7 @@
Skip to content
- + \ No newline at end of file diff --git a/previews/PR135/explanations/paradigms.html b/previews/PR135/explanations/paradigms.html index 03a8d3a35..c806d57fe 100644 --- a/previews/PR135/explanations/paradigms.html +++ b/previews/PR135/explanations/paradigms.html @@ -8,10 +8,10 @@ - + - + @@ -19,7 +19,7 @@
Skip to content

Paradigms

GeometryOps exposes functions like apply and applyreduce, as well as the fix and prepare APIs, that represent paradigms of programming, by which we mean the ability to program in a certain way, and in so doing, fit neatly into the tools we've built without needing to re-implement the wheel.

Below, we'll describe some of the foundational paradigms of GeometryOps, and why you should care!

apply

The apply function allows you to decompose a given collection of geometries down to a certain level, operate on it, and reconstruct it back to the same nested form as the original. In general, its invocation is:

julia
apply(f, trait::Trait, geom)

Functionally, it's similar to map in the way you apply it to geometries - except that you tell it at which level it should stop, by passing a trait to it.

apply will start by decomposing the geometry, feature, featurecollection, iterable, or table that you pass to it, and stop when it encounters a geometry for which GI.trait(geom) isa Trait. This encompasses unions of traits especially, but beware that any geometry which is not explicitly handled, and hits GI.PointTrait, will cause an error.

apply is unlike map in that it returns reconstructed geometries, instead of the raw output of the function. If you want a purely map-like behaviour, like calculating the length of each linestring in your feature collection, then call GO.flatten(f, trait, geom), which will decompose each geometry to the given trait and apply f to it, returning the decomposition as a flattened vector.

applyreduce

applyreduce is like the previous map-based approach that we mentioned, except that it reduces the result of f by op. Note that applyreduce does not guarantee associativity, so it's best to have typeof(init) == returntype(op).

fix and prepare

The fix and prepare paradigms are different from apply, though they are built on top of it. They involve the use of structs as "actions", where a constructed object indicates an action that should be taken. A trait like interface prescribes the level (polygon, linestring, point, etc) at which each action should be applied.

In general, the idea here is to be able to invoke several actions efficiently and simultaneously, for example when correcting invalid geometries, or instantiating a Prepared geometry with several preparations (sorted edge lists, rtrees, monotone chains, etc.)

- + \ No newline at end of file diff --git a/previews/PR135/explanations/peculiarities.html b/previews/PR135/explanations/peculiarities.html index f8073af94..38b8ecb17 100644 --- a/previews/PR135/explanations/peculiarities.html +++ b/previews/PR135/explanations/peculiarities.html @@ -8,10 +8,10 @@ - + - + @@ -19,7 +19,7 @@
Skip to content

Peculiarities

What does apply return and why?

apply returns the target geometries returned by f, whatever type/package they are from, but geometries, features or feature collections that wrapped the target are replaced with GeoInterace.jl wrappers with matching GeoInterface.trait to the originals. All non-geointerface iterables become Arrays. Tables.jl compatible tables are converted either back to the original type if a Tables.materializer is defined, and if not then returned as generic NamedTuple column tables (i.e., a NamedTuple of vectors).

It is recommended for consistency that f returns GeoInterface geometries unless there is a performance/conversion overhead to doing that.

Why do you want me to provide a target in set operations?

In polygon set operations like intersection, difference, and union, many different geometry types may be obtained - depending on the relationship between the polygons. For example, when performing an union on two nonintersecting polygons, one would technically have two disjoint polygons as an output.

We use the target keyword to allow the user to control which kinds of geometry they want back. For example, setting target to PolygonTrait will cause a vector of polygons to be returned (this is the only currently supported behaviour). In future, we may implement MultiPolygonTrait or GeometryCollectionTrait targets which will return a single geometry, as LibGEOS and ArchGDAL do.

This also allows for a lot more type stability - when you ask for polygons, we won't return a geometrycollection with line segments. Especially in simulation workflows, this is excellent for simplified data processing.

_True and _False (or BoolsAsTypes)

Warning

These are internals and explicitly not public API, meaning they may change at any time!

When dispatch can be controlled by the value of a boolean variable, this introduces type instability. Instead of introducing type instability, we chose to encode our boolean decision variables, like threaded and calc_extent in apply, as types. This allows the compiler to reason about what will happen, and call the correct compiled method, in a stable way without worrying about

- + \ No newline at end of file diff --git a/previews/PR135/explanations/winding_order.html b/previews/PR135/explanations/winding_order.html index e10cc5acb..a225c1a74 100644 --- a/previews/PR135/explanations/winding_order.html +++ b/previews/PR135/explanations/winding_order.html @@ -8,10 +8,10 @@ - + - + @@ -19,7 +19,7 @@
Skip to content
- + \ No newline at end of file diff --git a/previews/PR135/hashmap.json b/previews/PR135/hashmap.json index ac971aa32..158b22b5b 100644 --- a/previews/PR135/hashmap.json +++ b/previews/PR135/hashmap.json @@ -1 +1 @@ -{"api.md":"BkdCNSpp","call_notes.md":"CzI2sRnh","experiments_accurate_accumulators.md":"BEvtokUD","experiments_predicates.md":"Cb_olK_t","explanations_crs.md":"BcB0kXKu","explanations_paradigms.md":"BQbTsKKW","explanations_peculiarities.md":"D7wlShpB","explanations_winding_order.md":"BzkvsLOj","index.md":"Dm7lK_c9","introduction.md":"EstDN9hf","source_geometryops.md":"BnVY2YxC","source_methods_angles.md":"BGpTF9uY","source_methods_area.md":"CXVG-VeW","source_methods_barycentric.md":"DiCxsA-F","source_methods_buffer.md":"CxkGeu_t","source_methods_centroid.md":"CICsk7_Q","source_methods_clipping_clipping_processor.md":"5X_cs7CX","source_methods_clipping_coverage.md":"8uUWzwQ-","source_methods_clipping_cut.md":"DC5HX6wc","source_methods_clipping_difference.md":"BlomfT6l","source_methods_clipping_intersection.md":"fOblypzx","source_methods_clipping_predicates.md":"DUY6Mg1n","source_methods_clipping_union.md":"BUlTcf5A","source_methods_convex_hull.md":"C926w3uT","source_methods_distance.md":"BNGMDaam","source_methods_equals.md":"CInRsM-y","source_methods_geom_relations_contains.md":"CT7VUSG_","source_methods_geom_relations_coveredby.md":"t8U8nVnZ","source_methods_geom_relations_covers.md":"DG_3zZRD","source_methods_geom_relations_crosses.md":"5YXBFQwW","source_methods_geom_relations_disjoint.md":"Cp4OC057","source_methods_geom_relations_geom_geom_processors.md":"B09zbe43","source_methods_geom_relations_intersects.md":"DH-bN_kE","source_methods_geom_relations_overlaps.md":"DaCX808s","source_methods_geom_relations_touches.md":"z4ceia7e","source_methods_geom_relations_within.md":"DNx_0bB6","source_methods_orientation.md":"C6hml0DY","source_methods_polygonize.md":"BjnTo1d_","source_not_implemented_yet.md":"DttPeX-2","source_primitives.md":"4UCUXvfy","source_transformations_correction_closed_ring.md":"CohSwGqX","source_transformations_correction_geometry_correction.md":"gAjUHu5U","source_transformations_correction_intersecting_polygons.md":"Dp-0YCkQ","source_transformations_extent.md":"aqCZuaCr","source_transformations_flip.md":"D247Ejcp","source_transformations_reproject.md":"plsPtFYv","source_transformations_segmentize.md":"DnUWdzb5","source_transformations_simplify.md":"k2EFW9YY","source_transformations_transform.md":"Bz0uRhiV","source_transformations_tuples.md":"BlsK6oDR","source_types.md":"D4PahPU_","source_utils.md":"CZ0t5hCA","tutorials_creating_geometry.md":"CQNXRFYE","tutorials_geodesic_paths.md":"B5A7YX9J","tutorials_spatial_joins.md":"BU34-gKB"} +{"api.md":"9bKu-Iv9","call_notes.md":"CzI2sRnh","experiments_accurate_accumulators.md":"BEvtokUD","experiments_predicates.md":"DJ9haq0_","explanations_crs.md":"BcB0kXKu","explanations_paradigms.md":"BQbTsKKW","explanations_peculiarities.md":"D7wlShpB","explanations_winding_order.md":"BzkvsLOj","index.md":"Dm7lK_c9","introduction.md":"EstDN9hf","source_geometryops.md":"BnVY2YxC","source_methods_angles.md":"DIrieyKk","source_methods_area.md":"TI4YB_Sz","source_methods_barycentric.md":"CkNqARiG","source_methods_buffer.md":"CxkGeu_t","source_methods_centroid.md":"CLmopaiG","source_methods_clipping_clipping_processor.md":"5X_cs7CX","source_methods_clipping_coverage.md":"6uKDiP9y","source_methods_clipping_cut.md":"CdaQF2b4","source_methods_clipping_difference.md":"BlomfT6l","source_methods_clipping_intersection.md":"fOblypzx","source_methods_clipping_predicates.md":"DUY6Mg1n","source_methods_clipping_union.md":"BUlTcf5A","source_methods_convex_hull.md":"C5LkdFR_","source_methods_distance.md":"B9pnbS2m","source_methods_equals.md":"C_dUmjSr","source_methods_geom_relations_contains.md":"7FBluCIz","source_methods_geom_relations_coveredby.md":"CpSZpBMD","source_methods_geom_relations_covers.md":"DU7RNv3T","source_methods_geom_relations_crosses.md":"5YXBFQwW","source_methods_geom_relations_disjoint.md":"CdOMOhdn","source_methods_geom_relations_geom_geom_processors.md":"B09zbe43","source_methods_geom_relations_intersects.md":"-qwBxS9g","source_methods_geom_relations_overlaps.md":"4HmWrCsY","source_methods_geom_relations_touches.md":"DMienZ7T","source_methods_geom_relations_within.md":"CpoApCgK","source_methods_orientation.md":"C6hml0DY","source_methods_polygonize.md":"BjnTo1d_","source_not_implemented_yet.md":"DttPeX-2","source_primitives.md":"4UCUXvfy","source_transformations_correction_closed_ring.md":"CohSwGqX","source_transformations_correction_geometry_correction.md":"gAjUHu5U","source_transformations_correction_intersecting_polygons.md":"Dp-0YCkQ","source_transformations_extent.md":"aqCZuaCr","source_transformations_flip.md":"D247Ejcp","source_transformations_reproject.md":"plsPtFYv","source_transformations_segmentize.md":"RPr3lout","source_transformations_simplify.md":"Dco3qKQU","source_transformations_transform.md":"Bz0uRhiV","source_transformations_tuples.md":"BlsK6oDR","source_types.md":"D4PahPU_","source_utils.md":"CZ0t5hCA","tutorials_creating_geometry.md":"EcNPbH1J","tutorials_geodesic_paths.md":"B4I7D6HJ","tutorials_spatial_joins.md":"XXlENBfc"} diff --git a/previews/PR135/index.html b/previews/PR135/index.html index 1cae6e0c6..fd16e96db 100644 --- a/previews/PR135/index.html +++ b/previews/PR135/index.html @@ -8,10 +8,10 @@ - + - + @@ -19,7 +19,7 @@
Skip to content

GeometryOps.jl

Blazing fast geometry operations in pure Julia

GeometryOps

What is GeometryOps.jl?

GeometryOps.jl is a package for geometric calculations on (primarily 2D) geometries.

The driving idea behind this package is to unify all the disparate packages for geometric calculations in Julia, and make them GeoInterface.jl-compatible. We seem to be focusing primarily on 2/2.5D geometries for now.

Most of the usecases are driven by GIS and similar Earth data workflows, so this might be a bit specialized towards that, but methods should always be general to any coordinate space.

We welcome contributions, either as pull requests or discussion on issues!

How to navigate the docs

GeometryOps' docs are divided into three main sections: tutorials, explanations and source code.
Documentation and examples for many functions can be found in the source code section, since we use literate programming in GeometryOps.

  • Tutorials are meant to teach the fundamental concepts behind GeometryOps, and how to perform certain operations.
  • Explanations usually contain little code, and explain in more detail how GeometryOps works.
  • Source code usually contains explanations and examples at the top of the page, followed by annotated source code from that file.
- + \ No newline at end of file diff --git a/previews/PR135/introduction.html b/previews/PR135/introduction.html index f527cf1ab..59bd26f4f 100644 --- a/previews/PR135/introduction.html +++ b/previews/PR135/introduction.html @@ -8,10 +8,10 @@ - + - + @@ -19,7 +19,7 @@
Skip to content

Introduction

GeometryOps.jl is a package for geometric calculations on (primarily 2D) geometries.

The driving idea behind this package is to unify all the disparate packages for geometric calculations in Julia, and make them GeoInterface.jl-compatible. We seem to be focusing primarily on 2/2.5D geometries for now.

Most of the usecases are driven by GIS and similar Earth data workflows, so this might be a bit specialized towards that, but methods should always be general to any coordinate space.

We welcome contributions, either as pull requests or discussion on issues!

Main concepts

The apply paradigm

Note

See the Primitive Functions page for more information on this.

The apply function allows you to decompose a given collection of geometries down to a certain level, and then operate on it.

Functionally, it's similar to map in the way you apply it to geometries.

apply and applyreduce take any geometry, vector of geometries, collection of geometries, or table (like Shapefile.Table, DataFrame, or GeoTable)!

What's this GeoInterface.Wrapper thing?

Write a comment about GeoInterface.Wrapper and why it helps in type stability to guarantee a particular return type.

- + \ No newline at end of file diff --git a/previews/PR135/source/GeometryOps.html b/previews/PR135/source/GeometryOps.html index 330b94615..506153ed9 100644 --- a/previews/PR135/source/GeometryOps.html +++ b/previews/PR135/source/GeometryOps.html @@ -8,10 +8,10 @@ - + - + @@ -92,7 +92,7 @@ end end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/angles.html b/previews/PR135/source/methods/angles.html index 47088b403..9e9107638 100644 --- a/previews/PR135/source/methods/angles.html +++ b/previews/PR135/source/methods/angles.html @@ -8,11 +8,11 @@ - + - - + + @@ -23,7 +23,7 @@ using Makie, CairoMakie rect = GI.Polygon([[(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)]]) -f, a, p = poly(collect(GI.getpoint(rect)); axis = (; aspect = DataAspect()))

This is clearly a rectangle, with angles of 90 degrees.

julia
GO.angles(rect)  # [90, 90, 90, 90]
4-element Vector{Float64}:
+f, a, p = poly(collect(GI.getpoint(rect)); axis = (; aspect = DataAspect()))

This is clearly a rectangle, with angles of 90 degrees.

julia
GO.angles(rect)  # [90, 90, 90, 90]
4-element Vector{Float64}:
  90.0
  90.0
  90.0
@@ -142,7 +142,7 @@
     angle = real(acos(val) * 180 / π)
     return angle * (cross_prod < 0 ? -1 : 1)
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/area.html b/previews/PR135/source/methods/area.html index 32023425c..beba73747 100644 --- a/previews/PR135/source/methods/area.html +++ b/previews/PR135/source/methods/area.html @@ -8,11 +8,11 @@ - + - - + + @@ -24,10 +24,10 @@ using CairoMakie rect = GI.Polygon([[(0,0), (0,1), (1,1), (1,0), (0, 0)]]) -f, a, p = poly(collect(GI.getpoint(rect)); axis = (; aspect = DataAspect()))

This is clearly a rectangle, etc. But now let's look at how the points look:

julia
lines!(
+f, a, p = poly(collect(GI.getpoint(rect)); axis = (; aspect = DataAspect()))

This is clearly a rectangle, etc. But now let's look at how the points look:

julia
lines!(
     collect(GI.getpoint(rect));
     color = 1:GI.npoint(rect), linewidth = 10.0)
-f

The points are ordered in a counterclockwise fashion, which means that the signed area is negative. If we reverse the order of the points, we get a positive area.

julia
GO.signed_area(rect)  # -1.0
-1.0

Implementation

This is the GeoInterface-compatible implementation. First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Note that area and signed area are zero for all points and curves, even if the curves are closed like with a linear ring. Also note that signed area really only makes sense for polygons, given with a multipolygon can have several polygons each with a different orientation and thus the absolute value of the signed area might not be the area. This is why signed area is only implemented for polygons.

Targets for applys functions

julia
const _AREA_TARGETS = TraitTarget{Union{GI.PolygonTrait,GI.AbstractCurveTrait,GI.MultiPointTrait,GI.PointTrait}}()
+f

The points are ordered in a counterclockwise fashion, which means that the signed area is negative. If we reverse the order of the points, we get a positive area.

julia
GO.signed_area(rect)  # -1.0
-1.0

Implementation

This is the GeoInterface-compatible implementation. First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Note that area and signed area are zero for all points and curves, even if the curves are closed like with a linear ring. Also note that signed area really only makes sense for polygons, given with a multipolygon can have several polygons each with a different orientation and thus the absolute value of the signed area might not be the area. This is why signed area is only implemented for polygons.

Targets for applys functions

julia
const _AREA_TARGETS = TraitTarget{Union{GI.PolygonTrait,GI.AbstractCurveTrait,GI.MultiPointTrait,GI.PointTrait}}()
 
 """
     area(geom, [T = Float64])::T
@@ -105,7 +105,7 @@
     area += _area_component(p1, p2)
     return T(area / 2)
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/barycentric.html b/previews/PR135/source/methods/barycentric.html index 16ccc02c6..d2502c900 100644 --- a/previews/PR135/source/methods/barycentric.html +++ b/previews/PR135/source/methods/barycentric.html @@ -8,11 +8,11 @@ - + - - + + @@ -85,7 +85,7 @@ # and render! hm = heatmap!(a2, xrange, yrange, mean_values; colormap = p1.colormap, colorrange = p1.plots[1].colorrange[], xautolimits = false, yautolimits = false) translate!(hm, 0, 0, -1) # translate the heatmap behind the cropping polygon! -f # finally, display the figure

Barycentric-coordinate API

In some cases, we actually want barycentric interpolation, and have no interest in the coordinates themselves.

However, the coordinates can be useful for debugging, and when performing 3D rendering, multiple barycentric values (depth, uv) are needed for depth buffering.

julia
const _VecTypes = Union{Tuple{Vararg{T, N}}, GeometryBasics.StaticArraysCore.StaticArray{Tuple{N}, T, 1}} where {N, T}
+f # finally, display the figure

Barycentric-coordinate API

In some cases, we actually want barycentric interpolation, and have no interest in the coordinates themselves.

However, the coordinates can be useful for debugging, and when performing 3D rendering, multiple barycentric values (depth, uv) are needed for depth buffering.

julia
const _VecTypes = Union{Tuple{Vararg{T, N}}, GeometryBasics.StaticArraysCore.StaticArray{Tuple{N}, T, 1}} where {N, T}
 
 """
     abstract type AbstractBarycentricCoordinateMethod
@@ -433,7 +433,7 @@
 
 struct Wachspress <: AbstractBarycentricCoordinateMethod
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/buffer.html b/previews/PR135/source/methods/buffer.html index 7d902abe6..aef5afda4 100644 --- a/previews/PR135/source/methods/buffer.html +++ b/previews/PR135/source/methods/buffer.html @@ -8,10 +8,10 @@ - + - + @@ -29,7 +29,7 @@ println(io, " in your REPL, \nor otherwise loading LibGEOS.jl via using or import.") end end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/centroid.html b/previews/PR135/source/methods/centroid.html index 0ef85bf77..d33b14f63 100644 --- a/previews/PR135/source/methods/centroid.html +++ b/previews/PR135/source/methods/centroid.html @@ -8,11 +8,11 @@ - + - - + + @@ -24,9 +24,9 @@ using CairoMakie cshape = GI.Polygon([[(0,0), (0,3), (3,3), (3,2), (1,2), (1,1), (3,1), (3,0), (0,0)]]) -f, a, p = poly(collect(GI.getpoint(cshape)); axis = (; aspect = DataAspect()))

Let's see what the centroid looks like (plotted in red):

julia
cent = GO.centroid(cshape)
+f, a, p = poly(collect(GI.getpoint(cshape)); axis = (; aspect = DataAspect()))

Let's see what the centroid looks like (plotted in red):

julia
cent = GO.centroid(cshape)
 scatter!(GI.x(cent), GI.y(cent), color = :red)
-f

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Note that if you call centroid on a LineString or LinearRing, the centroid_and_length function will be called due to the weighting scheme described above, while centroid_and_area is called for polygons and multipolygons. However, centroid_and_area can still be called on a LineString or LinearRing when they are closed, for example as the interior hole of a polygon.

The helper functions centroid_and_length and centroid_and_area are made available just in case the user also needs the area or length to decrease repeat computation.

julia
"""
+f

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Note that if you call centroid on a LineString or LinearRing, the centroid_and_length function will be called due to the weighting scheme described above, while centroid_and_area is called for polygons and multipolygons. However, centroid_and_area can still be called on a LineString or LinearRing when they are closed, for example as the interior hole of a polygon.

The helper functions centroid_and_length and centroid_and_area are made available just in case the user also needs the area or length to decrease repeat computation.

julia
"""
     centroid(geom, [T=Float64])::Tuple{T, T}
 
 Returns the centroid of a given line segment, linear ring, polygon, or
@@ -111,7 +111,7 @@
     y = (y1 * area1 + y2 * area2) / area
     return (x, y), area
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/clipping/clipping_processor.html b/previews/PR135/source/methods/clipping/clipping_processor.html index 41e8bdb74..1eb198206 100644 --- a/previews/PR135/source/methods/clipping/clipping_processor.html +++ b/previews/PR135/source/methods/clipping/clipping_processor.html @@ -8,10 +8,10 @@ - + - + @@ -526,7 +526,7 @@ end return end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/clipping/coverage.html b/previews/PR135/source/methods/clipping/coverage.html index 8365f9207..a658f921e 100644 --- a/previews/PR135/source/methods/clipping/coverage.html +++ b/previews/PR135/source/methods/clipping/coverage.html @@ -8,11 +8,11 @@ - + - - + + @@ -28,7 +28,7 @@ xmin, xmax, ymin, ymax = 0, 2, 0, 2 f, a, p = poly(collect(GI.getpoint(cell)); axis = (; aspect = DataAspect())) poly!(collect(GI.getpoint(rect))) -f

It is clear that half of the polygon is within the cell, so the coverage should be 1.0, half of the area of the rectangle.

julia
GO.coverage(rect, xmin, xmax, ymin, ymax)
1.0

Implementation

This is the GeoInterface-compatible implementation. First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Note that the coverage is zero for all points and curves, even if the curves are closed like with a linear ring.

Targets for applys functions

julia
const _COVERAGE_TARGETS = TraitTarget{Union{GI.PolygonTrait,GI.AbstractCurveTrait,GI.MultiPointTrait,GI.PointTrait}}()

Wall types for coverage

julia
const UNKNOWN, NORTH, EAST, SOUTH, WEST = 0:4
+f

It is clear that half of the polygon is within the cell, so the coverage should be 1.0, half of the area of the rectangle.

julia
GO.coverage(rect, xmin, xmax, ymin, ymax)
1.0

Implementation

This is the GeoInterface-compatible implementation. First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Note that the coverage is zero for all points and curves, even if the curves are closed like with a linear ring.

Targets for applys functions

julia
const _COVERAGE_TARGETS = TraitTarget{Union{GI.PolygonTrait,GI.AbstractCurveTrait,GI.MultiPointTrait,GI.PointTrait}}()

Wall types for coverage

julia
const UNKNOWN, NORTH, EAST, SOUTH, WEST = 0:4
 
 """
     coverage(geom, xmin, xmax, ymin, ymax, [T = Float64])::T
@@ -241,7 +241,7 @@
     y_wall = (wall == NORTH || wall == WEST) ? ymax : ymin
     return x1 * y_wall - x_wall * y1
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/clipping/cut.html b/previews/PR135/source/methods/clipping/cut.html index 1c2c9cf1d..fa3089176 100644 --- a/previews/PR135/source/methods/clipping/cut.html +++ b/previews/PR135/source/methods/clipping/cut.html @@ -8,11 +8,11 @@ - + - - + + @@ -29,7 +29,7 @@ f, a, p1 = Makie.poly(collect(GI.getpoint(cut_polys[1])); color = (:blue, 0.5)) Makie.poly!(collect(GI.getpoint(cut_polys[2])); color = (:orange, 0.5)) Makie.lines!(GI.getpoint(line); color = :black) -f

Implementation

This function depends on polygon clipping helper function and is inspired by the Greiner-Hormann clipping algorithm used elsewhere in this library. The inspiration came from this Stack Overflow discussion.

julia
"""
+f

Implementation

This function depends on polygon clipping helper function and is inspired by the Greiner-Hormann clipping algorithm used elsewhere in this library. The inspiration came from this Stack Overflow discussion.

julia
"""
     cut(geom, line, [T::Type])
 
 Return given geom cut by given line as a list of geometries of the same type as the input
@@ -105,7 +105,7 @@
     end
     return return_coords
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/clipping/difference.html b/previews/PR135/source/methods/clipping/difference.html index af9cd8d5c..1eb74497b 100644 --- a/previews/PR135/source/methods/clipping/difference.html +++ b/previews/PR135/source/methods/clipping/difference.html @@ -8,10 +8,10 @@ - + - + @@ -184,7 +184,7 @@ ) return nothing end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/clipping/intersection.html b/previews/PR135/source/methods/clipping/intersection.html index b28e82f0e..63aa4808f 100644 --- a/previews/PR135/source/methods/clipping/intersection.html +++ b/previews/PR135/source/methods/clipping/intersection.html @@ -8,10 +8,10 @@ - + - + @@ -401,7 +401,7 @@ α, β = _clamped_frac(distance(min_pt, a2, T), a_dist, eps(T)), one(T) - eps(T) end

Return point with smallest distance

julia
    return _tuple_point(min_pt, T), α, β
 end

Return value of x/y clamped between ϵ and 1 - ϵ

julia
_clamped_frac(x::T, y::T, ϵ = zero(T)) where T = clamp(x / y, ϵ, one(T) - ϵ)

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/clipping/predicates.html b/previews/PR135/source/methods/clipping/predicates.html index 6c8455ce2..ffbbbed2b 100644 --- a/previews/PR135/source/methods/clipping/predicates.html +++ b/previews/PR135/source/methods/clipping/predicates.html @@ -8,10 +8,10 @@ - + - + @@ -62,7 +62,7 @@ end import .Predicates

If we want to inject adaptivity, we would do something like:

function cross(a, b, c) # try Predicates._cross_naive(a, b, c) # check the error bound there # then try Predicates._cross_adaptive(a, b, c) # then try Predicates._cross_exact end


This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/clipping/union.html b/previews/PR135/source/methods/clipping/union.html index 32a7f2aba..62a4c3c1e 100644 --- a/previews/PR135/source/methods/clipping/union.html +++ b/previews/PR135/source/methods/clipping/union.html @@ -8,10 +8,10 @@ - + - + @@ -269,7 +269,7 @@ throw(ArgumentError("Union between $trait_a and $trait_b with target $Target isn't implemented yet.")) return nothing end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/convex_hull.html b/previews/PR135/source/methods/convex_hull.html index 1482d2ad5..721996be7 100644 --- a/previews/PR135/source/methods/convex_hull.html +++ b/previews/PR135/source/methods/convex_hull.html @@ -8,11 +8,11 @@ - + - - + + @@ -26,7 +26,7 @@ hull_poly = GO.convex_hull(points) lines!(a, hull_poly; label = "Convex hull", color = Makie.wong_colors()[2]) axislegend(a) -f

Convex hull of the USA

julia
import GeometryOps as GO, GeoInterface as GI
+f

Convex hull of the USA

julia
import GeometryOps as GO, GeoInterface as GI
 using CairoMakie # to plot
 using NaturalEarth # for data
 
@@ -34,7 +34,7 @@
 usa = all_adm0.geometry[findfirst(==("USA"), all_adm0.ADM0_A3)]
 f, a, p = lines(usa)
 lines!(a, GO.convex_hull(usa); color = Makie.wong_colors()[2])
-f

Investigating the winding order

The winding order of the monotone chain method is counterclockwise, while the winding order of the GEOS method is clockwise.

GeometryOps' convexity detection says that the GEOS hull is convex, while the monotone chain method hull is not. However, they are both going over the same points (we checked), it's just that the winding order is different.

In reality, both sets are convex, but we need to fix the GeometryOps convexity detector (isconcave)!

We may also decide at a later date to change the returned winding order of the polygon, but most algorithms are robust to that, and you can always fix it...

julia
import GeoInterface as GI, GeometryOps as GO, LibGEOS as LG
+f

Investigating the winding order

The winding order of the monotone chain method is counterclockwise, while the winding order of the GEOS method is clockwise.

GeometryOps' convexity detection says that the GEOS hull is convex, while the monotone chain method hull is not. However, they are both going over the same points (we checked), it's just that the winding order is different.

In reality, both sets are convex, but we need to fix the GeometryOps convexity detector (isconcave)!

We may also decide at a later date to change the returned winding order of the polygon, but most algorithms are robust to that, and you can always fix it...

julia
import GeoInterface as GI, GeometryOps as GO, LibGEOS as LG
 using CairoMakie # to plot
 
 points = rand(Point2{Float64}, 100)
@@ -45,7 +45,7 @@
 a1, p1 = lines(fig[1, 1], go_hull; color = 1:GI.npoint(go_hull), axis = (; title = "MonotoneChainMethod()"))
 a2, p2 = lines(fig[2, 1], lg_hull; color = 1:GI.npoint(lg_hull), axis = (; title = "GEOS()"))
 cb = Colorbar(fig[1:2, 2], p1; label = "Vertex number")
-fig

Implementation

julia
"""
+fig

Implementation

julia
"""
     convex_hull([method], geometries)
 
 Compute the convex hull of the points in `geometries`.
@@ -75,7 +75,7 @@
 struct MonotoneChainMethod end

GrahamScanMethod, etc. can be implemented in GO as well, if someone wants to. If we add an extension on Quickhull.jl, then that would be another algorithm.

julia
convex_hull(geometries) = convex_hull(MonotoneChainMethod(), geometries)

TODO: have this respect the CRS by pulling it out of geometries.

julia
function convex_hull(::MonotoneChainMethod, geometries)

Extract all points as tuples. We have to collect and allocate here, because DelaunayTriangulation only accepts vectors of point-like geoms.

Cleanest would be to use the iterable from GO.flatten directly, but that would require us to implement the convex hull algorithm directly.

TODO: create a specialized method that extracts only the information required, GeometryBasics points can be passed through directly.

julia
    points = collect(flatten(tuples, GI.PointTrait, geometries))

Compute the convex hull using DelTri (shorthand for DelaunayTriangulation.jl).

julia
    hull = DelaunayTriangulation.convex_hull(points)

Convert the result to a GI.Polygon and return it. View would be more efficient here, but re-allocating is cleaner.

julia
    point_vec = DelaunayTriangulation.get_points(hull)[DelaunayTriangulation.get_vertices(hull)]
     return GI.Polygon([GI.LinearRing(point_vec)])
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/distance.html b/previews/PR135/source/methods/distance.html index 14236c326..62b7b44b6 100644 --- a/previews/PR135/source/methods/distance.html +++ b/previews/PR135/source/methods/distance.html @@ -8,11 +8,11 @@ - + - - + + @@ -29,13 +29,13 @@ f, a, p = poly(collect(GI.getpoint(rect)); axis = (; aspect = DataAspect())) scatter!(GI.x(point_in), GI.y(point_in); color = :red) scatter!(GI.x(point_out), GI.y(point_out); color = :orange) -f

This is clearly a rectangle with one point inside and one point outside. The points are both an equal distance to the polygon. The distance to point_in is negative while the distance to point_out is positive.

julia
(
+f

This is clearly a rectangle with one point inside and one point outside. The points are both an equal distance to the polygon. The distance to point_in is negative while the distance to point_out is positive.

julia
(
 GO.distance(point_in, rect),  # == 0
 GO.signed_distance(point_in, rect),  # < 0
 GO.signed_distance(point_out, rect)  # > 0
 )
(0.0, -0.5, 0.5)

Consider also a heatmap of signed distances around this object:

julia
xrange = yrange = LinRange(-0.5, 1.5, 300)
 f, a, p = heatmap(xrange, yrange, GO.signed_distance.(Point2f.(xrange, yrange'), Ref(rect)); colormap = :RdBu, colorrange = (-0.75, 0.75))
-a.aspect = DataAspect(); Colorbar(f[1, 2], p, label = "Signed distance"); lines!(a, GI.convert(GO.GeometryBasics, rect)); f

Implementation

This is the GeoInterface-compatible implementation. First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Distance and signed distance are only implemented for points to other geometries right now. This could be extended to include distance from other geometries in the future.

The distance calculated is the Euclidean distance using the Pythagorean theorem. Also note that singed_distance only makes sense for "filled-in" shapes, like polygons, so it isn't implemented for curves.

julia
const _DISTANCE_TARGETS = TraitTarget{Union{GI.AbstractPolygonTrait,GI.LineStringTrait,GI.LinearRingTrait,GI.LineTrait,GI.PointTrait}}()
+a.aspect = DataAspect(); Colorbar(f[1, 2], p, label = "Signed distance"); lines!(a, GI.convert(GO.GeometryBasics, rect)); f

Implementation

This is the GeoInterface-compatible implementation. First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Distance and signed distance are only implemented for points to other geometries right now. This could be extended to include distance from other geometries in the future.

The distance calculated is the Euclidean distance using the Pythagorean theorem. Also note that singed_distance only makes sense for "filled-in" shapes, like polygons, so it isn't implemented for curves.

julia
const _DISTANCE_TARGETS = TraitTarget{Union{GI.AbstractPolygonTrait,GI.LineStringTrait,GI.LinearRingTrait,GI.LineTrait,GI.PointTrait}}()
 
 """
     distance(point, geom, ::Type{T} = Float64)::T
@@ -199,7 +199,7 @@
     end
     return min_dist
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/equals.html b/previews/PR135/source/methods/equals.html index ae3c29d0e..c6dc233e4 100644 --- a/previews/PR135/source/methods/equals.html +++ b/previews/PR135/source/methods/equals.html @@ -8,11 +8,11 @@ - + - - + + @@ -29,7 +29,7 @@ scatter!(GI.getpoint(l1), color = :blue) lines!(GI.getpoint(l2), color = :orange) scatter!(GI.getpoint(l2), color = :orange) -f

We can see that the two lines do not share a common set of points and edges in the plot, so they are not equal:

julia
GO.equals(l1, l2)  # returns false
false

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Note that while we need the same set of points and edges, they don't need to be provided in the same order for polygons. For for example, we need the same set points for two multipoints to be equal, but they don't have to be saved in the same order. The winding order also doesn't have to be the same to represent the same geometry. This requires checking every point against every other point in the two geometries we are comparing. Also, some geometries must be "closed" like polygons and linear rings. These will be assumed to be closed, even if they don't have a repeated last point explicitly written in the coordinates. Additionally, geometries and multi-geometries can be equal if the multi-geometry only includes that single geometry.

julia
"""
+f

We can see that the two lines do not share a common set of points and edges in the plot, so they are not equal:

julia
GO.equals(l1, l2)  # returns false
false

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Note that while we need the same set of points and edges, they don't need to be provided in the same order for polygons. For for example, we need the same set points for two multipoints to be equal, but they don't have to be saved in the same order. The winding order also doesn't have to be the same to represent the same geometry. This requires checking every point against every other point in the two geometries we are comparing. Also, some geometries must be "closed" like polygons and linear rings. These will be assumed to be closed, even if they don't have a repeated last point explicitly written in the coordinates. Additionally, geometries and multi-geometries can be equal if the multi-geometry only includes that single geometry.

julia
"""
     equals(geom1, geom2)::Bool
 
 Compare two Geometries return true if they are the same geometry.
@@ -283,7 +283,7 @@
     end
     return true
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/geom_relations/contains.html b/previews/PR135/source/methods/geom_relations/contains.html index e9d214d99..ba4702def 100644 --- a/previews/PR135/source/methods/geom_relations/contains.html +++ b/previews/PR135/source/methods/geom_relations/contains.html @@ -8,11 +8,11 @@ - + - - + + @@ -29,7 +29,7 @@ scatter!(GI.getpoint(l1), color = :blue) lines!(GI.getpoint(l2), color = :orange) scatter!(GI.getpoint(l2), color = :orange) -f

We can see that all of the points and edges of l2 are within l1, so l1 contains l2. However, l2 does not contain l1.

julia
GO.contains(l1, l2)  # returns true
+f

We can see that all of the points and edges of l2 are within l1, so l1 contains l2. However, l2 does not contain l1.

julia
GO.contains(l1, l2)  # returns true
 GO.contains(l2, l1)  # returns false
false

Implementation

This is the GeoInterface-compatible implementation.

Given that contains is the exact opposite of within, we simply pass the two inputs variables, swapped in order, to within.

julia
"""
     contains(g1::AbstractGeometry, g2::AbstractGeometry)::Bool
 
@@ -51,7 +51,7 @@
 ```
 """
 contains(g1, g2) = GeometryOps.within(g2, g1)

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/geom_relations/coveredby.html b/previews/PR135/source/methods/geom_relations/coveredby.html index 191b3da53..18919d4e8 100644 --- a/previews/PR135/source/methods/geom_relations/coveredby.html +++ b/previews/PR135/source/methods/geom_relations/coveredby.html @@ -8,11 +8,11 @@ - + - - + + @@ -27,7 +27,7 @@ l1 = GI.Line([p1, (1.0, 1.0)]) f, a, p = lines(GI.getpoint(l1)) scatter!(p1, color = :red) -f

As we can see, p1 is on the endpoint of l1. This means it is not within, but it does meet the definition of coveredby.

julia
GO.coveredby(p1, l1)  # true
true

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

Each of these calls a method in the geom_geom_processors file. The methods in this file determine if the given geometries meet a set of criteria. For the coveredby function and arguments g1 and g2, this criteria is as follows: - points of g1 are allowed to be in the interior of g2 (either through overlap or crossing for lines) - points of g1 are allowed to be on the boundary of g2 - points of g1 are not allowed to be in the exterior of g2 - no points of g1 are required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

The code for the specific implementations is in the geom_geom_processors file.

julia
const COVEREDBY_ALLOWS = (in_allow = true, on_allow = true, out_allow = false)
+f

As we can see, p1 is on the endpoint of l1. This means it is not within, but it does meet the definition of coveredby.

julia
GO.coveredby(p1, l1)  # true
true

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

Each of these calls a method in the geom_geom_processors file. The methods in this file determine if the given geometries meet a set of criteria. For the coveredby function and arguments g1 and g2, this criteria is as follows: - points of g1 are allowed to be in the interior of g2 (either through overlap or crossing for lines) - points of g1 are allowed to be on the boundary of g2 - points of g1 are not allowed to be in the exterior of g2 - no points of g1 are required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

The code for the specific implementations is in the geom_geom_processors file.

julia
const COVEREDBY_ALLOWS = (in_allow = true, on_allow = true, out_allow = false)
 const COVEREDBY_CURVE_ALLOWS = (over_allow = true, cross_allow = true, on_allow = true, out_allow = false)
 const COVEREDBY_CURVE_REQUIRES = (in_require = false, on_require = false, out_require = false)
 const COVEREDBY_POLYGON_REQUIRES = (in_require = true, on_require = false, out_require = false,)
@@ -201,7 +201,7 @@
     end
     return true
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/geom_relations/covers.html b/previews/PR135/source/methods/geom_relations/covers.html index a6d73db38..226591c96 100644 --- a/previews/PR135/source/methods/geom_relations/covers.html +++ b/previews/PR135/source/methods/geom_relations/covers.html @@ -8,11 +8,11 @@ - + - - + + @@ -29,7 +29,7 @@ f, a, p = lines(GI.getpoint(l1)) scatter!(p1, color = :red) -f

julia
GO.covers(l1, p1)  # returns true
+f

julia
GO.covers(l1, p1)  # returns true
 GO.covers(p1, l1)  # returns false
false

Implementation

This is the GeoInterface-compatible implementation.

Given that covers is the exact opposite of coveredby, we simply pass the two inputs variables, swapped in order, to coveredby.

julia
"""
     covers(g1::AbstractGeometry, g2::AbstractGeometry)::Bool
 
@@ -51,7 +51,7 @@
 ```
 """
 covers(g1, g2)::Bool = GeometryOps.coveredby(g2, g1)

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/geom_relations/crosses.html b/previews/PR135/source/methods/geom_relations/crosses.html index 5ff2a844c..97236e81b 100644 --- a/previews/PR135/source/methods/geom_relations/crosses.html +++ b/previews/PR135/source/methods/geom_relations/crosses.html @@ -8,10 +8,10 @@ - + - + @@ -138,7 +138,7 @@ end return false end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/geom_relations/disjoint.html b/previews/PR135/source/methods/geom_relations/disjoint.html index 81f1cefe8..1d19f7d5c 100644 --- a/previews/PR135/source/methods/geom_relations/disjoint.html +++ b/previews/PR135/source/methods/geom_relations/disjoint.html @@ -8,11 +8,11 @@ - + - - + + @@ -29,7 +29,7 @@ scatter!(GI.getpoint(l1), color = :blue) lines!(GI.getpoint(l2), color = :orange) scatter!(GI.getpoint(l2), color = :orange) -f

We can see that none of the edges or vertices of l1 interact with l2 so they are disjoint.

julia
GO.disjoint(l1, l2)  # returns true
true

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

Each of these calls a method in the geom_geom_processors file. The methods in this file determine if the given geometries meet a set of criteria. For the disjoint function and arguments g1 and g2, this criteria is as follows: - points of g1 are not allowed to be in the interior of g2 - points of g1 are not allowed to be on the boundary of g2 - points of g1 are allowed to be in the exterior of g2 - no points required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

The code for the specific implementations is in the geom_geom_processors file.

julia
const DISJOINT_ALLOWS = (in_allow = false, on_allow = false, out_allow = true)
+f

We can see that none of the edges or vertices of l1 interact with l2 so they are disjoint.

julia
GO.disjoint(l1, l2)  # returns true
true

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

Each of these calls a method in the geom_geom_processors file. The methods in this file determine if the given geometries meet a set of criteria. For the disjoint function and arguments g1 and g2, this criteria is as follows: - points of g1 are not allowed to be in the interior of g2 - points of g1 are not allowed to be on the boundary of g2 - points of g1 are allowed to be in the exterior of g2 - no points required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

The code for the specific implementations is in the geom_geom_processors file.

julia
const DISJOINT_ALLOWS = (in_allow = false, on_allow = false, out_allow = true)
 const DISJOINT_CURVE_ALLOWS = (over_allow = false, cross_allow = false, on_allow = false, out_allow = true)
 const DISJOINT_REQUIRES = (in_require = false, on_require = false, out_require = false)
 const DISJOINT_EXACT = (exact = _False(),)
@@ -196,7 +196,7 @@
     end
     return true
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/geom_relations/geom_geom_processors.html b/previews/PR135/source/methods/geom_relations/geom_geom_processors.html index 8754edfdc..253be6637 100644 --- a/previews/PR135/source/methods/geom_relations/geom_geom_processors.html +++ b/previews/PR135/source/methods/geom_relations/geom_geom_processors.html @@ -8,10 +8,10 @@ - + - + @@ -455,7 +455,7 @@ end return skip, returnval end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/geom_relations/intersects.html b/previews/PR135/source/methods/geom_relations/intersects.html index dc785bd55..d6d3351fb 100644 --- a/previews/PR135/source/methods/geom_relations/intersects.html +++ b/previews/PR135/source/methods/geom_relations/intersects.html @@ -8,11 +8,11 @@ - + - - + + @@ -27,7 +27,7 @@ line2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)]) f, a, p = lines(GI.getpoint(line1)) lines!(GI.getpoint(line2)) -f

We can see that they intersect, so we expect intersects to return true, and we can visualize the intersection point in red.

julia
GO.intersects(line1, line2)  # true
true

Implementation

This is the GeoInterface-compatible implementation.

Given that intersects is the exact opposite of disjoint, we simply pass the two inputs variables, swapped in order, to disjoint.

julia
"""
+f

We can see that they intersect, so we expect intersects to return true, and we can visualize the intersection point in red.

julia
GO.intersects(line1, line2)  # true
true

Implementation

This is the GeoInterface-compatible implementation.

Given that intersects is the exact opposite of disjoint, we simply pass the two inputs variables, swapped in order, to disjoint.

julia
"""
     intersects(geom1, geom2)::Bool
 
 Return true if the interiors or boundaries of the two geometries interact.
@@ -45,7 +45,7 @@
 ```
 """
 intersects(geom1, geom2) = !disjoint(geom1, geom2)

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/geom_relations/overlaps.html b/previews/PR135/source/methods/geom_relations/overlaps.html index 4f958ffc6..acb86354f 100644 --- a/previews/PR135/source/methods/geom_relations/overlaps.html +++ b/previews/PR135/source/methods/geom_relations/overlaps.html @@ -8,11 +8,11 @@ - + - - + + @@ -29,7 +29,7 @@ scatter!(GI.getpoint(l1), color = :blue) lines!(GI.getpoint(l2), color = :orange) scatter!(GI.getpoint(l2), color = :orange) -f

We can see that the two lines overlap in the plot:

julia
GO.overlaps(l1, l2)  # true
true

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Note that that since only elements of the same dimension can overlap, any two geometries with traits that are of different dimensions automatically can return false.

For geometries with the same trait dimension, we must make sure that they share a point, an edge, or area for points, lines, and polygons/multipolygons respectively, without being contained.

julia
"""
+f

We can see that the two lines overlap in the plot:

julia
GO.overlaps(l1, l2)  # true
true

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait. This is also used in the implementation, since it's a lot less work!

Note that that since only elements of the same dimension can overlap, any two geometries with traits that are of different dimensions automatically can return false.

For geometries with the same trait dimension, we must make sure that they share a point, an edge, or area for points, lines, and polygons/multipolygons respectively, without being contained.

julia
"""
     overlaps(geom1, geom2)::Bool
 
 Compare two Geometries of the same dimension and return true if their
@@ -230,7 +230,7 @@
     seg_val, _, _ = _intersection_point(Float64, edge_a, edge_b; exact = _False())
     return seg_val != line_out
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/geom_relations/touches.html b/previews/PR135/source/methods/geom_relations/touches.html index 4d652f3f5..c45e0edc6 100644 --- a/previews/PR135/source/methods/geom_relations/touches.html +++ b/previews/PR135/source/methods/geom_relations/touches.html @@ -8,11 +8,11 @@ - + - - + + @@ -28,7 +28,7 @@ f, a, p = lines(GI.getpoint(l1)) lines!(GI.getpoint(l2)) -f

We can see that these two lines touch only at their endpoints.

julia
GO.touches(l1, l2)  # true
true

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

Each of these calls a method in the geom_geom_processors file. The methods in this file determine if the given geometries meet a set of criteria. For the touches function and arguments g1 and g2, this criteria is as follows: - points of g1 are not allowed to be in the interior of g2 - points of g1 are allowed to be on the boundary of g2 - points of g1 are allowed to be in the exterior of g2 - no points of g1 are required to be in the interior of g2 - at least one point of g1 is required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

The code for the specific implementations is in the geom_geom_processors file.

julia
const TOUCHES_POINT_ALLOWED = (in_allow = false, on_allow = true, out_allow = false)
+f

We can see that these two lines touch only at their endpoints.

julia
GO.touches(l1, l2)  # true
true

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

Each of these calls a method in the geom_geom_processors file. The methods in this file determine if the given geometries meet a set of criteria. For the touches function and arguments g1 and g2, this criteria is as follows: - points of g1 are not allowed to be in the interior of g2 - points of g1 are allowed to be on the boundary of g2 - points of g1 are allowed to be in the exterior of g2 - no points of g1 are required to be in the interior of g2 - at least one point of g1 is required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

The code for the specific implementations is in the geom_geom_processors file.

julia
const TOUCHES_POINT_ALLOWED = (in_allow = false, on_allow = true, out_allow = false)
 const TOUCHES_CURVE_ALLOWED = (over_allow = false, cross_allow = false, on_allow = true, out_allow = true)
 const TOUCHES_POLYGON_ALLOWS = (in_allow = false, on_allow = true, out_allow = true)
 const TOUCHES_REQUIRES = (in_require = false, on_require = true, out_require = false)
@@ -192,7 +192,7 @@
     end
     return true
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/geom_relations/within.html b/previews/PR135/source/methods/geom_relations/within.html index 86cdab821..830e5a759 100644 --- a/previews/PR135/source/methods/geom_relations/within.html +++ b/previews/PR135/source/methods/geom_relations/within.html @@ -8,11 +8,11 @@ - + - - + + @@ -29,7 +29,7 @@ scatter!(GI.getpoint(l1), color = :blue) lines!(GI.getpoint(l2), color = :orange) scatter!(GI.getpoint(l2), color = :orange) -f

We can see that all of the points and edges of l2 are within l1, so l2 is within l1, but l1 is not within l2

julia
GO.within(l1, l2)  # false
+f

We can see that all of the points and edges of l2 are within l1, so l2 is within l1, but l1 is not within l2

julia
GO.within(l1, l2)  # false
 GO.within(l2, l1)  # true
true

Implementation

This is the GeoInterface-compatible implementation.

First, we implement a wrapper method that dispatches to the correct implementation based on the geometry trait.

Each of these calls a method in the geom_geom_processors file. The methods in this file determine if the given geometries meet a set of criteria. For the within function and arguments g1 and g2, this criteria is as follows: - points of g1 are allowed to be in the interior of g2 (either through overlap or crossing for lines) - points of g1 are allowed to be on the boundary of g2 - points of g1 are not allowed to be in the exterior of g2 - at least one point of g1 is required to be in the interior of g2 - no points of g1 are required to be on the boundary of g2 - no points of g1 are required to be in the exterior of g2

The code for the specific implementations is in the geom_geom_processors file.

julia
const WITHIN_POINT_ALLOWS = (in_allow = true, on_allow = false, out_allow = false)
 const WITHIN_CURVE_ALLOWS = (over_allow = true, cross_allow = true, on_allow = true, out_allow = false)
 const WITHIN_POLYGON_ALLOWS = (in_allow = true, on_allow = true, out_allow = false)
@@ -211,7 +211,7 @@
     end
     return true
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/orientation.html b/previews/PR135/source/methods/orientation.html index dbd997a96..bedf2d391 100644 --- a/previews/PR135/source/methods/orientation.html +++ b/previews/PR135/source/methods/orientation.html @@ -8,10 +8,10 @@ - + - + @@ -118,7 +118,7 @@ _isparallel(bx - ax, by - ay, dx - cx, dy - cy) _isparallel(Δx1, Δy1, Δx2, Δy2) = (Δx1 * Δy2 == Δy1 * Δx2)

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/methods/polygonize.html b/previews/PR135/source/methods/polygonize.html index 9c947b090..fcd796967 100644 --- a/previews/PR135/source/methods/polygonize.html +++ b/previews/PR135/source/methods/polygonize.html @@ -8,10 +8,10 @@ - + - + @@ -307,7 +307,7 @@ end return edges end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/not_implemented_yet.html b/previews/PR135/source/not_implemented_yet.html index 9d185a667..2baadac7a 100644 --- a/previews/PR135/source/not_implemented_yet.html +++ b/previews/PR135/source/not_implemented_yet.html @@ -8,10 +8,10 @@ - + - + @@ -22,7 +22,7 @@ function buffer end function convexhull end function concavehull end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/primitives.html b/previews/PR135/source/primitives.html index eba226871..e108dd561 100644 --- a/previews/PR135/source/primitives.html +++ b/previews/PR135/source/primitives.html @@ -8,10 +8,10 @@ - + - + @@ -334,7 +334,7 @@ Base.@assume_effects :foldable function _mapreducetasks(f::F, op, taskrange, threaded::_False; init) where F mapreduce(f, op, taskrange; init) end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/transformations/correction/closed_ring.html b/previews/PR135/source/transformations/correction/closed_ring.html index 64f1381ae..a9dabe78c 100644 --- a/previews/PR135/source/transformations/correction/closed_ring.html +++ b/previews/PR135/source/transformations/correction/closed_ring.html @@ -8,10 +8,10 @@ - + - + @@ -48,7 +48,7 @@ else

Assemble the ring as a vector

julia
        tups = tuples.(GI.getpoint(ring))

Close the ring

julia
        push!(tups, tups[1])

Return an actual ring

julia
        return GI.LinearRing(tups)
     end
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/transformations/correction/geometry_correction.html b/previews/PR135/source/transformations/correction/geometry_correction.html index b0ee686f2..480752081 100644 --- a/previews/PR135/source/transformations/correction/geometry_correction.html +++ b/previews/PR135/source/transformations/correction/geometry_correction.html @@ -8,10 +8,10 @@ - + - + @@ -49,7 +49,7 @@ end return final_geometry end

Available corrections

# GeometryOps.ClosedRingType.
julia
ClosedRing() <: GeometryCorrection

This correction ensures that a polygon's exterior and interior rings are closed.

It can be called on any geometry correction as usual.

See also GeometryCorrection.

source


# GeometryOps.DiffIntersectingPolygonsType.
julia
DiffIntersectingPolygons() <: GeometryCorrection

This correction ensures that the polygons included in a multipolygon aren't intersecting. If any polygon's are intersecting, they will be made nonintersecting through the difference operation to create a unique set of disjoint (other than potentially connections by a single point) polygons covering the same area. See also GeometryCorrection, UnionIntersectingPolygons.

source


# GeometryOps.GeometryCorrectionType.
julia
abstract type GeometryCorrection

This abstract type represents a geometry correction.

Interface

Any GeometryCorrection must implement two functions: * application_level(::GeometryCorrection)::AbstractGeometryTrait: This function should return the GeoInterface trait that the correction is intended to be applied to, like PointTrait or LineStringTrait or PolygonTrait. * (::GeometryCorrection)(::AbstractGeometryTrait, geometry)::(some_geometry): This function should apply the correction to the given geometry, and return a new geometry.

source


# GeometryOps.UnionIntersectingPolygonsType.
julia
UnionIntersectingPolygons() <: GeometryCorrection

This correction ensures that the polygon's included in a multipolygon aren't intersecting. If any polygon's are intersecting, they will be combined through the union operation to create a unique set of disjoint (other than potentially connections by a single point) polygons covering the same area.

See also GeometryCorrection.

source



This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/transformations/correction/intersecting_polygons.html b/previews/PR135/source/transformations/correction/intersecting_polygons.html index a4b6d59ba..fa1f58b16 100644 --- a/previews/PR135/source/transformations/correction/intersecting_polygons.html +++ b/previews/PR135/source/transformations/correction/intersecting_polygons.html @@ -8,10 +8,10 @@ - + - + @@ -115,7 +115,7 @@ end return diff_multipoly end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/transformations/extent.html b/previews/PR135/source/transformations/extent.html index b4da89094..3870877b1 100644 --- a/previews/PR135/source/transformations/extent.html +++ b/previews/PR135/source/transformations/extent.html @@ -8,10 +8,10 @@ - + - + @@ -31,7 +31,7 @@ """ embed_extent(x; threaded=false, crs=nothing) = apply(identity, GI.PointTrait(), x; calc_extent=true, threaded, crs)

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/transformations/flip.html b/previews/PR135/source/transformations/flip.html index d0e1e43c3..a29eb4b04 100644 --- a/previews/PR135/source/transformations/flip.html +++ b/previews/PR135/source/transformations/flip.html @@ -8,10 +8,10 @@ - + - + @@ -40,7 +40,7 @@ end end end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/transformations/reproject.html b/previews/PR135/source/transformations/reproject.html index d1da91ef6..10ba63ee9 100644 --- a/previews/PR135/source/transformations/reproject.html +++ b/previews/PR135/source/transformations/reproject.html @@ -8,10 +8,10 @@ - + - + @@ -59,7 +59,7 @@ nothing end end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/transformations/segmentize.html b/previews/PR135/source/transformations/segmentize.html index 574abc726..a0a9bd37c 100644 --- a/previews/PR135/source/transformations/segmentize.html +++ b/previews/PR135/source/transformations/segmentize.html @@ -8,11 +8,11 @@ - + - - + + @@ -39,7 +39,7 @@ f, a, p = poly(collect(GI.getpoint(linear)); label = "Linear", axis = (; aspect = DataAspect())) p2 = poly!(collect(GI.getpoint(geodesic)); label = "Geodesic") axislegend(a; position = :lt) -f

There are two methods available for segmentizing geometries at the moment:

Missing docstring.

Missing docstring for LinearSegments. Check Documenter's build log for details.

Missing docstring.

Missing docstring for GeodesicSegments. Check Documenter's build log for details.

Benchmark

We benchmark our method against LibGEOS's GEOSDensify method, which is a similar method for densifying geometries.

julia
using BenchmarkTools: BenchmarkGroup
+f

There are two methods available for segmentizing geometries at the moment:

Missing docstring.

Missing docstring for LinearSegments. Check Documenter's build log for details.

Missing docstring.

Missing docstring for GeodesicSegments. Check Documenter's build log for details.

Benchmark

We benchmark our method against LibGEOS's GEOSDensify method, which is a similar method for densifying geometries.

julia
using BenchmarkTools: BenchmarkGroup
 using Chairmarks: @be
 using Main: plot_trials
 using CairoMakie
@@ -77,7 +77,7 @@
 
 end
 
-plot_trials(segmentize_suite)

julia
abstract type SegmentizeMethod end
+plot_trials(segmentize_suite)

julia
abstract type SegmentizeMethod end
 """
     LinearSegments(; max_distance::Real)
 
@@ -172,7 +172,7 @@
     end

End the line with the original coordinate, to avoid any multiplication errors.

julia
    push!(new_coords, (x2, y2))
     return nothing
 end

Note

The _fill_linear_kernel definition for GeodesicSegments is in the GeometryOpsProjExt extension module, in the segmentize.jl file.


This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/transformations/simplify.html b/previews/PR135/source/transformations/simplify.html index 81f77fd85..63157c4bb 100644 --- a/previews/PR135/source/transformations/simplify.html +++ b/previews/PR135/source/transformations/simplify.html @@ -8,11 +8,11 @@ - + - - + + @@ -29,7 +29,7 @@ f, a, p = poly(original; label = "Original") poly!(simple; label = "Simplified") axislegend(a) -f

Benchmark

We benchmark these methods against LibGEOS's simplify implementation, which uses the Douglas-Peucker algorithm.

julia
using BenchmarkTools, Chairmarks, GeoJSON, CairoMakie
+f

Benchmark

We benchmark these methods against LibGEOS's simplify implementation, which uses the Douglas-Peucker algorithm.

julia
using BenchmarkTools, Chairmarks, GeoJSON, CairoMakie
 import GeometryOps as GO, LibGEOS as LG, GeoInterface as GI
 using CoordinateTransformations
 using NaturalEarth
@@ -42,7 +42,7 @@
 usa_poly = GI.getgeom(usa_multipoly, findmax(GO.area.(GI.getgeom(usa_multipoly)))[2]) # isolate the poly with the most area
 usa_centroid = GO.centroid(usa_poly)
 usa_reflected = GO.transform(Translation(usa_centroid...)  LinearMap(Makie.rotmatrix2d(π))  Translation((-).(usa_centroid)...), usa_poly)
-f, a, p = plot(usa_poly; label = "Original", axis = (; aspect = DataAspect()))#; plot!(usa_reflected; label = "Reflected")

This is the complex polygon we'll be benchmarking.

julia
simplify_suite = BenchmarkGroup(["Simplify"])
+f, a, p = plot(usa_poly; label = "Original", axis = (; aspect = DataAspect()))#; plot!(usa_reflected; label = "Reflected")

This is the complex polygon we'll be benchmarking.

julia
simplify_suite = BenchmarkGroup(["Simplify"])
 singlepoly_suite = BenchmarkGroup(["Polygon", "title:Polygon simplify", "subtitle:Random blob"])
 
 include(joinpath(dirname(dirname(pathof(GO))), "test", "data", "polygon_generation.jl"))
@@ -56,7 +56,7 @@
     singlepoly_suite["LibGEOS"][GI.npoint(geom)] = @be LG.simplify($geom_lg, 0.1) seconds=1
 end
 
-plot_trials(singlepoly_suite; legend_position=(1, 1, TopRight()), legend_valign = -2, legend_halign = 1.2, legend_orientation = :horizontal)

julia
multipoly_suite = BenchmarkGroup(["MultiPolygon", "title:Multipolygon simplify", "subtitle:USA multipolygon"])
+plot_trials(singlepoly_suite; legend_position=(1, 1, TopRight()), legend_valign = -2, legend_halign = 1.2, legend_orientation = :horizontal)

julia
multipoly_suite = BenchmarkGroup(["MultiPolygon", "title:Multipolygon simplify", "subtitle:USA multipolygon"])
 
 for frac in exp10.(LinRange(log10(0.3), log10(1), 6)) # TODO: this example isn't the best.  How can we get this better?
     geom = GO.simplify(usa_multipoly; ratio = frac)
@@ -75,7 +75,7 @@
     # GO-VW : $(GI.npoint( GO.simplify((GO.VisvalingamWhyatt(; tol = _tol)), geom_go)))
     println()
 end
-plot_trials(multipoly_suite)

julia
export simplify, VisvalingamWhyatt, DouglasPeucker, RadialDistance
+plot_trials(multipoly_suite)

julia
export simplify, VisvalingamWhyatt, DouglasPeucker, RadialDistance
 
 const _SIMPLIFY_TARGET = TraitTarget{Union{GI.PolygonTrait, GI.AbstractCurveTrait, GI.MultiPointTrait, GI.PointTrait}}()
 const MIN_POINTS = 3
@@ -508,7 +508,7 @@
     end
     return nothing
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/transformations/transform.html b/previews/PR135/source/transformations/transform.html index 573792ac7..ae4b5e65b 100644 --- a/previews/PR135/source/transformations/transform.html +++ b/previews/PR135/source/transformations/transform.html @@ -8,10 +8,10 @@ - + - + @@ -73,7 +73,7 @@ end end end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/transformations/tuples.html b/previews/PR135/source/transformations/tuples.html index fc8f2e964..9d7b79bd0 100644 --- a/previews/PR135/source/transformations/tuples.html +++ b/previews/PR135/source/transformations/tuples.html @@ -8,10 +8,10 @@ - + - + @@ -37,7 +37,7 @@ end end end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/types.html b/previews/PR135/source/types.html index 5ece51d46..fce157bba 100644 --- a/previews/PR135/source/types.html +++ b/previews/PR135/source/types.html @@ -8,10 +8,10 @@ - + - + @@ -85,7 +85,7 @@ error("$(f) requires a `$(kw)` keyword argument to the `GEOS` algorithm, which was not provided.") end end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/source/utils.html b/previews/PR135/source/utils.html index 57b63ee1f..c1f6221d9 100644 --- a/previews/PR135/source/utils.html +++ b/previews/PR135/source/utils.html @@ -8,10 +8,10 @@ - + - + @@ -141,7 +141,7 @@ _linearring(geom::GI.LineString) = GI.LinearRing(parent(geom); extent=geom.extent, crs=geom.crs) _linearring(geom::GI.LinearRing) = geom

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR135/tutorials/creating_geometry.html b/previews/PR135/tutorials/creating_geometry.html index ba3828750..007853f2d 100644 --- a/previews/PR135/tutorials/creating_geometry.html +++ b/previews/PR135/tutorials/creating_geometry.html @@ -8,11 +8,11 @@ - + - - + + @@ -28,37 +28,37 @@ import Proj # Plotting using CairoMakie -using GeoMakie

Creating and plotting geometries

Let's start by making a single Point.

julia
point = GI.Point(0, 0)
GeoInterface.Wrappers.Point{false, false, Tuple{Int64, Int64}, Nothing}((0, 0), nothing)

Now, let's plot our point.

julia
fig, ax, plt = plot(point)

Let's create a set of points, and have a bit more fun with plotting.

julia
x = [-5, 0, 5, 0];
+using GeoMakie

Creating and plotting geometries

Let's start by making a single Point.

julia
point = GI.Point(0, 0)
GeoInterface.Wrappers.Point{false, false, Tuple{Int64, Int64}, Nothing}((0, 0), nothing)

Now, let's plot our point.

julia
fig, ax, plt = plot(point)

Let's create a set of points, and have a bit more fun with plotting.

julia
x = [-5, 0, 5, 0];
 y = [0, -5, 0, 5];
 points = GI.Point.(zip(x,y));
 plot!(ax, points; marker = '✈', markersize = 30)
-fig

Points can be combined into a single MultiPoint geometry.

julia
x = [-5, -5, 5, 5];
+fig

Points can be combined into a single MultiPoint geometry.

julia
x = [-5, -5, 5, 5];
 y = [-5, 5, 5, -5];
 multipoint = GI.MultiPoint(GI.Point.(zip(x, y)));
 plot!(ax, multipoint; marker = '☁', markersize = 30)
-fig

Let's create a LineString connecting two points.

julia
p1 = GI.Point.(-5, 0);
+fig

Let's create a LineString connecting two points.

julia
p1 = GI.Point.(-5, 0);
 p2 = GI.Point.(5, 0);
 line = GI.LineString([p1,p2])
 plot!(ax, line; color = :red)
-fig

Now, let's create a line connecting multiple points (i.e. a LineString). This time we get a bit more fancy with point creation.

julia
r = 2;
+fig

Now, let's create a line connecting multiple points (i.e. a LineString). This time we get a bit more fancy with point creation.

julia
r = 2;
 k = 10;
 ϴ = 0:0.01:2pi;
 x = r .* (k + 1) .* cos.(ϴ) .- r .* cos.((k + 1) .* ϴ);
 y = r .* (k + 1) .* sin.(ϴ) .- r .* sin.((k + 1) .* ϴ);
 lines = GI.LineString(GI.Point.(zip(x,y)));
 plot!(ax, lines; linewidth = 5)
-fig

We can also create a single LinearRing trait, the building block of a polygon. A LinearRing is simply a LineString with the same beginning and endpoint, i.e., an arbitrary closed shape composed of point pairs.

A LinearRing is composed of a series of points.

julia
ring1 = GI.LinearRing(GI.getpoint(lines));
GeoInterface.Wrappers.LinearRing{false, false, Vector{GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}[GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.0, 0.0), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.010987813253244, 0.0004397316773170068), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.043805248003498, 0.0035114210915891397), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.098016055420953, 0.011814947665167774), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.172899020101585, 0.027886421973952302), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.267456684570245, 0.05416726609360478), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.380427415579764, 0.09297443860091348), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.51030066635026, 0.1464721641710074), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.655335250260467, 0.21664550952386064), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.813580405100698, 0.30527612515520186), nothing)  …  GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.866418416586406, -0.3376428491230612), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.704405820024185, -0.24279488312757858), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.55494217175954, -0.16692537029320365), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.420040147662014, -0.10832215707812454), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.30151010318639, -0.0650624499034016), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.200938172182195, -0.03503632062070827), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.119667078681967, -0.01597247419241532), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.058779893613323, -0.005465967083412071), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.019086932781654, -0.0010075412835199304), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.001115954499138, -1.4219350464667047e-5), nothing)], nothing, nothing)

Now, let's make the LinearRing into a Polygon.

julia
polygon1 = GI.Polygon([ring1]);
GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearRing{false, false, Vector{GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}[GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.0, 0.0), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.010987813253244, 0.0004397316773170068), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.043805248003498, 0.0035114210915891397), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.098016055420953, 0.011814947665167774), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.172899020101585, 0.027886421973952302), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.267456684570245, 0.05416726609360478), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.380427415579764, 0.09297443860091348), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.51030066635026, 0.1464721641710074), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.655335250260467, 0.21664550952386064), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.813580405100698, 0.30527612515520186), nothing)  …  GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.866418416586406, -0.3376428491230612), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.704405820024185, -0.24279488312757858), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.55494217175954, -0.16692537029320365), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.420040147662014, -0.10832215707812454), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.30151010318639, -0.0650624499034016), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.200938172182195, -0.03503632062070827), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.119667078681967, -0.01597247419241532), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.058779893613323, -0.005465967083412071), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.019086932781654, -0.0010075412835199304), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.001115954499138, -1.4219350464667047e-5), nothing)], nothing, nothing)], nothing, nothing)

Now, we can use GeometryOps and CoordinateTransformations to shift polygon1 up, to avoid plotting over our earlier results. This is done through the GeometryOps.transform function.

julia
xoffset = 0.;
+fig

We can also create a single LinearRing trait, the building block of a polygon. A LinearRing is simply a LineString with the same beginning and endpoint, i.e., an arbitrary closed shape composed of point pairs.

A LinearRing is composed of a series of points.

julia
ring1 = GI.LinearRing(GI.getpoint(lines));
GeoInterface.Wrappers.LinearRing{false, false, Vector{GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}[GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.0, 0.0), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.010987813253244, 0.0004397316773170068), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.043805248003498, 0.0035114210915891397), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.098016055420953, 0.011814947665167774), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.172899020101585, 0.027886421973952302), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.267456684570245, 0.05416726609360478), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.380427415579764, 0.09297443860091348), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.51030066635026, 0.1464721641710074), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.655335250260467, 0.21664550952386064), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.813580405100698, 0.30527612515520186), nothing)  …  GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.866418416586406, -0.3376428491230612), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.704405820024185, -0.24279488312757858), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.55494217175954, -0.16692537029320365), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.420040147662014, -0.10832215707812454), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.30151010318639, -0.0650624499034016), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.200938172182195, -0.03503632062070827), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.119667078681967, -0.01597247419241532), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.058779893613323, -0.005465967083412071), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.019086932781654, -0.0010075412835199304), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.001115954499138, -1.4219350464667047e-5), nothing)], nothing, nothing)

Now, let's make the LinearRing into a Polygon.

julia
polygon1 = GI.Polygon([ring1]);
GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearRing{false, false, Vector{GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}[GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.0, 0.0), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.010987813253244, 0.0004397316773170068), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.043805248003498, 0.0035114210915891397), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.098016055420953, 0.011814947665167774), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.172899020101585, 0.027886421973952302), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.267456684570245, 0.05416726609360478), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.380427415579764, 0.09297443860091348), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.51030066635026, 0.1464721641710074), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.655335250260467, 0.21664550952386064), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.813580405100698, 0.30527612515520186), nothing)  …  GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.866418416586406, -0.3376428491230612), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.704405820024185, -0.24279488312757858), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.55494217175954, -0.16692537029320365), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.420040147662014, -0.10832215707812454), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.30151010318639, -0.0650624499034016), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.200938172182195, -0.03503632062070827), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.119667078681967, -0.01597247419241532), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.058779893613323, -0.005465967083412071), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.019086932781654, -0.0010075412835199304), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.001115954499138, -1.4219350464667047e-5), nothing)], nothing, nothing)], nothing, nothing)

Now, we can use GeometryOps and CoordinateTransformations to shift polygon1 up, to avoid plotting over our earlier results. This is done through the GeometryOps.transform function.

julia
xoffset = 0.;
 yoffset = 50.;
 f = CoordinateTransformations.Translation(xoffset, yoffset);
 polygon1 = GO.transform(f, polygon1);
 plot!(polygon1)
-fig

Polygons can contain "holes". The first LinearRing in a polygon is the exterior, and all subsequent LinearRings are treated as holes in the leading LinearRing.

GeoInterface offers the GI.getexterior(poly) and GI.gethole(poly) methods to get the exterior ring and an iterable of holes, respectively.

julia
hole = GI.LinearRing(GI.getpoint(multipoint))
+fig

Polygons can contain "holes". The first LinearRing in a polygon is the exterior, and all subsequent LinearRings are treated as holes in the leading LinearRing.

GeoInterface offers the GI.getexterior(poly) and GI.gethole(poly) methods to get the exterior ring and an iterable of holes, respectively.

julia
hole = GI.LinearRing(GI.getpoint(multipoint))
 polygon2 = GI.Polygon([ring1, hole])
GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, T, Nothing, Nothing} where T}, Nothing, Nothing}(GeoInterface.Wrappers.LinearRing{false, false, T, Nothing, Nothing} where T[GeoInterface.Wrappers.LinearRing{false, false, Vector{GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}[GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.0, 0.0), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.010987813253244, 0.0004397316773170068), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.043805248003498, 0.0035114210915891397), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.098016055420953, 0.011814947665167774), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.172899020101585, 0.027886421973952302), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.267456684570245, 0.05416726609360478), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.380427415579764, 0.09297443860091348), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.51030066635026, 0.1464721641710074), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.655335250260467, 0.21664550952386064), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.813580405100698, 0.30527612515520186), nothing)  …  GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.866418416586406, -0.3376428491230612), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.704405820024185, -0.24279488312757858), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.55494217175954, -0.16692537029320365), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.420040147662014, -0.10832215707812454), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.30151010318639, -0.0650624499034016), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.200938172182195, -0.03503632062070827), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.119667078681967, -0.01597247419241532), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.058779893613323, -0.005465967083412071), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.019086932781654, -0.0010075412835199304), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Float64, Float64}, Nothing}((20.001115954499138, -1.4219350464667047e-5), nothing)], nothing, nothing), GeoInterface.Wrappers.LinearRing{false, false, Vector{GeoInterface.Wrappers.Point{false, false, Tuple{Int64, Int64}, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Point{false, false, Tuple{Int64, Int64}, Nothing}[GeoInterface.Wrappers.Point{false, false, Tuple{Int64, Int64}, Nothing}((-5, -5), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Int64, Int64}, Nothing}((-5, 5), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Int64, Int64}, Nothing}((5, 5), nothing), GeoInterface.Wrappers.Point{false, false, Tuple{Int64, Int64}, Nothing}((5, -5), nothing)], nothing, nothing)], nothing, nothing)

Shift polygon2 to the right, to avoid plotting over our earlier results.

julia
xoffset = 50.;
 yoffset = 0.;
 f = CoordinateTransformations.Translation(xoffset, yoffset);
 polygon2 = GO.transform(f, polygon2);
 plot!(polygon2)
-fig

Polygons can also be grouped together as a MultiPolygon.

julia
r = 5;
+fig

Polygons can also be grouped together as a MultiPolygon.

julia
r = 5;
 x = cos.(reverse(ϴ)) .* r .+ xoffset;
 y = sin.(reverse(ϴ)) .* r .+ yoffset;
 ring2 =  GI.LinearRing(GI.Point.(zip(x,y)));
@@ -68,7 +68,7 @@
 f = CoordinateTransformations.Translation(xoffset, yoffset);
 multipolygon = GO.transform(f, multipolygon);
 plot!(multipolygon)
-fig

Great, now we can make Points, MultiPoints, Lines, LineStrings, Polygons (with holes), and MultiPolygons and modify them using [CoordinateTransformations] and [GeometryOps].

Plot geometries on a map using GeoMakie and coordinate reference system (CRS)

In geospatial sciences we often have data in one Coordinate Reference System (CRS) (source) and would like to display it in different (destination) CRS. GeoMakie allows us to do this by automatically projecting from source to destination CRS.

Here, our source CRS is common geographic (i.e. coordinates of latitude and longitude), WGS84.

julia
source_crs1 = GFT.EPSG(4326)
GeoFormatTypes.EPSG{1}((4326,))

Now let's pick a destination CRS for displaying our map. Here we'll pick natearth2.

julia
destination_crs = "+proj=natearth2"
"+proj=natearth2"

Let's add land area for context. First, download and open the Natural Earth global land polygons at 110 m resolution.GeoMakie ships with this particular dataset, so we will access it from there.

julia
land_path = GeoMakie.assetpath("ne_110m_land.geojson")
"/home/runner/.julia/packages/GeoMakie/2upVC/assets/ne_110m_land.geojson"

Note

Natural Earth has lots of other datasets, and there is a Julia package that provides an interface to it called NaturalEarth.jl.

Read the land MultiPolygons as a GeoJSON.FeatureCollection.

julia
land_geo = GeoJSON.read(land_path)
FeatureCollection with 127 Features

We then need to create a figure with a GeoAxis that can handle the projection between source and destination CRS. For GeoMakie, source is the CRS of the input and dest is the CRS you want to visualize in.

julia
fig = Figure(size=(1000, 500));
+fig

Great, now we can make Points, MultiPoints, Lines, LineStrings, Polygons (with holes), and MultiPolygons and modify them using [CoordinateTransformations] and [GeometryOps].

Plot geometries on a map using GeoMakie and coordinate reference system (CRS)

In geospatial sciences we often have data in one Coordinate Reference System (CRS) (source) and would like to display it in different (destination) CRS. GeoMakie allows us to do this by automatically projecting from source to destination CRS.

Here, our source CRS is common geographic (i.e. coordinates of latitude and longitude), WGS84.

julia
source_crs1 = GFT.EPSG(4326)
GeoFormatTypes.EPSG{1}((4326,))

Now let's pick a destination CRS for displaying our map. Here we'll pick natearth2.

julia
destination_crs = "+proj=natearth2"
"+proj=natearth2"

Let's add land area for context. First, download and open the Natural Earth global land polygons at 110 m resolution.GeoMakie ships with this particular dataset, so we will access it from there.

julia
land_path = GeoMakie.assetpath("ne_110m_land.geojson")
"/home/runner/.julia/packages/GeoMakie/2upVC/assets/ne_110m_land.geojson"

Note

Natural Earth has lots of other datasets, and there is a Julia package that provides an interface to it called NaturalEarth.jl.

Read the land MultiPolygons as a GeoJSON.FeatureCollection.

julia
land_geo = GeoJSON.read(land_path)
FeatureCollection with 127 Features

We then need to create a figure with a GeoAxis that can handle the projection between source and destination CRS. For GeoMakie, source is the CRS of the input and dest is the CRS you want to visualize in.

julia
fig = Figure(size=(1000, 500));
 ga = GeoAxis(
     fig[1, 1];
     source = source_crs1,
@@ -76,8 +76,8 @@
     xticklabelsvisible = false,
     yticklabelsvisible = false,
 );

Plot land for context.

julia
poly!(ga, land_geo, color=:black)
-fig

Now let's plot a Polygon like before, but this time with a CRS that differs from our source data

julia
plot!(multipolygon; color = :green)
-fig

But what if we want to plot geometries with a different source CRS on the same figure?

To show how to do this let's create a geometry with coordinates in UTM (Universal Transverse Mercator) zone 10N EPSG:32610.

julia
source_crs2 = GFT.EPSG(32610)
GeoFormatTypes.EPSG{1}((32610,))

Create a polygon (we're working in meters now, not latitude and longitude)

julia
r = 1000000;
+fig

Now let's plot a Polygon like before, but this time with a CRS that differs from our source data

julia
plot!(multipolygon; color = :green)
+fig

But what if we want to plot geometries with a different source CRS on the same figure?

To show how to do this let's create a geometry with coordinates in UTM (Universal Transverse Mercator) zone 10N EPSG:32610.

julia
source_crs2 = GFT.EPSG(32610)
GeoFormatTypes.EPSG{1}((32610,))

Create a polygon (we're working in meters now, not latitude and longitude)

julia
r = 1000000;
 ϴ = 0:0.01:2pi;
 x = r .* cos.(ϴ).^3 .+ 500000;
 y = r .* sin.(ϴ) .^ 3 .+5000000;
629-element Vector{Float64}:
@@ -87,7 +87,7 @@
 
  5.0e6
  5.0e6

Now create a LinearRing from Points

julia
ring3 = GI.LinearRing(Point.(zip(x, y)))
GeoInterface.Wrappers.LinearRing{false, false, Vector{Point{2, Float64}}, Nothing, Nothing}(Point{2, Float64}[[1.5e6, 5.0e6], [1.4998500087497458e6, 5.000000999950001e6], [1.4994001399837343e6, 5.000007998400139e6], [1.4986507085647392e6, 5.000026987852369e6], [1.4976022389592e6, 5.000063948817746e6], [1.4962554647802354e6, 5.000124843834609e6], [1.4946113281484335e6, 5.000215611503127e6], [1.4926709788709967e6, 5.000342160541625e6], [1.4904357734399722e6, 5.000510363870095e6], [1.4879072738504685e6, 5.0007260527263e6]  …  [1.4870405593989636e6, 4.999194331880103e6], [1.4896621210021754e6, 4.999426363321033e6], [1.491990928929295e6, 4.999609061508909e6], [1.4940253560034204e6, 4.999748243174828e6], [1.4957639801366436e6, 4.999849768598615e6], [1.497205585568957e6, 4.999919535736425e6], [1.4983491639274692e6, 4.999963474314044e6], [1.4991939151049731e6, 4.999987539891298e6], [1.4997392479570867e6, 4.999997707902938e6], [1.499984780817334e6, 4.999999967681458e6]], nothing, nothing)

Now create a Polygon from the LineRing

julia
polygon3 = GI.Polygon([ring3])
GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Point{2, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearRing{false, false, Vector{Point{2, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{Point{2, Float64}}, Nothing, Nothing}(Point{2, Float64}[[1.5e6, 5.0e6], [1.4998500087497458e6, 5.000000999950001e6], [1.4994001399837343e6, 5.000007998400139e6], [1.4986507085647392e6, 5.000026987852369e6], [1.4976022389592e6, 5.000063948817746e6], [1.4962554647802354e6, 5.000124843834609e6], [1.4946113281484335e6, 5.000215611503127e6], [1.4926709788709967e6, 5.000342160541625e6], [1.4904357734399722e6, 5.000510363870095e6], [1.4879072738504685e6, 5.0007260527263e6]  …  [1.4870405593989636e6, 4.999194331880103e6], [1.4896621210021754e6, 4.999426363321033e6], [1.491990928929295e6, 4.999609061508909e6], [1.4940253560034204e6, 4.999748243174828e6], [1.4957639801366436e6, 4.999849768598615e6], [1.497205585568957e6, 4.999919535736425e6], [1.4983491639274692e6, 4.999963474314044e6], [1.4991939151049731e6, 4.999987539891298e6], [1.4997392479570867e6, 4.999997707902938e6], [1.499984780817334e6, 4.999999967681458e6]], nothing, nothing)], nothing, nothing)

Now plot on the existing GeoAxis.

Note

The keyword argument source is used to specify the source CRS of that particular plot, when plotting on an existing GeoAxis.

julia
plot!(ga,polygon3; color=:red, source = source_crs2)
-fig

Create geospatial geometries with embedded coordinate reference system information

Great, we can make geometries and plot them on a map... now let's export the data to common geospatial data formats. To do this we now need to create geometries with embedded CRS information, making it a geospatial geometry. All that's needed is to include ; crs = crs as a keyword argument when constructing the geometry.

Let's do this for a new Polygon

julia
r = 3;
+fig

Create geospatial geometries with embedded coordinate reference system information

Great, we can make geometries and plot them on a map... now let's export the data to common geospatial data formats. To do this we now need to create geometries with embedded CRS information, making it a geospatial geometry. All that's needed is to include ; crs = crs as a keyword argument when constructing the geometry.

Let's do this for a new Polygon

julia
r = 3;
 k = 7;
 ϴ = 0:0.01:2pi;
 x = r .* (k + 1) .* cos.(ϴ) .- r .* cos.((k + 1) .* ϴ);
@@ -107,7 +107,7 @@
 GeoParquet.write(fn, df, (:geometry,))
"shapes.parquet"

Finally, if there's no Julia-native package that can write data to your desired format (e.g. .gpkg, .gml, etc), you can use GeoDataFrames. This package uses the GDAL library under the hood which supports writing to nearly all geospatial formats.

julia
import GeoDataFrames
 fn = "shapes.gpkg"
 GeoDataFrames.write(fn, df)
"shapes.gpkg"

And there we go, you can now create mapped geometries from scratch, manipulate them, plot them on a map, and save them in multiple geospatial data formats.

- + \ No newline at end of file diff --git a/previews/PR135/tutorials/geodesic_paths.html b/previews/PR135/tutorials/geodesic_paths.html index aceee8a9f..bf276b52a 100644 --- a/previews/PR135/tutorials/geodesic_paths.html +++ b/previews/PR135/tutorials/geodesic_paths.html @@ -8,11 +8,11 @@ - + - - + + @@ -28,8 +28,8 @@ fig, ga, _cp = lines(GeoMakie.coastlines(); axis = (; type = GeoAxis)) lines!(ga, GO.segmentize(GO.GeodesicSegments(; max_distance = 100_000), GI.LineString([IAH, AMS])); color = Makie.wong_colors()[2]) -fig

- +fig

+ \ No newline at end of file diff --git a/previews/PR135/tutorials/spatial_joins.html b/previews/PR135/tutorials/spatial_joins.html index cc9b484f0..440ebd91f 100644 --- a/previews/PR135/tutorials/spatial_joins.html +++ b/previews/PR135/tutorials/spatial_joins.html @@ -8,11 +8,11 @@ - + - - + + @@ -28,14 +28,14 @@ pl = GI.Polygon([GI.LinearRing([(0, 0), (1, 0), (1, 1), (0, 0)])]) pu = GI.Polygon([GI.LinearRing([(0, 0), (0, 1), (1, 1), (0, 0)])]) poly_df = DataFrame(geometry = [pl, pu], color = [:red, :blue]) -f, a, p = poly(poly_df.geometry; color = tuple.(poly_df.color, 0.3))

Here, the upper polygon is blue, and the lower polygon is red. Keep this in mind!

Now, we generate the points.

julia
points = tuple.(rand(1000), rand(1000))
+f, a, p = poly(poly_df.geometry; color = tuple.(poly_df.color, 0.3))

Here, the upper polygon is blue, and the lower polygon is red. Keep this in mind!

Now, we generate the points.

julia
points = tuple.(rand(1000), rand(1000))
 points_df = DataFrame(geometry = points)
 scatter!(points_df.geometry)
-f

You can see that they are evenly distributed around the box. But how do we know which points are in which polygons?

We have to join the two dataframes based on which polygon (if any) each point lies within.

Now, we can perform the "spatial join" using FlexiJoins. We are performing an outer join here

julia
@time joined_df = FlexiJoins.innerjoin(
+f

You can see that they are evenly distributed around the box. But how do we know which points are in which polygons?

We have to join the two dataframes based on which polygon (if any) each point lies within.

Now, we can perform the "spatial join" using FlexiJoins. We are performing an outer join here

julia
@time joined_df = FlexiJoins.innerjoin(
     (points_df, poly_df),
     by_pred(:geometry, GO.within, :geometry)
 )
julia
scatter!(a, joined_df.geometry; color = joined_df.color)
-f

Here, you can see that the colors were assigned appropriately to the scattered points!

Real-world example

Suppose I have a list of polygons representing administrative regions (or mining sites, or what have you), and I have a list of polygons for each country. I want to find the country each region is in.

julia
import GeoInterface as GI, GeometryOps as GO
+f

Here, you can see that the colors were assigned appropriately to the scattered points!

Real-world example

Suppose I have a list of polygons representing administrative regions (or mining sites, or what have you), and I have a list of polygons for each country. I want to find the country each region is in.

julia
import GeoInterface as GI, GeometryOps as GO
 using FlexiJoins, DataFrames, GADM # GADM gives us country and sublevel geometry
 
 using CairoMakie, GeoInterfaceMakie
@@ -60,7 +60,7 @@
     ::FlexiJoins.ByPred{typeof(my_predicate_function)}, 
     datas
 ) = true

This will enable FlexiJoins to support your custom function, when it's passed to by_pred(:geometry, my_predicate_function, :geometry).

- + \ No newline at end of file