From 5b77528ccab6e25dd836cdc73ad66f37570c35aa Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Mon, 5 Aug 2024 02:24:17 +0000 Subject: [PATCH] build based on 9f4f21e --- previews/PR195/404.html | 4 +-- previews/PR195/api.html | 10 +++--- ...{api.md.DHJubgq9.js => api.md.BOAMfjMD.js} | 2 +- ...Jubgq9.lean.js => api.md.BOAMfjMD.lean.js} | 0 .../{app.CwzsUFVf.js => app.BsEKKDDb.js} | 2 +- previews/PR195/assets/bgtsnje.DVcLHOBC.png | Bin 59599 -> 0 bytes previews/PR195/assets/buspiou.ByFWjvme.png | Bin 0 -> 66519 bytes ...zvjk.CLtpJ5Wb.png => caclkux.CLtpJ5Wb.png} | Bin ...eboz.0OJvb21A.png => cgvuwzn.0OJvb21A.png} | Bin .../chunks/@localSearchIndexroot.BCaxkeLm.js | 1 + .../chunks/@localSearchIndexroot.kKZ6qjPz.js | 1 - ...KAZZrZ.js => VPLocalSearchBox.B29r77KC.js} | 2 +- .../{theme.DJtgRhan.js => theme.BvCUQvTg.js} | 4 +-- ...bjxj.CZy9YIUA.png => clplkkm.CZy9YIUA.png} | Bin previews/PR195/assets/ctiloyl.CFVRSdJa.png | Bin 207344 -> 0 bytes previews/PR195/assets/ddzbkrv.Cu3R6KIg.png | Bin 59890 -> 0 bytes ...vpdz.B94PsR1K.png => deanaex.B94PsR1K.png} | Bin ...nkuw.mCtKcWOr.png => dhlqeej.mCtKcWOr.png} | Bin ...dnuc.BOOG5oTW.png => dwvmxse.BOOG5oTW.png} | Bin ... => experiments_predicates.md.CrrYSeMP.js} | 2 +- ...xperiments_predicates.md.CrrYSeMP.lean.js} | 2 +- ...yuxp.CgiryX2p.png => fkyrbwd.CgiryX2p.png} | Bin ...wyaf.BD0hVfse.png => fnrfugo.BD0hVfse.png} | Bin previews/PR195/assets/fptcvzg.CEAi3Hur.png | Bin 0 -> 228782 bytes ...vgkt.CG4dr3Lx.png => gcksvkc.CG4dr3Lx.png} | Bin ...txgd.B9NpLJr_.png => ggltyjf.B9NpLJr_.png} | Bin ...hrdt._0R9BbFk.png => hmrsdci._0R9BbFk.png} | Bin previews/PR195/assets/ilxipml.BiyYzhuJ.png | Bin 0 -> 79707 bytes ...cifb.DiwGEg2f.png => itpybxw.DiwGEg2f.png} | Bin ...lklp.C3SxJ3x-.png => lhkqeby.C3SxJ3x-.png} | Bin ...xrrb.CULn5saZ.png => lrliqyk.CULn5saZ.png} | Bin ...dqjv.3UVIT8DR.png => lztgeom.3UVIT8DR.png} | Bin ...mtsn.BEFUMtlf.png => mcqalyu.BEFUMtlf.png} | Bin ...vdje.Dab1-ETk.png => mosjncj.Dab1-ETk.png} | Bin previews/PR195/assets/ngakfcp.CDGBiJCb.png | Bin 0 -> 75084 bytes previews/PR195/assets/opbptnb.CcuM5mrn.png | Bin 0 -> 60386 bytes previews/PR195/assets/orltnjp.Bl1rHoTH.png | Bin 0 -> 206362 bytes ...oyoq.DwqDxlAG.png => ovekyuq.DwqDxlAG.png} | Bin ...zquu.Danh069g.png => pehcnll.Danh069g.png} | Bin ...bvur.3sfpQl2i.png => pfelvgi.3sfpQl2i.png} | Bin ...mxer.DuBHk1fh.png => prxdefz.DuBHk1fh.png} | Bin ...kwsq.Cx40vhB3.png => sjtvrcb.Cx40vhB3.png} | Bin ...gfmq.DeeQUply.png => smqrhuk.DeeQUply.png} | Bin ...js => source_lazy_wrappers.md.DfDLIJRO.js} | 13 ++++++-- ... source_lazy_wrappers.md.DfDLIJRO.lean.js} | 2 +- ...s => source_methods_angles.md.CE49jq22.js} | 2 +- ...source_methods_angles.md.CE49jq22.lean.js} | 2 +- ....js => source_methods_area.md.CVTC71h0.js} | 2 +- ...> source_methods_area.md.CVTC71h0.lean.js} | 2 +- ...source_methods_barycentric.md.Cs4dTCTH.js} | 2 +- ...e_methods_barycentric.md.Cs4dTCTH.lean.js} | 2 +- ...=> source_methods_centroid.md.BHSz4F0b.js} | 2 +- ...urce_methods_centroid.md.BHSz4F0b.lean.js} | 2 +- ..._methods_clipping_coverage.md.A7dmJGZH.js} | 2 +- ...ods_clipping_coverage.md.A7dmJGZH.lean.js} | 2 +- ...ource_methods_clipping_cut.md.D2wwxGIq.js} | 2 +- ..._methods_clipping_cut.md.D2wwxGIq.lean.js} | 2 +- ...source_methods_convex_hull.md.CoBp1HPw.js} | 6 ++-- ...e_methods_convex_hull.md.CoBp1HPw.lean.js} | 2 +- ...=> source_methods_distance.md.CIQ2kRRk.js} | 2 +- ...urce_methods_distance.md.CIQ2kRRk.lean.js} | 2 +- ...s => source_methods_equals.md.CG2GX2G-.js} | 2 +- ...source_methods_equals.md.CG2GX2G-.lean.js} | 2 +- ...ds_geom_relations_contains.md.Cpcc7vXO.js} | 2 +- ...om_relations_contains.md.Cpcc7vXO.lean.js} | 2 +- ...s_geom_relations_coveredby.md.Dj_nAWvy.js} | 2 +- ...m_relations_coveredby.md.Dj_nAWvy.lean.js} | 2 +- ...hods_geom_relations_covers.md.CSDFydXd.js} | 2 +- ...geom_relations_covers.md.CSDFydXd.lean.js} | 2 +- ...ds_geom_relations_disjoint.md.DiBqaDAE.js} | 2 +- ...om_relations_disjoint.md.DiBqaDAE.lean.js} | 2 +- ..._geom_relations_intersects.md.BsITeqCZ.js} | 2 +- ..._relations_intersects.md.BsITeqCZ.lean.js} | 2 +- ...ds_geom_relations_overlaps.md.CVfdoel4.js} | 2 +- ...om_relations_overlaps.md.CVfdoel4.lean.js} | 2 +- ...ods_geom_relations_touches.md.qExTc2Ih.js} | 2 +- ...eom_relations_touches.md.qExTc2Ih.lean.js} | 2 +- ...hods_geom_relations_within.md.B_OARYtU.js} | 2 +- ...geom_relations_within.md.B_OARYtU.lean.js} | 2 +- ...tion_intersecting_polygons.md.CVZsm4oL.js} | 2 +- ...intersecting_polygons.md.CVZsm4oL.lean.js} | 0 ...transformations_segmentize.md.udwcI5Oo.js} | 2 +- ...formations_segmentize.md.udwcI5Oo.lean.js} | 2 +- ...e_transformations_simplify.md.DN9O_9Po.js} | 2 +- ...nsformations_simplify.md.DN9O_9Po.lean.js} | 2 +- ...wnre.Bglvb-jp.png => soxjjou.Bglvb-jp.png} | Bin ...uvvf.-VpeHhXX.png => sphoxvj.-VpeHhXX.png} | Bin ...zmep.Dz86q2IX.png => sxiveed.Dz86q2IX.png} | Bin ...cpgs.Dig-DWOQ.png => tdamwbc.Dig-DWOQ.png} | Bin ...ials_creating_geometry.md.CTcfZU4F.lean.js | 1 - ...utorials_creating_geometry.md.KPKOdNQp.js} | 2 +- ...ials_creating_geometry.md.KPKOdNQp.lean.js | 1 + ...> tutorials_geodesic_paths.md.BxIfIMgw.js} | 2 +- ...orials_geodesic_paths.md.BxIfIMgw.lean.js} | 2 +- ...=> tutorials_spatial_joins.md.DnrEv8Ha.js} | 2 +- ...torials_spatial_joins.md.DnrEv8Ha.lean.js} | 2 +- previews/PR195/assets/ubioczg.BEJwZXDr.png | Bin 0 -> 64766 bytes ...yasm.DHcwB147.png => udpiilt.DHcwB147.png} | Bin previews/PR195/assets/vnmulmx.B5YNxAG6.png | Bin 63940 -> 0 bytes previews/PR195/assets/wrywclb.Ub4DcWOw.png | Bin 62413 -> 0 bytes previews/PR195/assets/xaostbm.Do8zVjU_.png | Bin 0 -> 62224 bytes ...fzdo.lu4jwpi-.png => xefmvbl.lu4jwpi-.png} | Bin ...nlfl.Cb0_DiYE.png => xmiofty.Cb0_DiYE.png} | Bin previews/PR195/assets/xqumkjw.bGrOSeNg.png | Bin 75624 -> 0 bytes ...obnh.DaovVbE6.png => yhbmgox.DaovVbE6.png} | Bin previews/PR195/assets/ypadxpg.BbLXt6JS.png | Bin 78698 -> 0 bytes previews/PR195/assets/yprriky.DFdMtN1X.png | Bin 229180 -> 0 bytes ...opgb.DC3TvBOO.png => yvjcfvo.DC3TvBOO.png} | Bin previews/PR195/call_notes.html | 6 ++-- .../experiments/accurate_accumulators.html | 6 ++-- previews/PR195/experiments/predicates.html | 10 +++--- previews/PR195/explanations/crs.html | 6 ++-- previews/PR195/explanations/paradigms.html | 6 ++-- .../PR195/explanations/peculiarities.html | 6 ++-- .../PR195/explanations/winding_order.html | 6 ++-- previews/PR195/hashmap.json | 2 +- previews/PR195/index.html | 6 ++-- previews/PR195/introduction.html | 6 ++-- previews/PR195/source/GeometryOps.html | 6 ++-- previews/PR195/source/lazy_wrappers.html | 19 +++++++---- previews/PR195/source/methods/angles.html | 10 +++--- previews/PR195/source/methods/area.html | 12 +++---- .../PR195/source/methods/barycentric.html | 10 +++--- previews/PR195/source/methods/buffer.html | 6 ++-- previews/PR195/source/methods/centroid.html | 12 +++---- .../methods/clipping/clipping_processor.html | 6 ++-- .../source/methods/clipping/coverage.html | 10 +++--- .../PR195/source/methods/clipping/cut.html | 10 +++--- .../source/methods/clipping/difference.html | 6 ++-- .../source/methods/clipping/intersection.html | 6 ++-- .../source/methods/clipping/predicates.html | 6 ++-- .../PR195/source/methods/clipping/union.html | 6 ++-- .../PR195/source/methods/convex_hull.html | 14 ++++---- previews/PR195/source/methods/distance.html | 12 +++---- previews/PR195/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 +++--- .../PR195/source/methods/orientation.html | 6 ++-- previews/PR195/source/methods/polygonize.html | 6 ++-- .../PR195/source/not_implemented_yet.html | 6 ++-- previews/PR195/source/primitives.html | 6 ++-- .../correction/closed_ring.html | 6 ++-- .../correction/geometry_correction.html | 6 ++-- .../correction/intersecting_polygons.html | 10 +++--- .../PR195/source/transformations/extent.html | 6 ++-- .../PR195/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 ++-- .../PR195/source/transformations/tuples.html | 6 ++-- previews/PR195/source/types.html | 6 ++-- previews/PR195/source/utils.html | 6 ++-- .../PR195/tutorials/creating_geometry.html | 30 +++++++++--------- previews/PR195/tutorials/geodesic_paths.html | 10 +++--- previews/PR195/tutorials/spatial_joins.html | 14 ++++---- 164 files changed, 318 insertions(+), 304 deletions(-) rename previews/PR195/assets/{api.md.DHJubgq9.js => api.md.BOAMfjMD.js} (99%) rename previews/PR195/assets/{api.md.DHJubgq9.lean.js => api.md.BOAMfjMD.lean.js} (100%) rename previews/PR195/assets/{app.CwzsUFVf.js => app.BsEKKDDb.js} (95%) delete mode 100644 previews/PR195/assets/bgtsnje.DVcLHOBC.png create mode 100644 previews/PR195/assets/buspiou.ByFWjvme.png rename previews/PR195/assets/{fqvzvjk.CLtpJ5Wb.png => caclkux.CLtpJ5Wb.png} (100%) rename previews/PR195/assets/{btfeboz.0OJvb21A.png => cgvuwzn.0OJvb21A.png} (100%) create mode 100644 previews/PR195/assets/chunks/@localSearchIndexroot.BCaxkeLm.js delete mode 100644 previews/PR195/assets/chunks/@localSearchIndexroot.kKZ6qjPz.js rename previews/PR195/assets/chunks/{VPLocalSearchBox.dAKAZZrZ.js => VPLocalSearchBox.B29r77KC.js} (99%) rename previews/PR195/assets/chunks/{theme.DJtgRhan.js => theme.BvCUQvTg.js} (99%) rename previews/PR195/assets/{qukbjxj.CZy9YIUA.png => clplkkm.CZy9YIUA.png} (100%) delete mode 100644 previews/PR195/assets/ctiloyl.CFVRSdJa.png delete mode 100644 previews/PR195/assets/ddzbkrv.Cu3R6KIg.png rename previews/PR195/assets/{yjtvpdz.B94PsR1K.png => deanaex.B94PsR1K.png} (100%) rename previews/PR195/assets/{idgnkuw.mCtKcWOr.png => dhlqeej.mCtKcWOr.png} (100%) rename previews/PR195/assets/{grddnuc.BOOG5oTW.png => dwvmxse.BOOG5oTW.png} (100%) rename previews/PR195/assets/{experiments_predicates.md.CcitvZuA.js => experiments_predicates.md.CrrYSeMP.js} (99%) rename previews/PR195/assets/{experiments_predicates.md.CcitvZuA.lean.js => experiments_predicates.md.CrrYSeMP.lean.js} (74%) rename previews/PR195/assets/{fyvyuxp.CgiryX2p.png => fkyrbwd.CgiryX2p.png} (100%) rename previews/PR195/assets/{cxpwyaf.BD0hVfse.png => fnrfugo.BD0hVfse.png} (100%) create mode 100644 previews/PR195/assets/fptcvzg.CEAi3Hur.png rename previews/PR195/assets/{kxmvgkt.CG4dr3Lx.png => gcksvkc.CG4dr3Lx.png} (100%) rename previews/PR195/assets/{eqjtxgd.B9NpLJr_.png => ggltyjf.B9NpLJr_.png} (100%) rename previews/PR195/assets/{slqhrdt._0R9BbFk.png => hmrsdci._0R9BbFk.png} (100%) create mode 100644 previews/PR195/assets/ilxipml.BiyYzhuJ.png rename previews/PR195/assets/{jnncifb.DiwGEg2f.png => itpybxw.DiwGEg2f.png} (100%) rename previews/PR195/assets/{ratlklp.C3SxJ3x-.png => lhkqeby.C3SxJ3x-.png} (100%) rename previews/PR195/assets/{yjoxrrb.CULn5saZ.png => lrliqyk.CULn5saZ.png} (100%) rename previews/PR195/assets/{fnddqjv.3UVIT8DR.png => lztgeom.3UVIT8DR.png} (100%) rename previews/PR195/assets/{gxbmtsn.BEFUMtlf.png => mcqalyu.BEFUMtlf.png} (100%) rename previews/PR195/assets/{zihvdje.Dab1-ETk.png => mosjncj.Dab1-ETk.png} (100%) create mode 100644 previews/PR195/assets/ngakfcp.CDGBiJCb.png create mode 100644 previews/PR195/assets/opbptnb.CcuM5mrn.png create mode 100644 previews/PR195/assets/orltnjp.Bl1rHoTH.png rename previews/PR195/assets/{tdkoyoq.DwqDxlAG.png => ovekyuq.DwqDxlAG.png} (100%) rename previews/PR195/assets/{jjazquu.Danh069g.png => pehcnll.Danh069g.png} (100%) rename previews/PR195/assets/{yqfbvur.3sfpQl2i.png => pfelvgi.3sfpQl2i.png} (100%) rename previews/PR195/assets/{nehmxer.DuBHk1fh.png => prxdefz.DuBHk1fh.png} (100%) rename previews/PR195/assets/{krjkwsq.Cx40vhB3.png => sjtvrcb.Cx40vhB3.png} (100%) rename previews/PR195/assets/{aoygfmq.DeeQUply.png => smqrhuk.DeeQUply.png} (100%) rename previews/PR195/assets/{source_lazy_wrappers.md.D-02co7q.js => source_lazy_wrappers.md.DfDLIJRO.js} (92%) rename previews/PR195/assets/{source_lazy_wrappers.md.D-02co7q.lean.js => source_lazy_wrappers.md.DfDLIJRO.lean.js} (73%) rename previews/PR195/assets/{source_methods_angles.md.DV0Y43Q3.js => source_methods_angles.md.CE49jq22.js} (99%) rename previews/PR195/assets/{source_methods_angles.md.DV0Y43Q3.lean.js => source_methods_angles.md.CE49jq22.lean.js} (87%) rename previews/PR195/assets/{source_methods_area.md.rtJOiNA4.js => source_methods_area.md.CVTC71h0.js} (99%) rename previews/PR195/assets/{source_methods_area.md.rtJOiNA4.lean.js => source_methods_area.md.CVTC71h0.lean.js} (77%) rename previews/PR195/assets/{source_methods_barycentric.md.vGqNyZU6.js => source_methods_barycentric.md.Cs4dTCTH.js} (99%) rename previews/PR195/assets/{source_methods_barycentric.md.vGqNyZU6.lean.js => source_methods_barycentric.md.Cs4dTCTH.lean.js} (99%) rename previews/PR195/assets/{source_methods_centroid.md.B5Cew8t8.js => source_methods_centroid.md.BHSz4F0b.js} (99%) rename previews/PR195/assets/{source_methods_centroid.md.B5Cew8t8.lean.js => source_methods_centroid.md.BHSz4F0b.lean.js} (77%) rename previews/PR195/assets/{source_methods_clipping_coverage.md.BuvGmFZb.js => source_methods_clipping_coverage.md.A7dmJGZH.js} (99%) rename previews/PR195/assets/{source_methods_clipping_coverage.md.BuvGmFZb.lean.js => source_methods_clipping_coverage.md.A7dmJGZH.lean.js} (87%) rename previews/PR195/assets/{source_methods_clipping_cut.md.QtgaScMf.js => source_methods_clipping_cut.md.D2wwxGIq.js} (99%) rename previews/PR195/assets/{source_methods_clipping_cut.md.QtgaScMf.lean.js => source_methods_clipping_cut.md.D2wwxGIq.lean.js} (87%) rename previews/PR195/assets/{source_methods_convex_hull.md.CgmNGeI7.js => source_methods_convex_hull.md.CoBp1HPw.js} (98%) rename previews/PR195/assets/{source_methods_convex_hull.md.CgmNGeI7.lean.js => source_methods_convex_hull.md.CoBp1HPw.lean.js} (59%) rename previews/PR195/assets/{source_methods_distance.md.8KpGRCHG.js => source_methods_distance.md.CIQ2kRRk.js} (99%) rename previews/PR195/assets/{source_methods_distance.md.8KpGRCHG.lean.js => source_methods_distance.md.CIQ2kRRk.lean.js} (78%) rename previews/PR195/assets/{source_methods_equals.md.Co-ScnAH.js => source_methods_equals.md.CG2GX2G-.js} (99%) rename previews/PR195/assets/{source_methods_equals.md.Co-ScnAH.lean.js => source_methods_equals.md.CG2GX2G-.lean.js} (87%) rename previews/PR195/assets/{source_methods_geom_relations_contains.md.BYVoqBEK.js => source_methods_geom_relations_contains.md.Cpcc7vXO.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_contains.md.BYVoqBEK.lean.js => source_methods_geom_relations_contains.md.Cpcc7vXO.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_coveredby.md.Da4Nggfz.js => source_methods_geom_relations_coveredby.md.Dj_nAWvy.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_coveredby.md.Da4Nggfz.lean.js => source_methods_geom_relations_coveredby.md.Dj_nAWvy.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_covers.md.6cjvwJ4o.js => source_methods_geom_relations_covers.md.CSDFydXd.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_covers.md.6cjvwJ4o.lean.js => source_methods_geom_relations_covers.md.CSDFydXd.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_disjoint.md.B6v5WyhO.js => source_methods_geom_relations_disjoint.md.DiBqaDAE.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_disjoint.md.B6v5WyhO.lean.js => source_methods_geom_relations_disjoint.md.DiBqaDAE.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_intersects.md.C_dWbJm2.js => source_methods_geom_relations_intersects.md.BsITeqCZ.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_intersects.md.C_dWbJm2.lean.js => source_methods_geom_relations_intersects.md.BsITeqCZ.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_overlaps.md.dIRYZVMG.js => source_methods_geom_relations_overlaps.md.CVfdoel4.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_overlaps.md.dIRYZVMG.lean.js => source_methods_geom_relations_overlaps.md.CVfdoel4.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_touches.md.kSP9eu6o.js => source_methods_geom_relations_touches.md.qExTc2Ih.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_touches.md.kSP9eu6o.lean.js => source_methods_geom_relations_touches.md.qExTc2Ih.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_within.md.50QxJSsQ.js => source_methods_geom_relations_within.md.B_OARYtU.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_within.md.50QxJSsQ.lean.js => source_methods_geom_relations_within.md.B_OARYtU.lean.js} (88%) rename previews/PR195/assets/{source_transformations_correction_intersecting_polygons.md.DDP7R70p.js => source_transformations_correction_intersecting_polygons.md.CVZsm4oL.js} (99%) rename previews/PR195/assets/{source_transformations_correction_intersecting_polygons.md.DDP7R70p.lean.js => source_transformations_correction_intersecting_polygons.md.CVZsm4oL.lean.js} (100%) rename previews/PR195/assets/{source_transformations_segmentize.md.dZC37wEB.js => source_transformations_segmentize.md.udwcI5Oo.js} (99%) rename previews/PR195/assets/{source_transformations_segmentize.md.dZC37wEB.lean.js => source_transformations_segmentize.md.udwcI5Oo.lean.js} (67%) rename previews/PR195/assets/{source_transformations_simplify.md.zSu4Fsil.js => source_transformations_simplify.md.DN9O_9Po.js} (99%) rename previews/PR195/assets/{source_transformations_simplify.md.zSu4Fsil.lean.js => source_transformations_simplify.md.DN9O_9Po.lean.js} (56%) rename previews/PR195/assets/{vcawnre.Bglvb-jp.png => soxjjou.Bglvb-jp.png} (100%) rename previews/PR195/assets/{qvluvvf.-VpeHhXX.png => sphoxvj.-VpeHhXX.png} (100%) rename previews/PR195/assets/{ujrzmep.Dz86q2IX.png => sxiveed.Dz86q2IX.png} (100%) rename previews/PR195/assets/{gkocpgs.Dig-DWOQ.png => tdamwbc.Dig-DWOQ.png} (100%) delete mode 100644 previews/PR195/assets/tutorials_creating_geometry.md.CTcfZU4F.lean.js rename previews/PR195/assets/{tutorials_creating_geometry.md.CTcfZU4F.js => tutorials_creating_geometry.md.KPKOdNQp.js} (99%) create mode 100644 previews/PR195/assets/tutorials_creating_geometry.md.KPKOdNQp.lean.js rename previews/PR195/assets/{tutorials_geodesic_paths.md.CiRkt159.js => tutorials_geodesic_paths.md.BxIfIMgw.js} (98%) rename previews/PR195/assets/{tutorials_geodesic_paths.md.CiRkt159.lean.js => tutorials_geodesic_paths.md.BxIfIMgw.lean.js} (87%) rename previews/PR195/assets/{tutorials_spatial_joins.md.DnL32vAR.js => tutorials_spatial_joins.md.DnrEv8Ha.js} (99%) rename previews/PR195/assets/{tutorials_spatial_joins.md.DnL32vAR.lean.js => tutorials_spatial_joins.md.DnrEv8Ha.lean.js} (92%) create mode 100644 previews/PR195/assets/ubioczg.BEJwZXDr.png rename previews/PR195/assets/{flhyasm.DHcwB147.png => udpiilt.DHcwB147.png} (100%) delete mode 100644 previews/PR195/assets/vnmulmx.B5YNxAG6.png delete mode 100644 previews/PR195/assets/wrywclb.Ub4DcWOw.png create mode 100644 previews/PR195/assets/xaostbm.Do8zVjU_.png rename previews/PR195/assets/{uxifzdo.lu4jwpi-.png => xefmvbl.lu4jwpi-.png} (100%) rename previews/PR195/assets/{jjenlfl.Cb0_DiYE.png => xmiofty.Cb0_DiYE.png} (100%) delete mode 100644 previews/PR195/assets/xqumkjw.bGrOSeNg.png rename previews/PR195/assets/{dvyobnh.DaovVbE6.png => yhbmgox.DaovVbE6.png} (100%) delete mode 100644 previews/PR195/assets/ypadxpg.BbLXt6JS.png delete mode 100644 previews/PR195/assets/yprriky.DFdMtN1X.png rename previews/PR195/assets/{msgopgb.DC3TvBOO.png => yvjcfvo.DC3TvBOO.png} (100%) diff --git a/previews/PR195/404.html b/previews/PR195/404.html index ba3b081fb..49b14b943 100644 --- a/previews/PR195/404.html +++ b/previews/PR195/404.html @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@
- + \ No newline at end of file diff --git a/previews/PR195/api.html b/previews/PR195/api.html index e7c10954d..b5df37f5f 100644 --- a/previews/PR195/api.html +++ b/previews/PR195/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/PR195/assets/api.md.DHJubgq9.js b/previews/PR195/assets/api.md.BOAMfjMD.js similarity index 99% rename from previews/PR195/assets/api.md.DHJubgq9.js rename to previews/PR195/assets/api.md.BOAMfjMD.js index 9ddd47ba7..8f581019a 100644 --- a/previews/PR195/assets/api.md.DHJubgq9.js +++ b/previews/PR195/assets/api.md.BOAMfjMD.js @@ -1,4 +1,4 @@ -import{_ as n,c as e,j as s,a,a7 as i,o as t}from"./chunks/framework.BjTE4JnT.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.BjTE4JnT.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/PR195/assets/api.md.DHJubgq9.lean.js b/previews/PR195/assets/api.md.BOAMfjMD.lean.js
similarity index 100%
rename from previews/PR195/assets/api.md.DHJubgq9.lean.js
rename to previews/PR195/assets/api.md.BOAMfjMD.lean.js
diff --git a/previews/PR195/assets/app.CwzsUFVf.js b/previews/PR195/assets/app.BsEKKDDb.js
similarity index 95%
rename from previews/PR195/assets/app.CwzsUFVf.js
rename to previews/PR195/assets/app.BsEKKDDb.js
index 33459fd4a..03128d1c6 100644
--- a/previews/PR195/assets/app.CwzsUFVf.js
+++ b/previews/PR195/assets/app.BsEKKDDb.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.BjTE4JnT.js";import{R as S}from"./chunks/theme.DJtgRhan.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.BjTE4JnT.js";import{R as S}from"./chunks/theme.BvCUQvTg.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/PR195/assets/bgtsnje.DVcLHOBC.png b/previews/PR195/assets/bgtsnje.DVcLHOBC.png
deleted file mode 100644
index ebd84eb5611f2a8e4f0e344226ecac5fe75f47e6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 59599
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
Wj2r%Lb1)-ga!*zn^FhYwzyAl*Io7rS

