From f8bcdfdd085e22c9641593a65f74edeba27212c8 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Mon, 5 Aug 2024 10:39:23 +0000 Subject: [PATCH] build based on d1e33f9 --- previews/PR195/404.html | 4 +-- previews/PR195/api.html | 10 +++--- ...{api.md.BOAMfjMD.js => api.md.Uu1jvSAS.js} | 2 +- ...AMfjMD.lean.js => api.md.Uu1jvSAS.lean.js} | 0 .../{app.BsEKKDDb.js => app.D4YnKNup.js} | 2 +- ...kyuq.DwqDxlAG.png => beqgrli.DwqDxlAG.png} | Bin previews/PR195/assets/buspiou.ByFWjvme.png | Bin 66519 -> 0 bytes ...uwzn.0OJvb21A.png => cdyaxrp.0OJvb21A.png} | Bin .../chunks/@localSearchIndexroot.BCaxkeLm.js | 1 - .../chunks/@localSearchIndexroot.Cwlz7_1p.js | 1 + ...9r77KC.js => VPLocalSearchBox.6VHNmFbn.js} | 2 +- .../{theme.BvCUQvTg.js => theme.9qdIUuGq.js} | 4 +-- ...lkux.CLtpJ5Wb.png => daqunkt.CLtpJ5Wb.png} | Bin ...cfvo.DC3TvBOO.png => eoxcftg.DC3TvBOO.png} | Bin previews/PR195/assets/eqwuvgg.BzbnQyYC.png | Bin 0 -> 208141 bytes ... => experiments_predicates.md.CY6VVdB5.js} | 2 +- ...xperiments_predicates.md.CY6VVdB5.lean.js} | 2 +- previews/PR195/assets/fptcvzg.CEAi3Hur.png | Bin 228782 -> 0 bytes ...qeej.mCtKcWOr.png => gfjxxxu.mCtKcWOr.png} | Bin ...geom.3UVIT8DR.png => gvepvsc.3UVIT8DR.png} | Bin ...sdci._0R9BbFk.png => gzdamsq._0R9BbFk.png} | Bin ...tyjf.B9NpLJr_.png => hvzncwg.B9NpLJr_.png} | Bin ...ofty.Cb0_DiYE.png => idfbizv.Cb0_DiYE.png} | Bin previews/PR195/assets/ilxipml.BiyYzhuJ.png | Bin 79707 -> 0 bytes ...lvgi.3sfpQl2i.png => jkhyrrr.3sfpQl2i.png} | Bin previews/PR195/assets/kbweijn.CvG_YJCZ.png | Bin 0 -> 65866 bytes ...mgox.DaovVbE6.png => krnipyg.DaovVbE6.png} | Bin ...mxse.BOOG5oTW.png => kwahkgr.BOOG5oTW.png} | Bin previews/PR195/assets/lahfsdq.DImzSSoe.png | Bin 0 -> 63579 bytes ...mwbc.Dig-DWOQ.png => lezewku.Dig-DWOQ.png} | Bin ...qeby.C3SxJ3x-.png => mifuspz.C3SxJ3x-.png} | Bin ...svkc.CG4dr3Lx.png => muraxhw.CG4dr3Lx.png} | Bin previews/PR195/assets/ngakfcp.CDGBiJCb.png | Bin 75084 -> 0 bytes ...alyu.BEFUMtlf.png => nnmtlyt.BEFUMtlf.png} | Bin previews/PR195/assets/nqwvgdx.DwXe1jXy.png | Bin 0 -> 65294 bytes previews/PR195/assets/opbptnb.CcuM5mrn.png | Bin 60386 -> 0 bytes previews/PR195/assets/orltnjp.Bl1rHoTH.png | Bin 206362 -> 0 bytes previews/PR195/assets/pimzqle.DUfss9cE.png | Bin 0 -> 230468 bytes ...jjou.Bglvb-jp.png => qdctfbg.Bglvb-jp.png} | Bin ...fugo.BD0hVfse.png => qfgsyzp.BD0hVfse.png} | Bin previews/PR195/assets/qplocma.BRlWDsG7.png | Bin 0 -> 79170 bytes ...jncj.Dab1-ETk.png => quljnfx.Dab1-ETk.png} | Bin ...iilt.DHcwB147.png => rjeiiti.DHcwB147.png} | Bin ...cnll.Danh069g.png => rzhkiia.Danh069g.png} | Bin ...defz.DuBHk1fh.png => sgsjzxm.DuBHk1fh.png} | Bin ...s => source_methods_angles.md.BFiryIAp.js} | 2 +- ...source_methods_angles.md.BFiryIAp.lean.js} | 2 +- ....js => source_methods_area.md.BBk_o4kT.js} | 2 +- ...> source_methods_area.md.BBk_o4kT.lean.js} | 2 +- ...source_methods_barycentric.md.IhV5n_Vs.js} | 2 +- ...e_methods_barycentric.md.IhV5n_Vs.lean.js} | 2 +- ...=> source_methods_centroid.md.BNPz6HBb.js} | 2 +- ...urce_methods_centroid.md.BNPz6HBb.lean.js} | 2 +- ..._methods_clipping_coverage.md.gih_Ixny.js} | 2 +- ...ods_clipping_coverage.md.gih_Ixny.lean.js} | 2 +- ...ource_methods_clipping_cut.md.Ufq7LqwY.js} | 2 +- ..._methods_clipping_cut.md.Ufq7LqwY.lean.js} | 2 +- ...source_methods_convex_hull.md.krPAiW_K.js} | 6 ++-- ...e_methods_convex_hull.md.krPAiW_K.lean.js} | 2 +- ...=> source_methods_distance.md.Bd0D210y.js} | 2 +- ...urce_methods_distance.md.Bd0D210y.lean.js} | 2 +- ...s => source_methods_equals.md.C5IDvJmK.js} | 2 +- ...source_methods_equals.md.C5IDvJmK.lean.js} | 2 +- ...ds_geom_relations_contains.md.CEI3G1Lt.js} | 2 +- ...om_relations_contains.md.CEI3G1Lt.lean.js} | 2 +- ...s_geom_relations_coveredby.md.CUwQuZpN.js} | 2 +- ...m_relations_coveredby.md.CUwQuZpN.lean.js} | 2 +- ...hods_geom_relations_covers.md.Cgai50jA.js} | 2 +- ...geom_relations_covers.md.Cgai50jA.lean.js} | 2 +- ...ds_geom_relations_disjoint.md.NbP1nsJg.js} | 2 +- ...om_relations_disjoint.md.NbP1nsJg.lean.js} | 2 +- ..._geom_relations_intersects.md.5Sihtqge.js} | 2 +- ..._relations_intersects.md.5Sihtqge.lean.js} | 2 +- ...ds_geom_relations_overlaps.md.DPk6B9QY.js} | 2 +- ...om_relations_overlaps.md.DPk6B9QY.lean.js} | 2 +- ...ods_geom_relations_touches.md.DGbBQXvM.js} | 2 +- ...eom_relations_touches.md.DGbBQXvM.lean.js} | 2 +- ...hods_geom_relations_within.md.1DNJm7GW.js} | 2 +- ...geom_relations_within.md.1DNJm7GW.lean.js} | 2 +- ...transformations_segmentize.md.BS_Hz255.js} | 2 +- ...formations_segmentize.md.BS_Hz255.lean.js} | 2 +- ...e_transformations_simplify.md.DduEGYcq.js} | 2 +- ...nsformations_simplify.md.DduEGYcq.lean.js} | 2 +- ...rhuk.DeeQUply.png => tdlubmv.DeeQUply.png} | Bin ...utorials_creating_geometry.md.Cmy9Jd1C.js} | 2 +- ...ials_creating_geometry.md.Cmy9Jd1C.lean.js | 1 + ...ials_creating_geometry.md.KPKOdNQp.lean.js | 1 - ...> tutorials_geodesic_paths.md._9mxe3XA.js} | 2 +- ...orials_geodesic_paths.md._9mxe3XA.lean.js} | 2 +- ...=> tutorials_spatial_joins.md.M6zCnTRk.js} | 2 +- ...torials_spatial_joins.md.M6zCnTRk.lean.js} | 2 +- ...mvbl.lu4jwpi-.png => uahqken.lu4jwpi-.png} | Bin previews/PR195/assets/ubioczg.BEJwZXDr.png | Bin 64766 -> 0 bytes ...rbwd.CgiryX2p.png => vvnydlz.CgiryX2p.png} | Bin ...vrcb.Cx40vhB3.png => wfugidq.Cx40vhB3.png} | Bin ...ybxw.DiwGEg2f.png => wpdjkzg.DiwGEg2f.png} | Bin ...veed.Dz86q2IX.png => wwwjtxd.Dz86q2IX.png} | Bin previews/PR195/assets/xaostbm.Do8zVjU_.png | Bin 62224 -> 0 bytes previews/PR195/assets/ximousw.C1wK9Lw2.png | Bin 0 -> 76386 bytes ...iqyk.CULn5saZ.png => xtqwcaw.CULn5saZ.png} | Bin ...naex.B94PsR1K.png => yxpmwxt.B94PsR1K.png} | Bin ...lkkm.CZy9YIUA.png => zaodhix.CZy9YIUA.png} | Bin ...oxvj.-VpeHhXX.png => zbapdnn.-VpeHhXX.png} | Bin previews/PR195/assets/zkurkhd.BHHM5aFD.png | Bin 0 -> 60463 bytes 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 | 6 ++-- 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 | 6 ++-- .../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 ++++---- 160 files changed, 294 insertions(+), 294 deletions(-) rename previews/PR195/assets/{api.md.BOAMfjMD.js => api.md.Uu1jvSAS.js} (99%) rename previews/PR195/assets/{api.md.BOAMfjMD.lean.js => api.md.Uu1jvSAS.lean.js} (100%) rename previews/PR195/assets/{app.BsEKKDDb.js => app.D4YnKNup.js} (95%) rename previews/PR195/assets/{ovekyuq.DwqDxlAG.png => beqgrli.DwqDxlAG.png} (100%) delete mode 100644 previews/PR195/assets/buspiou.ByFWjvme.png rename previews/PR195/assets/{cgvuwzn.0OJvb21A.png => cdyaxrp.0OJvb21A.png} (100%) delete mode 100644 previews/PR195/assets/chunks/@localSearchIndexroot.BCaxkeLm.js create mode 100644 previews/PR195/assets/chunks/@localSearchIndexroot.Cwlz7_1p.js rename previews/PR195/assets/chunks/{VPLocalSearchBox.B29r77KC.js => VPLocalSearchBox.6VHNmFbn.js} (99%) rename previews/PR195/assets/chunks/{theme.BvCUQvTg.js => theme.9qdIUuGq.js} (99%) rename previews/PR195/assets/{caclkux.CLtpJ5Wb.png => daqunkt.CLtpJ5Wb.png} (100%) rename previews/PR195/assets/{yvjcfvo.DC3TvBOO.png => eoxcftg.DC3TvBOO.png} (100%) create mode 100644 previews/PR195/assets/eqwuvgg.BzbnQyYC.png rename previews/PR195/assets/{experiments_predicates.md.CrrYSeMP.js => experiments_predicates.md.CY6VVdB5.js} (99%) rename previews/PR195/assets/{experiments_predicates.md.CrrYSeMP.lean.js => experiments_predicates.md.CY6VVdB5.lean.js} (74%) delete mode 100644 previews/PR195/assets/fptcvzg.CEAi3Hur.png rename previews/PR195/assets/{dhlqeej.mCtKcWOr.png => gfjxxxu.mCtKcWOr.png} (100%) rename previews/PR195/assets/{lztgeom.3UVIT8DR.png => gvepvsc.3UVIT8DR.png} (100%) rename previews/PR195/assets/{hmrsdci._0R9BbFk.png => gzdamsq._0R9BbFk.png} (100%) rename previews/PR195/assets/{ggltyjf.B9NpLJr_.png => hvzncwg.B9NpLJr_.png} (100%) rename previews/PR195/assets/{xmiofty.Cb0_DiYE.png => idfbizv.Cb0_DiYE.png} (100%) delete mode 100644 previews/PR195/assets/ilxipml.BiyYzhuJ.png rename previews/PR195/assets/{pfelvgi.3sfpQl2i.png => jkhyrrr.3sfpQl2i.png} (100%) create mode 100644 previews/PR195/assets/kbweijn.CvG_YJCZ.png rename previews/PR195/assets/{yhbmgox.DaovVbE6.png => krnipyg.DaovVbE6.png} (100%) rename previews/PR195/assets/{dwvmxse.BOOG5oTW.png => kwahkgr.BOOG5oTW.png} (100%) create mode 100644 previews/PR195/assets/lahfsdq.DImzSSoe.png rename previews/PR195/assets/{tdamwbc.Dig-DWOQ.png => lezewku.Dig-DWOQ.png} (100%) rename previews/PR195/assets/{lhkqeby.C3SxJ3x-.png => mifuspz.C3SxJ3x-.png} (100%) rename previews/PR195/assets/{gcksvkc.CG4dr3Lx.png => muraxhw.CG4dr3Lx.png} (100%) delete mode 100644 previews/PR195/assets/ngakfcp.CDGBiJCb.png rename previews/PR195/assets/{mcqalyu.BEFUMtlf.png => nnmtlyt.BEFUMtlf.png} (100%) create mode 100644 previews/PR195/assets/nqwvgdx.DwXe1jXy.png delete mode 100644 previews/PR195/assets/opbptnb.CcuM5mrn.png delete mode 100644 previews/PR195/assets/orltnjp.Bl1rHoTH.png create mode 100644 previews/PR195/assets/pimzqle.DUfss9cE.png rename previews/PR195/assets/{soxjjou.Bglvb-jp.png => qdctfbg.Bglvb-jp.png} (100%) rename previews/PR195/assets/{fnrfugo.BD0hVfse.png => qfgsyzp.BD0hVfse.png} (100%) create mode 100644 previews/PR195/assets/qplocma.BRlWDsG7.png rename previews/PR195/assets/{mosjncj.Dab1-ETk.png => quljnfx.Dab1-ETk.png} (100%) rename previews/PR195/assets/{udpiilt.DHcwB147.png => rjeiiti.DHcwB147.png} (100%) rename previews/PR195/assets/{pehcnll.Danh069g.png => rzhkiia.Danh069g.png} (100%) rename previews/PR195/assets/{prxdefz.DuBHk1fh.png => sgsjzxm.DuBHk1fh.png} (100%) rename previews/PR195/assets/{source_methods_angles.md.CE49jq22.js => source_methods_angles.md.BFiryIAp.js} (99%) rename previews/PR195/assets/{source_methods_angles.md.CE49jq22.lean.js => source_methods_angles.md.BFiryIAp.lean.js} (87%) rename previews/PR195/assets/{source_methods_area.md.CVTC71h0.js => source_methods_area.md.BBk_o4kT.js} (99%) rename previews/PR195/assets/{source_methods_area.md.CVTC71h0.lean.js => source_methods_area.md.BBk_o4kT.lean.js} (77%) rename previews/PR195/assets/{source_methods_barycentric.md.Cs4dTCTH.js => source_methods_barycentric.md.IhV5n_Vs.js} (99%) rename previews/PR195/assets/{source_methods_barycentric.md.Cs4dTCTH.lean.js => source_methods_barycentric.md.IhV5n_Vs.lean.js} (99%) rename previews/PR195/assets/{source_methods_centroid.md.BHSz4F0b.js => source_methods_centroid.md.BNPz6HBb.js} (99%) rename previews/PR195/assets/{source_methods_centroid.md.BHSz4F0b.lean.js => source_methods_centroid.md.BNPz6HBb.lean.js} (77%) rename previews/PR195/assets/{source_methods_clipping_coverage.md.A7dmJGZH.js => source_methods_clipping_coverage.md.gih_Ixny.js} (99%) rename previews/PR195/assets/{source_methods_clipping_coverage.md.A7dmJGZH.lean.js => source_methods_clipping_coverage.md.gih_Ixny.lean.js} (87%) rename previews/PR195/assets/{source_methods_clipping_cut.md.D2wwxGIq.js => source_methods_clipping_cut.md.Ufq7LqwY.js} (99%) rename previews/PR195/assets/{source_methods_clipping_cut.md.D2wwxGIq.lean.js => source_methods_clipping_cut.md.Ufq7LqwY.lean.js} (87%) rename previews/PR195/assets/{source_methods_convex_hull.md.CoBp1HPw.js => source_methods_convex_hull.md.krPAiW_K.js} (98%) rename previews/PR195/assets/{source_methods_convex_hull.md.CoBp1HPw.lean.js => source_methods_convex_hull.md.krPAiW_K.lean.js} (59%) rename previews/PR195/assets/{source_methods_distance.md.CIQ2kRRk.js => source_methods_distance.md.Bd0D210y.js} (99%) rename previews/PR195/assets/{source_methods_distance.md.CIQ2kRRk.lean.js => source_methods_distance.md.Bd0D210y.lean.js} (78%) rename previews/PR195/assets/{source_methods_equals.md.CG2GX2G-.js => source_methods_equals.md.C5IDvJmK.js} (99%) rename previews/PR195/assets/{source_methods_equals.md.CG2GX2G-.lean.js => source_methods_equals.md.C5IDvJmK.lean.js} (87%) rename previews/PR195/assets/{source_methods_geom_relations_contains.md.Cpcc7vXO.js => source_methods_geom_relations_contains.md.CEI3G1Lt.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_contains.md.Cpcc7vXO.lean.js => source_methods_geom_relations_contains.md.CEI3G1Lt.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_coveredby.md.Dj_nAWvy.js => source_methods_geom_relations_coveredby.md.CUwQuZpN.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_coveredby.md.Dj_nAWvy.lean.js => source_methods_geom_relations_coveredby.md.CUwQuZpN.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_covers.md.CSDFydXd.js => source_methods_geom_relations_covers.md.Cgai50jA.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_covers.md.CSDFydXd.lean.js => source_methods_geom_relations_covers.md.Cgai50jA.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_disjoint.md.DiBqaDAE.js => source_methods_geom_relations_disjoint.md.NbP1nsJg.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_disjoint.md.DiBqaDAE.lean.js => source_methods_geom_relations_disjoint.md.NbP1nsJg.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_intersects.md.BsITeqCZ.js => source_methods_geom_relations_intersects.md.5Sihtqge.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_intersects.md.BsITeqCZ.lean.js => source_methods_geom_relations_intersects.md.5Sihtqge.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_overlaps.md.CVfdoel4.js => source_methods_geom_relations_overlaps.md.DPk6B9QY.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_overlaps.md.CVfdoel4.lean.js => source_methods_geom_relations_overlaps.md.DPk6B9QY.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_touches.md.qExTc2Ih.js => source_methods_geom_relations_touches.md.DGbBQXvM.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_touches.md.qExTc2Ih.lean.js => source_methods_geom_relations_touches.md.DGbBQXvM.lean.js} (88%) rename previews/PR195/assets/{source_methods_geom_relations_within.md.B_OARYtU.js => source_methods_geom_relations_within.md.1DNJm7GW.js} (99%) rename previews/PR195/assets/{source_methods_geom_relations_within.md.B_OARYtU.lean.js => source_methods_geom_relations_within.md.1DNJm7GW.lean.js} (88%) rename previews/PR195/assets/{source_transformations_segmentize.md.udwcI5Oo.js => source_transformations_segmentize.md.BS_Hz255.js} (99%) rename previews/PR195/assets/{source_transformations_segmentize.md.udwcI5Oo.lean.js => source_transformations_segmentize.md.BS_Hz255.lean.js} (67%) rename previews/PR195/assets/{source_transformations_simplify.md.DN9O_9Po.js => source_transformations_simplify.md.DduEGYcq.js} (99%) rename previews/PR195/assets/{source_transformations_simplify.md.DN9O_9Po.lean.js => source_transformations_simplify.md.DduEGYcq.lean.js} (56%) rename previews/PR195/assets/{smqrhuk.DeeQUply.png => tdlubmv.DeeQUply.png} (100%) rename previews/PR195/assets/{tutorials_creating_geometry.md.KPKOdNQp.js => tutorials_creating_geometry.md.Cmy9Jd1C.js} (99%) create mode 100644 previews/PR195/assets/tutorials_creating_geometry.md.Cmy9Jd1C.lean.js delete mode 100644 previews/PR195/assets/tutorials_creating_geometry.md.KPKOdNQp.lean.js rename previews/PR195/assets/{tutorials_geodesic_paths.md.BxIfIMgw.js => tutorials_geodesic_paths.md._9mxe3XA.js} (98%) rename previews/PR195/assets/{tutorials_geodesic_paths.md.BxIfIMgw.lean.js => tutorials_geodesic_paths.md._9mxe3XA.lean.js} (87%) rename previews/PR195/assets/{tutorials_spatial_joins.md.DnrEv8Ha.js => tutorials_spatial_joins.md.M6zCnTRk.js} (99%) rename previews/PR195/assets/{tutorials_spatial_joins.md.DnrEv8Ha.lean.js => tutorials_spatial_joins.md.M6zCnTRk.lean.js} (92%) rename previews/PR195/assets/{xefmvbl.lu4jwpi-.png => uahqken.lu4jwpi-.png} (100%) delete mode 100644 previews/PR195/assets/ubioczg.BEJwZXDr.png rename previews/PR195/assets/{fkyrbwd.CgiryX2p.png => vvnydlz.CgiryX2p.png} (100%) rename previews/PR195/assets/{sjtvrcb.Cx40vhB3.png => wfugidq.Cx40vhB3.png} (100%) rename previews/PR195/assets/{itpybxw.DiwGEg2f.png => wpdjkzg.DiwGEg2f.png} (100%) rename previews/PR195/assets/{sxiveed.Dz86q2IX.png => wwwjtxd.Dz86q2IX.png} (100%) delete mode 100644 previews/PR195/assets/xaostbm.Do8zVjU_.png create mode 100644 previews/PR195/assets/ximousw.C1wK9Lw2.png rename previews/PR195/assets/{lrliqyk.CULn5saZ.png => xtqwcaw.CULn5saZ.png} (100%) rename previews/PR195/assets/{deanaex.B94PsR1K.png => yxpmwxt.B94PsR1K.png} (100%) rename previews/PR195/assets/{clplkkm.CZy9YIUA.png => zaodhix.CZy9YIUA.png} (100%) rename previews/PR195/assets/{sphoxvj.-VpeHhXX.png => zbapdnn.-VpeHhXX.png} (100%) create mode 100644 previews/PR195/assets/zkurkhd.BHHM5aFD.png diff --git a/previews/PR195/404.html b/previews/PR195/404.html index 49b14b943..07061514d 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 b5df37f5f..fb81d24ff 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.BOAMfjMD.js b/previews/PR195/assets/api.md.Uu1jvSAS.js similarity index 99% rename from previews/PR195/assets/api.md.BOAMfjMD.js rename to previews/PR195/assets/api.md.Uu1jvSAS.js index 8f581019a..1b5387cf9 100644 --- a/previews/PR195/assets/api.md.BOAMfjMD.js +++ b/previews/PR195/assets/api.md.Uu1jvSAS.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.BOAMfjMD.lean.js b/previews/PR195/assets/api.md.Uu1jvSAS.lean.js
similarity index 100%
rename from previews/PR195/assets/api.md.BOAMfjMD.lean.js
rename to previews/PR195/assets/api.md.Uu1jvSAS.lean.js
diff --git a/previews/PR195/assets/app.BsEKKDDb.js b/previews/PR195/assets/app.D4YnKNup.js
similarity index 95%
rename from previews/PR195/assets/app.BsEKKDDb.js
rename to previews/PR195/assets/app.D4YnKNup.js
index 03128d1c6..49254b196 100644
--- a/previews/PR195/assets/app.BsEKKDDb.js
+++ b/previews/PR195/assets/app.D4YnKNup.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.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};
+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.9qdIUuGq.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/ovekyuq.DwqDxlAG.png b/previews/PR195/assets/beqgrli.DwqDxlAG.png
similarity index 100%
rename from previews/PR195/assets/ovekyuq.DwqDxlAG.png
rename to previews/PR195/assets/beqgrli.DwqDxlAG.png
diff --git a/previews/PR195/assets/buspiou.ByFWjvme.png b/previews/PR195/assets/buspiou.ByFWjvme.png
deleted file mode 100644
index cb3eaaf97af7a10cfef137b90f8c44e9088545c9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

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 2af0d857f..e0b36478e 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 9d79d659a..28c17aa08 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 f05335585..dd06d3770 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 c80188552..c135d396c 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 a0ff55636..0af2f3f44 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 b6ba85ead..2ae013631 100644 --- a/previews/PR195/hashmap.json +++ b/previews/PR195/hashmap.json @@ -1 +1 @@ -{"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"} +{"api.md":"Uu1jvSAS","call_notes.md":"7w_VILqE","experiments_accurate_accumulators.md":"GjJQmUUF","experiments_predicates.md":"CY6VVdB5","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":"BFiryIAp","source_methods_area.md":"BBk_o4kT","source_methods_barycentric.md":"IhV5n_Vs","source_methods_buffer.md":"DU5YIRhh","source_methods_centroid.md":"BNPz6HBb","source_methods_clipping_clipping_processor.md":"CtF2CQFc","source_methods_clipping_coverage.md":"gih_Ixny","source_methods_clipping_cut.md":"Ufq7LqwY","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":"krPAiW_K","source_methods_distance.md":"Bd0D210y","source_methods_equals.md":"C5IDvJmK","source_methods_geom_relations_contains.md":"CEI3G1Lt","source_methods_geom_relations_coveredby.md":"CUwQuZpN","source_methods_geom_relations_covers.md":"Cgai50jA","source_methods_geom_relations_crosses.md":"BHrKZtR2","source_methods_geom_relations_disjoint.md":"NbP1nsJg","source_methods_geom_relations_geom_geom_processors.md":"C9HuuL9H","source_methods_geom_relations_intersects.md":"5Sihtqge","source_methods_geom_relations_overlaps.md":"DPk6B9QY","source_methods_geom_relations_touches.md":"DGbBQXvM","source_methods_geom_relations_within.md":"1DNJm7GW","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":"BS_Hz255","source_transformations_simplify.md":"DduEGYcq","source_transformations_transform.md":"CQbjpjpu","source_transformations_tuples.md":"De5DcGB5","source_types.md":"Df5Ol6GF","source_utils.md":"BvaNAnX0","tutorials_creating_geometry.md":"Cmy9Jd1C","tutorials_geodesic_paths.md":"_9mxe3XA","tutorials_spatial_joins.md":"M6zCnTRk"} diff --git a/previews/PR195/index.html b/previews/PR195/index.html index ca7a2ddcc..7259a12b1 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 8a28917b2..1df58efae 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 52cd8543b..ecb8ccf15 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 543865bb3..cc2ea13b1 100644 --- a/previews/PR195/source/lazy_wrappers.html +++ b/previews/PR195/source/lazy_wrappers.html @@ -8,10 +8,10 @@ - + - + @@ -124,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 5222f0bb0..f6309b4c7 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 6f95520ab..ec8f738fc 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 2a8ec9881..a80307b56 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 4deae9269..1fec91910 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 19212260e..71a293fda 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 2c280437a..77a66db41 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 5f7e6a0e8..f0bfb78d8 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 546225cce..7ad62d8b9 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 97d9f9607..d1195f941 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 69012b28a..85b64f749 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 59028794e..fa3a4f5f4 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 aacf08dba..f8ed72b73 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 625a17983..6071649b5 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 c6c49e271..871c1210b 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 d46fd0437..357a18c99 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 72f3fca7b..1c307a408 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 1002e9148..bc15b6f37 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 bfed3d488..e2cd8a717 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 f587d9881..3bf0c113b 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 0c103937c..d15a9e7c3 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 cfe87fb1d..f0296e0d9 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 fdab74210..0a8a38975 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 a10133d3c..a0c4e021b 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 a4a178f5b..142dc06ae 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 7441fd4ad..837c92127 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 b1697c4ee..4c7371727 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 bc8a58beb..4041ad985 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 fc11ffc78..d7a1e29aa 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 8afaf6d8e..2cf6bdaef 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 bf92d0324..e99dc486c 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 b2fd28f20..8c16c3774 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 4a723641a..2faac8de0 100644 --- a/previews/PR195/source/transformations/correction/intersecting_polygons.html +++ b/previews/PR195/source/transformations/correction/intersecting_polygons.html @@ -8,10 +8,10 @@ - + - + @@ -115,7 +115,7 @@ end return diff_multipoly end

This page was generated using Literate.jl.

- + \ No newline at end of file diff --git a/previews/PR195/source/transformations/extent.html b/previews/PR195/source/transformations/extent.html index 9172dd59b..be3e0110f 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 4cdbd9240..78472d2e1 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 acbd77ba8..e5fdc0fa0 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 ce0c9bf1f..3160258cb 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 c9fca5434..1a2893a72 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 c7b7b4d04..35f92e452 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 f23b749e7..adfbd549b 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 9fa64dd47..d35f52674 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 dde44510c..b41ffbc37 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 8c2a23f89..e05bbeb18 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 1aa77945e..90732389a 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 3235e810a..f5af0719e 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