diff --git a/previews/PR195/assets/buspiou.ByFWjvme.png b/previews/PR195/assets/buspiou.ByFWjvme.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb3eaaf97af7a10cfef137b90f8c44e9088545c9
GIT binary patch
literal 66519
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

- + \ No newline at end of file diff --git a/previews/PR195/experiments/predicates.html b/previews/PR195/experiments/predicates.html index 111f66d7f..2af0d857f 100644 --- a/previews/PR195/experiments/predicates.html +++ b/previews/PR195/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/PR195/explanations/crs.html b/previews/PR195/explanations/crs.html index 6e9b5733f..9d79d659a 100644 --- a/previews/PR195/explanations/crs.html +++ b/previews/PR195/explanations/crs.html @@ -8,10 +8,10 @@ - + - + @@ -19,7 +19,7 @@
Skip to content
- + \ No newline at end of file diff --git a/previews/PR195/explanations/paradigms.html b/previews/PR195/explanations/paradigms.html index e04d90ab4..f05335585 100644 --- a/previews/PR195/explanations/paradigms.html +++ b/previews/PR195/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/PR195/explanations/peculiarities.html b/previews/PR195/explanations/peculiarities.html index 25a0dec4c..c80188552 100644 --- a/previews/PR195/explanations/peculiarities.html +++ b/previews/PR195/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/PR195/explanations/winding_order.html b/previews/PR195/explanations/winding_order.html index 462c3435f..a0ff55636 100644 --- a/previews/PR195/explanations/winding_order.html +++ b/previews/PR195/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/PR195/hashmap.json b/previews/PR195/hashmap.json index ba8e99867..b6ba85ead 100644 --- a/previews/PR195/hashmap.json +++ b/previews/PR195/hashmap.json @@ -1 +1 @@ -{"api.md":"DHJubgq9","call_notes.md":"7w_VILqE","experiments_accurate_accumulators.md":"GjJQmUUF","experiments_predicates.md":"CcitvZuA","explanations_crs.md":"rB2P2tWO","explanations_paradigms.md":"DI9tAELo","explanations_peculiarities.md":"FHOVDDB1","explanations_winding_order.md":"Bm0h6HnC","index.md":"BWJsEQ2o","introduction.md":"D8tkvOeC","source_geometryops.md":"Cz4BxN1w","source_lazy_wrappers.md":"D-02co7q","source_methods_angles.md":"DV0Y43Q3","source_methods_area.md":"rtJOiNA4","source_methods_barycentric.md":"vGqNyZU6","source_methods_buffer.md":"DU5YIRhh","source_methods_centroid.md":"B5Cew8t8","source_methods_clipping_clipping_processor.md":"CtF2CQFc","source_methods_clipping_coverage.md":"BuvGmFZb","source_methods_clipping_cut.md":"QtgaScMf","source_methods_clipping_difference.md":"XpSQ9j5U","source_methods_clipping_intersection.md":"DrpOytWq","source_methods_clipping_predicates.md":"DXSSIe8m","source_methods_clipping_union.md":"W5PFYzX8","source_methods_convex_hull.md":"CgmNGeI7","source_methods_distance.md":"8KpGRCHG","source_methods_equals.md":"Co-ScnAH","source_methods_geom_relations_contains.md":"BYVoqBEK","source_methods_geom_relations_coveredby.md":"Da4Nggfz","source_methods_geom_relations_covers.md":"6cjvwJ4o","source_methods_geom_relations_crosses.md":"BHrKZtR2","source_methods_geom_relations_disjoint.md":"B6v5WyhO","source_methods_geom_relations_geom_geom_processors.md":"C9HuuL9H","source_methods_geom_relations_intersects.md":"C_dWbJm2","source_methods_geom_relations_overlaps.md":"dIRYZVMG","source_methods_geom_relations_touches.md":"kSP9eu6o","source_methods_geom_relations_within.md":"50QxJSsQ","source_methods_orientation.md":"CAyjPYQQ","source_methods_polygonize.md":"Bajq04Cb","source_not_implemented_yet.md":"Bz3ySMxN","source_primitives.md":"CwLE6hnK","source_transformations_correction_closed_ring.md":"BDlXIhE4","source_transformations_correction_geometry_correction.md":"VEdgiIPU","source_transformations_correction_intersecting_polygons.md":"DDP7R70p","source_transformations_extent.md":"BOfr-Fyq","source_transformations_flip.md":"BXRIoP3a","source_transformations_reproject.md":"AFBdSlqT","source_transformations_segmentize.md":"dZC37wEB","source_transformations_simplify.md":"zSu4Fsil","source_transformations_transform.md":"CQbjpjpu","source_transformations_tuples.md":"De5DcGB5","source_types.md":"Df5Ol6GF","source_utils.md":"BvaNAnX0","tutorials_creating_geometry.md":"CTcfZU4F","tutorials_geodesic_paths.md":"CiRkt159","tutorials_spatial_joins.md":"DnL32vAR"} +{"api.md":"BOAMfjMD","call_notes.md":"7w_VILqE","experiments_accurate_accumulators.md":"GjJQmUUF","experiments_predicates.md":"CrrYSeMP","explanations_crs.md":"rB2P2tWO","explanations_paradigms.md":"DI9tAELo","explanations_peculiarities.md":"FHOVDDB1","explanations_winding_order.md":"Bm0h6HnC","index.md":"BWJsEQ2o","introduction.md":"D8tkvOeC","source_geometryops.md":"Cz4BxN1w","source_lazy_wrappers.md":"DfDLIJRO","source_methods_angles.md":"CE49jq22","source_methods_area.md":"CVTC71h0","source_methods_barycentric.md":"Cs4dTCTH","source_methods_buffer.md":"DU5YIRhh","source_methods_centroid.md":"BHSz4F0b","source_methods_clipping_clipping_processor.md":"CtF2CQFc","source_methods_clipping_coverage.md":"A7dmJGZH","source_methods_clipping_cut.md":"D2wwxGIq","source_methods_clipping_difference.md":"XpSQ9j5U","source_methods_clipping_intersection.md":"DrpOytWq","source_methods_clipping_predicates.md":"DXSSIe8m","source_methods_clipping_union.md":"W5PFYzX8","source_methods_convex_hull.md":"CoBp1HPw","source_methods_distance.md":"CIQ2kRRk","source_methods_equals.md":"CG2GX2G-","source_methods_geom_relations_contains.md":"Cpcc7vXO","source_methods_geom_relations_coveredby.md":"Dj_nAWvy","source_methods_geom_relations_covers.md":"CSDFydXd","source_methods_geom_relations_crosses.md":"BHrKZtR2","source_methods_geom_relations_disjoint.md":"DiBqaDAE","source_methods_geom_relations_geom_geom_processors.md":"C9HuuL9H","source_methods_geom_relations_intersects.md":"BsITeqCZ","source_methods_geom_relations_overlaps.md":"CVfdoel4","source_methods_geom_relations_touches.md":"qExTc2Ih","source_methods_geom_relations_within.md":"B_OARYtU","source_methods_orientation.md":"CAyjPYQQ","source_methods_polygonize.md":"Bajq04Cb","source_not_implemented_yet.md":"Bz3ySMxN","source_primitives.md":"CwLE6hnK","source_transformations_correction_closed_ring.md":"BDlXIhE4","source_transformations_correction_geometry_correction.md":"VEdgiIPU","source_transformations_correction_intersecting_polygons.md":"CVZsm4oL","source_transformations_extent.md":"BOfr-Fyq","source_transformations_flip.md":"BXRIoP3a","source_transformations_reproject.md":"AFBdSlqT","source_transformations_segmentize.md":"udwcI5Oo","source_transformations_simplify.md":"DN9O_9Po","source_transformations_transform.md":"CQbjpjpu","source_transformations_tuples.md":"De5DcGB5","source_types.md":"Df5Ol6GF","source_utils.md":"BvaNAnX0","tutorials_creating_geometry.md":"KPKOdNQp","tutorials_geodesic_paths.md":"BxIfIMgw","tutorials_spatial_joins.md":"DnrEv8Ha"} diff --git a/previews/PR195/index.html b/previews/PR195/index.html index 4f7e91c69..ca7a2ddcc 100644 --- a/previews/PR195/index.html +++ b/previews/PR195/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/PR195/introduction.html b/previews/PR195/introduction.html index fdda20c63..8a28917b2 100644 --- a/previews/PR195/introduction.html +++ b/previews/PR195/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/PR195/source/GeometryOps.html b/previews/PR195/source/GeometryOps.html index 358cf908a..52cd8543b 100644 --- a/previews/PR195/source/GeometryOps.html +++ b/previews/PR195/source/GeometryOps.html @@ -8,10 +8,10 @@ - + - + @@ -93,7 +93,7 @@ end end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR195/source/lazy_wrappers.html b/previews/PR195/source/lazy_wrappers.html index d12d72c97..543865bb3 100644 --- a/previews/PR195/source/lazy_wrappers.html +++ b/previews/PR195/source/lazy_wrappers.html @@ -8,11 +8,11 @@ - + - - + + @@ -78,10 +78,13 @@ function LazyClosedRingTuplePointIterator(ring::LazyClosedRing) geom = ring.ring + if GI.isempty(geom) + return LazyClosedRingTuplePointIterator{GI.is3d(geom), GI.ismeasured(geom), typeof(geom)}(geom, true) + end isclosed = GI.getpoint(geom, 1) == GI.getpoint(geom, GI.npoint(geom)) return LazyClosedRingTuplePointIterator{GI.is3d(geom), GI.ismeasured(geom), typeof(geom)}(geom, isclosed) end

Base iterator interface

julia
Base.IteratorSize(::LazyClosedRingTuplePointIterator) = Base.HasLength()
-Base.length(iter::LazyClosedRingTuplePointIterator) = GI.npoint(iter.ring) + iter.closed
+Base.length(iter::LazyClosedRingTuplePointIterator) = GI.npoint(iter.ring) + !iter.closed
 Base.IteratorEltype(::LazyClosedRingTuplePointIterator) = Base.HasEltype()
 function Base.eltype(::LazyClosedRingTuplePointIterator{hasZ, hasM}) where {hasZ, hasM}
     if !hasZ && !hasM
@@ -94,7 +97,11 @@
 end
 
 function Base.iterate(iter::LazyClosedRingTuplePointIterator)
-    return (GI.getpoint(iter.ring, 1), 1)
+    if GI.isempty(iter.ring)
+        return nothing
+    else
+        return (GI.getpoint(iter.ring, 1), 1)
+    end
 end
 
 function Base.iterate(iter::LazyClosedRingTuplePointIterator, state)
@@ -117,7 +124,7 @@
         end
     end
 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR195/source/methods/angles.html b/previews/PR195/source/methods/angles.html index f881a68be..5222f0bb0 100644 --- a/previews/PR195/source/methods/angles.html +++ b/previews/PR195/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/PR195/source/methods/area.html b/previews/PR195/source/methods/area.html index be210ae26..6f95520ab 100644 --- a/previews/PR195/source/methods/area.html +++ b/previews/PR195/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/PR195/source/methods/barycentric.html b/previews/PR195/source/methods/barycentric.html index d5b566fb4..2a8ec9881 100644 --- a/previews/PR195/source/methods/barycentric.html +++ b/previews/PR195/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/PR195/source/methods/buffer.html b/previews/PR195/source/methods/buffer.html index 1ad7441ef..4deae9269 100644 --- a/previews/PR195/source/methods/buffer.html +++ b/previews/PR195/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/PR195/source/methods/centroid.html b/previews/PR195/source/methods/centroid.html index 6d28e7aa5..19212260e 100644 --- a/previews/PR195/source/methods/centroid.html +++ b/previews/PR195/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/PR195/source/methods/clipping/clipping_processor.html b/previews/PR195/source/methods/clipping/clipping_processor.html index d3dc667d0..2c280437a 100644 --- a/previews/PR195/source/methods/clipping/clipping_processor.html +++ b/previews/PR195/source/methods/clipping/clipping_processor.html @@ -8,10 +8,10 @@ - + - + @@ -525,7 +525,7 @@ end return end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR195/source/methods/clipping/coverage.html b/previews/PR195/source/methods/clipping/coverage.html index a52c450ba..5f7e6a0e8 100644 --- a/previews/PR195/source/methods/clipping/coverage.html +++ b/previews/PR195/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/PR195/source/methods/clipping/cut.html b/previews/PR195/source/methods/clipping/cut.html index d03806acc..546225cce 100644 --- a/previews/PR195/source/methods/clipping/cut.html +++ b/previews/PR195/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/PR195/source/methods/clipping/difference.html b/previews/PR195/source/methods/clipping/difference.html index 5d52be4c0..97d9f9607 100644 --- a/previews/PR195/source/methods/clipping/difference.html +++ b/previews/PR195/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/PR195/source/methods/clipping/intersection.html b/previews/PR195/source/methods/clipping/intersection.html index 0582110f5..69012b28a 100644 --- a/previews/PR195/source/methods/clipping/intersection.html +++ b/previews/PR195/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/PR195/source/methods/clipping/predicates.html b/previews/PR195/source/methods/clipping/predicates.html index 3c0de6250..59028794e 100644 --- a/previews/PR195/source/methods/clipping/predicates.html +++ b/previews/PR195/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/PR195/source/methods/clipping/union.html b/previews/PR195/source/methods/clipping/union.html index e2000b80c..aacf08dba 100644 --- a/previews/PR195/source/methods/clipping/union.html +++ b/previews/PR195/source/methods/clipping/union.html @@ -8,10 +8,10 @@ - + - + @@ -268,7 +268,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/PR195/source/methods/convex_hull.html b/previews/PR195/source/methods/convex_hull.html index c81a679dc..625a17983 100644 --- a/previews/PR195/source/methods/convex_hull.html +++ b/previews/PR195/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/PR195/source/methods/distance.html b/previews/PR195/source/methods/distance.html index e63c30927..c6c49e271 100644 --- a/previews/PR195/source/methods/distance.html +++ b/previews/PR195/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/PR195/source/methods/equals.html b/previews/PR195/source/methods/equals.html index 80cb85ba6..d46fd0437 100644 --- a/previews/PR195/source/methods/equals.html +++ b/previews/PR195/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/PR195/source/methods/geom_relations/contains.html b/previews/PR195/source/methods/geom_relations/contains.html index 109d351a2..72f3fca7b 100644 --- a/previews/PR195/source/methods/geom_relations/contains.html +++ b/previews/PR195/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/PR195/source/methods/geom_relations/coveredby.html b/previews/PR195/source/methods/geom_relations/coveredby.html index c712fedd2..1002e9148 100644 --- a/previews/PR195/source/methods/geom_relations/coveredby.html +++ b/previews/PR195/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/PR195/source/methods/geom_relations/covers.html b/previews/PR195/source/methods/geom_relations/covers.html index 1a72240c2..bfed3d488 100644 --- a/previews/PR195/source/methods/geom_relations/covers.html +++ b/previews/PR195/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/PR195/source/methods/geom_relations/crosses.html b/previews/PR195/source/methods/geom_relations/crosses.html index 708b96c6c..f587d9881 100644 --- a/previews/PR195/source/methods/geom_relations/crosses.html +++ b/previews/PR195/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/PR195/source/methods/geom_relations/disjoint.html b/previews/PR195/source/methods/geom_relations/disjoint.html index f3707cc1a..0c103937c 100644 --- a/previews/PR195/source/methods/geom_relations/disjoint.html +++ b/previews/PR195/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/PR195/source/methods/geom_relations/geom_geom_processors.html b/previews/PR195/source/methods/geom_relations/geom_geom_processors.html index 6eeeafbca..cfe87fb1d 100644 --- a/previews/PR195/source/methods/geom_relations/geom_geom_processors.html +++ b/previews/PR195/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/PR195/source/methods/geom_relations/intersects.html b/previews/PR195/source/methods/geom_relations/intersects.html index b5acf29fd..fdab74210 100644 --- a/previews/PR195/source/methods/geom_relations/intersects.html +++ b/previews/PR195/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/PR195/source/methods/geom_relations/overlaps.html b/previews/PR195/source/methods/geom_relations/overlaps.html index b587505c9..a10133d3c 100644 --- a/previews/PR195/source/methods/geom_relations/overlaps.html +++ b/previews/PR195/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/PR195/source/methods/geom_relations/touches.html b/previews/PR195/source/methods/geom_relations/touches.html index a31bd32bd..a4a178f5b 100644 --- a/previews/PR195/source/methods/geom_relations/touches.html +++ b/previews/PR195/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/PR195/source/methods/geom_relations/within.html b/previews/PR195/source/methods/geom_relations/within.html index 89867d9bf..7441fd4ad 100644 --- a/previews/PR195/source/methods/geom_relations/within.html +++ b/previews/PR195/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/PR195/source/methods/orientation.html b/previews/PR195/source/methods/orientation.html index 84137604f..b1697c4ee 100644 --- a/previews/PR195/source/methods/orientation.html +++ b/previews/PR195/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/PR195/source/methods/polygonize.html b/previews/PR195/source/methods/polygonize.html index 212de66d2..bc8a58beb 100644 --- a/previews/PR195/source/methods/polygonize.html +++ b/previews/PR195/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/PR195/source/not_implemented_yet.html b/previews/PR195/source/not_implemented_yet.html index 7fa8bf05d..fc11ffc78 100644 --- a/previews/PR195/source/not_implemented_yet.html +++ b/previews/PR195/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/PR195/source/primitives.html b/previews/PR195/source/primitives.html index 9cd7034fc..8afaf6d8e 100644 --- a/previews/PR195/source/primitives.html +++ b/previews/PR195/source/primitives.html @@ -8,10 +8,10 @@ - + - + @@ -323,7 +323,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/PR195/source/transformations/correction/closed_ring.html b/previews/PR195/source/transformations/correction/closed_ring.html index d4fdb221d..bf92d0324 100644 --- a/previews/PR195/source/transformations/correction/closed_ring.html +++ b/previews/PR195/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/PR195/source/transformations/correction/geometry_correction.html b/previews/PR195/source/transformations/correction/geometry_correction.html index 16aba889b..b2fd28f20 100644 --- a/previews/PR195/source/transformations/correction/geometry_correction.html +++ b/previews/PR195/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/PR195/source/transformations/correction/intersecting_polygons.html b/previews/PR195/source/transformations/correction/intersecting_polygons.html index b0488ccc6..4a723641a 100644 --- a/previews/PR195/source/transformations/correction/intersecting_polygons.html +++ b/previews/PR195/source/transformations/correction/intersecting_polygons.html @@ -8,11 +8,11 @@ - + - - + + @@ -21,7 +21,7 @@
Skip to content

Intersecting Polygons

julia
export UnionIntersectingPolygons

If the sub-polygons of a multipolygon are intersecting, this makes them invalid according to specification. Each sub-polygon of a multipolygon being disjoint (other than by a single point) is a requirement for a valid multipolygon. However, different libraries may achieve this in different ways.

For example, taking the union of all sub-polygons of a multipolygon will create a new multipolygon where each sub-polygon is disjoint. This can be done with the UnionIntersectingPolygons correction.

The reason this operates on a multipolygon level is that it is easy for users to mistakenly create multipolygon's that overlap, which can then be detrimental to polygon clipping performance and even create wrong answers.

Example

Multipolygon providers may not check that the polygons making up their multipolygons do not intersect, which makes them invalid according to the specification.

For example, the following multipolygon is not valid:

julia
import GeoInterface as GI
 polygon = GI.Polygon([[(0.0, 0.0), (3.0, 0.0), (3.0, 3.0), (0.0, 3.0), (0.0, 0.0)]])
 multipolygon = GI.MultiPolygon([polygon, polygon])
GeoInterface.Wrappers.MultiPolygon{false, false, Vector{GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}[GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(0.0, 0.0), (3.0, 0.0), (3.0, 3.0), (0.0, 3.0), (0.0, 0.0)], nothing, nothing)], nothing, nothing), GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(0.0, 0.0), (3.0, 0.0), (3.0, 3.0), (0.0, 3.0), (0.0, 0.0)], nothing, nothing)], nothing, nothing)], nothing, nothing)

given that the two sub-polygons are the exact same shape.

julia
import GeometryOps as GO
-GO.fix(multipolygon, corrections = [GO.UnionIntersectingPolygons()])
GeoInterface.Wrappers.MultiPolygon{false, false, Vector{GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}[GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(3.0, 0.0), (3.0, 3.0), (0.0, 3.0), (0.0, 0.0), (3.0, 0.0)], nothing, nothing)], nothing, nothing)], nothing, nothing)

You can see that the the multipolygon now only contains one sub-polygon, rather than the two identical ones provided.

Implementation

julia
"""
+GO.fix(multipolygon, corrections = [GO.UnionIntersectingPolygons()])
GeoInterface.Wrappers.MultiPolygon{false, false, Vector{GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}[GeoInterface.Wrappers.Polygon{false, false, Vector{GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}}, Nothing, Nothing}(GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}[GeoInterface.Wrappers.LinearRing{false, false, Vector{Tuple{Float64, Float64}}, Nothing, Nothing}([(0.0, 0.0), (3.0, 0.0), (3.0, 3.0), (0.0, 3.0), (0.0, 0.0)], nothing, nothing)], nothing, nothing)], nothing, nothing)

You can see that the the multipolygon now only contains one sub-polygon, rather than the two identical ones provided.

Implementation

julia
"""
     UnionIntersectingPolygons() <: GeometryCorrection
 
 This correction ensures that the polygon's included in a multipolygon aren't intersecting.
@@ -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/PR195/source/transformations/extent.html b/previews/PR195/source/transformations/extent.html index 9572a2325..9172dd59b 100644 --- a/previews/PR195/source/transformations/extent.html +++ b/previews/PR195/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/PR195/source/transformations/flip.html b/previews/PR195/source/transformations/flip.html index 9f5ab99df..4cdbd9240 100644 --- a/previews/PR195/source/transformations/flip.html +++ b/previews/PR195/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/PR195/source/transformations/reproject.html b/previews/PR195/source/transformations/reproject.html index ddb6760cc..acbd77ba8 100644 --- a/previews/PR195/source/transformations/reproject.html +++ b/previews/PR195/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/PR195/source/transformations/segmentize.html b/previews/PR195/source/transformations/segmentize.html index 294757399..ce0c9bf1f 100644 --- a/previews/PR195/source/transformations/segmentize.html +++ b/previews/PR195/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/PR195/source/transformations/simplify.html b/previews/PR195/source/transformations/simplify.html index bbdac6595..c9fca5434 100644 --- a/previews/PR195/source/transformations/simplify.html +++ b/previews/PR195/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/PR195/source/transformations/transform.html b/previews/PR195/source/transformations/transform.html index 2f56de921..c7b7b4d04 100644 --- a/previews/PR195/source/transformations/transform.html +++ b/previews/PR195/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/PR195/source/transformations/tuples.html b/previews/PR195/source/transformations/tuples.html index 8e6e5a4b9..f23b749e7 100644 --- a/previews/PR195/source/transformations/tuples.html +++ b/previews/PR195/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/PR195/source/types.html b/previews/PR195/source/types.html index 30cbd221c..9fa64dd47 100644 --- a/previews/PR195/source/types.html +++ b/previews/PR195/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/PR195/source/utils.html b/previews/PR195/source/utils.html index 3ead9777a..dde44510c 100644 --- a/previews/PR195/source/utils.html +++ b/previews/PR195/source/utils.html @@ -8,10 +8,10 @@ - + - + @@ -138,7 +138,7 @@ (x1, x2), (y1, y2) = extent.X, extent.Y return x1 ≤ GI.x(p) ≤ x2 && y1 ≤ GI.y(p) ≤ y2 end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR195/tutorials/creating_geometry.html b/previews/PR195/tutorials/creating_geometry.html index c0bd812cc..8c2a23f89 100644 --- a/previews/PR195/tutorials/creating_geometry.html +++ b/previews/PR195/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/PR195/tutorials/geodesic_paths.html b/previews/PR195/tutorials/geodesic_paths.html index 3a072cfd3..1aa77945e 100644 --- a/previews/PR195/tutorials/geodesic_paths.html +++ b/previews/PR195/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/PR195/tutorials/spatial_joins.html b/previews/PR195/tutorials/spatial_joins.html index eb26d9067..3235e810a 100644 --- a/previews/PR195/tutorials/spatial_joins.html +++ b/previews/PR195/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