From fd1645b3ddcf9a391151ec851039c6ff27c283d9 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 29 Jan 2022 20:25:12 -0400 Subject: [PATCH 01/88] separate existing c utility into one folder --- src/Makevars.in | 2 +- src/Makevars.win | 2 +- .../capi-tessellator.cpp} | 6 +++- src/s2-capi/s2_c.h | 27 +++++++++++++++++ src/wk-c-utils.c | 30 ++----------------- 5 files changed, 36 insertions(+), 31 deletions(-) rename src/{s2-c-api.cpp => s2-capi/capi-tessellator.cpp} (98%) create mode 100644 src/s2-capi/s2_c.h diff --git a/src/Makevars.in b/src/Makevars.in index 943c0de8..ecce8d00 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -103,7 +103,7 @@ OBJECTS = $(ABSL_LIBS) \ s2-xptr.o \ wk-impl.o \ wk-c-utils.o \ - s2-c-api.o \ + s2-capi/capi-tessellator.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ s2/encoded_s2cell_id_vector.o \ diff --git a/src/Makevars.win b/src/Makevars.win index e87c2d08..b3090d41 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -88,7 +88,7 @@ ABSL_LIBS = absl/base/internal/cycleclock.o \ absl/types/bad_variant_access.o S2LIBS = $(ABSL_LIBS) \ - s2-c-api.o \ + s2-capi/capi-tessellator.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ s2/encoded_s2cell_id_vector.o \ diff --git a/src/s2-c-api.cpp b/src/s2-capi/capi-tessellator.cpp similarity index 98% rename from src/s2-c-api.cpp rename to src/s2-capi/capi-tessellator.cpp index 28d55e89..42db4c75 100644 --- a/src/s2-c-api.cpp +++ b/src/s2-capi/capi-tessellator.cpp @@ -4,13 +4,16 @@ #include "s2/s2projections.h" #include "s2/s2edge_tessellator.h" +// capi typedef start typedef struct s2_projection_t s2_projection_t; typedef struct s2_tessellator_t s2_tessellator_t; +// capi typedef end #ifdef __cplusplus extern "C" { #endif +// capi func start s2_projection_t* s2_projection_create_plate_carree(double scale); s2_projection_t* s2_projection_create_mercator(double max_x); void s2_projection_destroy(s2_projection_t* projection); @@ -26,6 +29,7 @@ int s2_tessellator_r2_points_size(s2_tessellator_t* tessellator); int s2_tessellator_s2_points_size(s2_tessellator_t* tessellator); int s2_tessellator_r2_point(s2_tessellator_t* tessellator, int i, double* coord); int s2_tessellator_s2_point(s2_tessellator_t* tessellator, int i, double* coord); +// capi func end #ifdef __cplusplus } @@ -69,7 +73,7 @@ class TessellatorWrapper { TessellatorWrapper(s2_projection_t* projection, double tolerance_radians): tessellator((S2::Projection*) projection, S1Angle::Radians(tolerance_radians)), has_r2_last(false), has_s2_last(false) {} - + void reset() { s2points.clear(); r2points.clear(); diff --git a/src/s2-capi/s2_c.h b/src/s2-capi/s2_c.h new file mode 100644 index 00000000..3e06c161 --- /dev/null +++ b/src/s2-capi/s2_c.h @@ -0,0 +1,27 @@ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct s2_projection_t s2_projection_t; +typedef struct s2_tessellator_t s2_tessellator_t; + +s2_projection_t* s2_projection_create_plate_carree(double scale); +s2_projection_t* s2_projection_create_mercator(double max_x); +void s2_projection_destroy(s2_projection_t* projection); +int s2_projection_project(s2_projection_t* projection, const double* coord_in, double* coord_out); +int s2_projection_unproject(s2_projection_t* projection, const double* coord_in, double* coord_out); + +s2_tessellator_t* s2_tessellator_create(s2_projection_t* projection, double tolerance_radians); +void s2_tessellator_destroy(s2_tessellator_t* tessellator); +int s2_tessellator_reset(s2_tessellator_t* tessellator); +int s2_tessellator_add_r2_point(s2_tessellator_t* tessellator, const double* coord); +int s2_tessellator_add_s2_point(s2_tessellator_t* tessellator, const double* coord); +int s2_tessellator_r2_points_size(s2_tessellator_t* tessellator); +int s2_tessellator_s2_points_size(s2_tessellator_t* tessellator); +int s2_tessellator_r2_point(s2_tessellator_t* tessellator, int i, double* coord); +int s2_tessellator_s2_point(s2_tessellator_t* tessellator, int i, double* coord); + +#ifdef __cplusplus +} +#endif diff --git a/src/wk-c-utils.c b/src/wk-c-utils.c index 4c24ca5e..080376a4 100644 --- a/src/wk-c-utils.c +++ b/src/wk-c-utils.c @@ -3,33 +3,7 @@ #include #include #include "wk-v1.h" - -typedef struct s2_projection_t s2_projection_t; -typedef struct s2_tessellator_t s2_tessellator_t; - -#ifdef __cplusplus -extern "C" { -#endif - -s2_projection_t* s2_projection_create_plate_carree(double scale); -s2_projection_t* s2_projection_create_mercator(double max_x); -void s2_projection_destroy(s2_projection_t* projection); -int s2_projection_project(s2_projection_t* projection, const double* coord_in, double* coord_out); -int s2_projection_unproject(s2_projection_t* projection, const double* coord_in, double* coord_out); - -s2_tessellator_t* s2_tessellator_create(s2_projection_t* projection, double tolerance_radians); -void s2_tessellator_destroy(s2_tessellator_t* tessellator); -int s2_tessellator_reset(s2_tessellator_t* tessellator); -int s2_tessellator_add_r2_point(s2_tessellator_t* tessellator, const double* coord); -int s2_tessellator_add_s2_point(s2_tessellator_t* tessellator, const double* coord); -int s2_tessellator_r2_points_size(s2_tessellator_t* tessellator); -int s2_tessellator_s2_points_size(s2_tessellator_t* tessellator); -int s2_tessellator_r2_point(s2_tessellator_t* tessellator, int i, double* coord); -int s2_tessellator_s2_point(s2_tessellator_t* tessellator, int i, double* coord); - -#ifdef __cplusplus -} -#endif +#include "s2-capi/s2_c.h" // Expose projections as external pointers so that they can theoretically be generated // by other packages. @@ -348,7 +322,7 @@ SEXP c_s2_coord_filter_new(SEXP handler_xptr, SEXP projection_xptr, SEXP unproje } else { coord_filter->tessellator = NULL; } - + coord_filter->unproject = LOGICAL(unproject)[0]; if (coord_filter->unproject) { handler->coord = &s2_coord_filter_coord_unproject; From 6a47443106a0cf0b34fa8429a4da8fcbe34baa11 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 31 Jan 2022 07:39:16 -0400 Subject: [PATCH 02/88] start on rewriting geography abstraction --- src/Makevars.in | 1 + src/Makevars.win | 1 + src/s2-capi/internal/s2_c_common.hpp | 130 +++++++++++++++++++++++++++ src/s2-capi/s2_c_common.cpp | 103 +++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 src/s2-capi/internal/s2_c_common.hpp create mode 100644 src/s2-capi/s2_c_common.cpp diff --git a/src/Makevars.in b/src/Makevars.in index ecce8d00..9b848af6 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -104,6 +104,7 @@ OBJECTS = $(ABSL_LIBS) \ wk-impl.o \ wk-c-utils.o \ s2-capi/capi-tessellator.o \ + s2-capi/s2_c_common.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ s2/encoded_s2cell_id_vector.o \ diff --git a/src/Makevars.win b/src/Makevars.win index b3090d41..b2c95696 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -89,6 +89,7 @@ ABSL_LIBS = absl/base/internal/cycleclock.o \ S2LIBS = $(ABSL_LIBS) \ s2-capi/capi-tessellator.o \ + s2-capi/s2_c_common.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ s2/encoded_s2cell_id_vector.o \ diff --git a/src/s2-capi/internal/s2_c_common.hpp b/src/s2-capi/internal/s2_c_common.hpp new file mode 100644 index 00000000..0fbbc767 --- /dev/null +++ b/src/s2-capi/internal/s2_c_common.hpp @@ -0,0 +1,130 @@ + +#include +#include +#include + +#include "s2/s2polygon.h" +#include "s2/s2polyline.h" + +class S2CAPIError: public std::runtime_error { +public: + S2CAPIError(std::string what): std::runtime_error(what.c_str()) {} +}; + + +// An S2Geography is an abstraction of S2 types that is designed to closely match +// the scope of a GEOS Geometry. Its methods are limited to those needed to +// implement C API functions. From an S2 perspective, an S2Geography is +// an S2Region that can be represented by zero or more S2Shape objects. +// Current implementations of S2Geography own their data (i.e., the +// coordinate vectors and underlying S2 objects), however, the interface is +// designed to allow future abstractions where this is not the case. +class S2Geography { +public: + + // The number of S2Shape objects needed to represent this S2Geography + virtual int num_shapes() = 0; + + // Returns the given S2Shape (where 0 <= id < num_shapes()). The + // caller retains ownership of the S2Shape but the data pointed to + // by the object requires that the underlying S2Geography outlives + // the returned object. + virtual std::unique_ptr Shape(int id) = 0; + + // Returns an S2Region that represents the object. The caller retains + // ownership of the S2Region but the data pointed to by the object + // requires that the underlying S2Geography outlives the returned + // object. + virtual std::unique_ptr Region() = 0; + + // Adds an unnormalized set of S2CellIDs to `cell_ids`. This is intended + // to be faster than using Region().GetCovering() directly and to + // return a small number of cells that can be used to compute a possible + // intersection quickly. + virtual void GetCellUnionBound(std::vector* cell_ids); +}; + + +// capi typedef start +typedef struct s2_geography_t s2_geography_t; +// capi typedef end + + +// An S2Geography representing zero or more points using a std::vector +// as the underlying representation. +class S2GeographyOwningPoint: public S2Geography { +public: + S2GeographyOwningPoint() {} + S2GeographyOwningPoint(S2Point point): points_(1) { points_.push_back(point); } + S2GeographyOwningPoint(std::vector points): points_(std::move(points)) {} + + int num_shapes() { return 1; } + std::unique_ptr Shape(int id); + std::unique_ptr Region(); + void GetCellUnionBound(std::vector* cell_ids); + +private: + std::vector points_; +}; + + +// An S2Geography representing zero or more polylines using the S2Polyline class +// as the underlying representation. +class S2GeographyOwningPolyline: public S2Geography { +public: + S2GeographyOwningPolyline() {} + S2GeographyOwningPolyline(std::vector> polylines): + polylines_(std::move(polylines)) {} + + int num_shapes(); + std::unique_ptr Shape(int id); + std::unique_ptr Region(); + void GetCellUnionBound(std::vector* cell_ids); + +private: + std::vector> polylines_; +}; + + +// An S2Geography representing zero or more polygons using the S2Polygon class +// as the underlying representation. Note that a single S2Polygon (from the S2 +// perspective) can represent zero or more polygons (from the simple features +// perspective). +class S2GeographyOwningPolygon: public S2Geography { +public: + S2GeographyOwningPolygon() {} + S2GeographyOwningPolygon(std::unique_ptr polygon): + polygon_(std::move(polygon)) {} + + int num_shapes() { return 1; } + std::unique_ptr Shape(int id); + std::unique_ptr Region(); + void GetCellUnionBound(std::vector* cell_ids); + +private: + std::unique_ptr polygon_; +}; + + +// An S2Geography wrapping zero or more S2Geography objects. These objects +// can be used to represent a simple features GEOMETRYCOLLECTION. +class S2GeographyCollection: public S2Geography { +public: + S2GeographyCollection(std::vector> features): + features_(std::move(features)), num_shapes_(features_.size()), + total_shapes_(0) { + for (const auto& feature: features_) { + num_shapes_.push_back(feature->num_shapes()); + total_shapes_ += feature->num_shapes(); + } + } + + int num_shapes(); + std::unique_ptr Shape(int id); + std::unique_ptr Region(); + +private: + std::vector> features_; + std::vector num_shapes_; + int total_shapes_; +}; diff --git a/src/s2-capi/s2_c_common.cpp b/src/s2-capi/s2_c_common.cpp new file mode 100644 index 00000000..f0dcf7ba --- /dev/null +++ b/src/s2-capi/s2_c_common.cpp @@ -0,0 +1,103 @@ + +#include "s2/s2shape.h" +#include "s2/s2region.h" +#include "s2/s2region_union.h" + +#include "s2/s2point_region.h" +#include "s2/s2point_vector_shape.h" +#include "s2/s2polyline.h" +#include "s2/s2polygon.h" + +#include "s2/mutable_s2shape_index.h" +#include "s2/s2shape_index_region.h" + +#include "internal/s2_c_common.hpp" + + +void S2Geography::GetCellUnionBound(std::vector* cell_ids) { + MutableS2ShapeIndex index; + for (int i = 0; i < num_shapes(); i++) { + index.Add(Shape(i)); + } + + MakeS2ShapeIndexRegion(&index).GetCellUnionBound(cell_ids); +} + +std::unique_ptr S2GeographyOwningPoint::Shape(int id) { + return absl::make_unique(points_); +} + +std::unique_ptr S2GeographyOwningPoint::Region() { + auto region = absl::make_unique(); + for (const S2Point& point: points_) { + region->Add(absl::make_unique(point)); + } + + return region; +} + +void S2GeographyOwningPoint::GetCellUnionBound(std::vector* cell_ids) { + if (points_.size() < 10) { + for (const S2Point& point: points_) { + cell_ids->push_back(S2CellId(point)); + } + } else { + S2Geography::GetCellUnionBound(cell_ids); + } +} + + +int S2GeographyOwningPolyline::num_shapes() { return polylines_.size(); } + +std::unique_ptr S2GeographyOwningPolyline::Shape(int id) { + return absl::make_unique(polylines_[id].get()); +} + +std::unique_ptr S2GeographyOwningPolyline::Region() { + auto region = absl::make_unique(); + for (const auto& polyline: polylines_) { + region->Add(std::unique_ptr(polyline->Clone())); + } + return region; +} + +void S2GeographyOwningPolyline::GetCellUnionBound(std::vector* cell_ids) { + for (const auto& polyline: polylines_) { + polyline->GetCellUnionBound(cell_ids); + } +} + +std::unique_ptr S2GeographyOwningPolygon::Shape(int id) { + return absl::make_unique(polygon_.get()); +} + +std::unique_ptr S2GeographyOwningPolygon::Region() { + return std::unique_ptr(polygon_->Clone()); +} + +void S2GeographyOwningPolygon::GetCellUnionBound(std::vector* cell_ids) { + polygon_->GetCellUnionBound(cell_ids); +} + + +int S2GeographyCollection::num_shapes() { return total_shapes_; } + +std::unique_ptr S2GeographyCollection::Shape(int id) { + int sum_shapes_ = 0; + for (int i = 0; i < total_shapes_; i++) { + sum_shapes_ += num_shapes_[i]; + if (id < sum_shapes_) { + return features_[i]->Shape(id - sum_shapes_ + num_shapes_[i]); + } + } + + throw S2CAPIError("shape id out of bounds"); +} + +std::unique_ptr S2GeographyCollection::Region() { + auto region = absl::make_unique(); + for (const auto& feature: features_) { + region->Add(feature->Region()); + } + return region; +} From 80334a09edcc57a0fc30e97f6d11ac2e6843fbe2 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 3 Feb 2022 21:25:24 -0400 Subject: [PATCH 03/88] scaffold some new geography bits in --- configure | 5 +++++ src/geography-collection.h | 6 ++++++ src/geography.h | 8 ++++++++ src/point-geography.h | 8 +++++++- src/polygon-geography.h | 6 ++++++ src/polyline-geography.h | 8 +++++++- src/s2-capi/internal/s2_c_common.hpp | 2 ++ src/s2-capi/s2_c.h | 1 + 8 files changed, 42 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 57f73a01..cf50b7d9 100755 --- a/configure +++ b/configure @@ -106,6 +106,11 @@ else PKG_CFLAGS="$PKG_CFLAGS -DIS_BIG_ENDIAN" fi +# Flag for developers to turn on the new C API +if [ "$S2_R_USE_C_API" ]; then + PKG_CFLAGS="$PKG_CFLAGS -DS2_R_USE_C_API" +fi + echo "Using PKG_LIBS=$PKG_LIBS" echo "Using PKG_CFLAGS=$PKG_CFLAGS" diff --git a/src/geography-collection.h b/src/geography-collection.h index 3c2b6cbd..d215050a 100644 --- a/src/geography-collection.h +++ b/src/geography-collection.h @@ -13,6 +13,12 @@ class GeographyCollection: public Geography { GeographyCollection(std::vector> features): features(std::move(features)) {} +#ifdef S2_R_USE_C_API + std::unique_ptr NewGeography() { + return nullptr; + } +#endif + Geography::Type GeographyType() { return Geography::Type::GEOGRAPHY_COLLECTION; } diff --git a/src/geography.h b/src/geography.h index 20f47149..f7d93c2f 100644 --- a/src/geography.h +++ b/src/geography.h @@ -14,6 +14,10 @@ #include "wk/geometry-handler.hpp" #include +#ifdef S2_R_USE_C_API +#include "s2-capi/internal/s2_c_common.hpp" +#endif + class Geography { public: @@ -51,6 +55,10 @@ class Geography { virtual S2Point Centroid() = 0; virtual std::unique_ptr Boundary() = 0; +#ifdef S2_R_USE_C_API + virtual std::unique_ptr NewGeography() = 0; +#endif + // every type will build the index differently based on // the underlying data, and this can (should?) be done // lazily. Returns a vector of shape IDs so the caller diff --git a/src/point-geography.h b/src/point-geography.h index c4f16f0a..da89d266 100644 --- a/src/point-geography.h +++ b/src/point-geography.h @@ -19,6 +19,12 @@ class PointGeography: public Geography { } PointGeography(std::vector points): points(points) {} +#ifdef S2_R_USE_C_API + std::unique_ptr NewGeography() { + return nullptr; + } +#endif + Geography::Type GeographyType() { return Geography::Type::GEOGRAPHY_POINT; } @@ -87,7 +93,7 @@ class PointGeography: public Geography { } S2LatLngRect GetRectBound() { - S2LatLngRect rect; + S2LatLngRect rect; for (size_t i = 0; i < this->points.size(); i++) { rect.AddPoint(this->points[i]); // depends on order } diff --git a/src/polygon-geography.h b/src/polygon-geography.h index db287718..adfe5a82 100644 --- a/src/polygon-geography.h +++ b/src/polygon-geography.h @@ -16,6 +16,12 @@ class PolygonGeography: public Geography { PolygonGeography(std::unique_ptr polygon): polygon(std::move(polygon)) {} +#ifdef S2_R_USE_C_API + std::unique_ptr NewGeography() { + return nullptr; + } +#endif + Geography::Type GeographyType() { return Geography::Type::GEOGRAPHY_POLYGON; } diff --git a/src/polyline-geography.h b/src/polyline-geography.h index 32ffef12..450b85d3 100644 --- a/src/polyline-geography.h +++ b/src/polyline-geography.h @@ -14,6 +14,12 @@ class PolylineGeography: public Geography { PolylineGeography(std::vector> polylines): polylines(std::move(polylines)) {} +#ifdef S2_R_USE_C_API + std::unique_ptr NewGeography() { + return nullptr; + } +#endif + Geography::Type GeographyType() { return Geography::Type::GEOGRAPHY_POLYLINE; } @@ -97,7 +103,7 @@ class PolylineGeography: public Geography { } S2LatLngRect GetRectBound() { - S2LatLngRect rect; + S2LatLngRect rect; if (this->polylines.size()) rect = this->polylines[0]->GetRectBound(); for (size_t i = 1; i < this->polylines.size(); i++) { diff --git a/src/s2-capi/internal/s2_c_common.hpp b/src/s2-capi/internal/s2_c_common.hpp index 0fbbc767..3c7fb16e 100644 --- a/src/s2-capi/internal/s2_c_common.hpp +++ b/src/s2-capi/internal/s2_c_common.hpp @@ -22,6 +22,8 @@ class S2CAPIError: public std::runtime_error { class S2Geography { public: + virtual ~S2Geography() {} + // The number of S2Shape objects needed to represent this S2Geography virtual int num_shapes() = 0; diff --git a/src/s2-capi/s2_c.h b/src/s2-capi/s2_c.h index 3e06c161..2deb99bc 100644 --- a/src/s2-capi/s2_c.h +++ b/src/s2-capi/s2_c.h @@ -3,6 +3,7 @@ extern "C" { #endif +typedef struct s2_geography_t s2_geography_t; typedef struct s2_projection_t s2_projection_t; typedef struct s2_tessellator_t s2_tessellator_t; From a716f5ed95c73e26564edd8b3d6f6444c65cf104 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 3 Feb 2022 21:31:04 -0400 Subject: [PATCH 04/88] check with C API def --- .github/workflows/check-s2-c-api.yaml | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/check-s2-c-api.yaml diff --git a/.github/workflows/check-s2-c-api.yaml b/.github/workflows/check-s2-c-api.yaml new file mode 100644 index 00000000..83b40520 --- /dev/null +++ b/.github/workflows/check-s2-c-api.yaml @@ -0,0 +1,35 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/master/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +name: R-CMD-check + +jobs: + R-CMD-check: + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + steps: + - uses: actions/checkout@v2 + + - uses: r-lib/actions/setup-r@v1 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v1 + with: + extra-packages: rcmdcheck + + - uses: r-lib/actions/check-r-package@v1 + env: + S2_R_USE_C_API: "true" + + - name: Show install output + if: always() + run: find check -name '00install.out*' -exec cat '{}' \; || true + shell: bash From 323a516779ab3e054539822e1e41b446b5633baf Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 3 Feb 2022 21:44:31 -0400 Subject: [PATCH 05/88] in theory, construct objects --- src/geography-collection.h | 9 ++++++++- src/point-geography.h | 2 +- src/polygon-geography.h | 2 +- src/polyline-geography.h | 8 +++++++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/geography-collection.h b/src/geography-collection.h index d215050a..e1efce08 100644 --- a/src/geography-collection.h +++ b/src/geography-collection.h @@ -15,7 +15,14 @@ class GeographyCollection: public Geography { #ifdef S2_R_USE_C_API std::unique_ptr NewGeography() { - return nullptr; + std::vector> features_cpy; + features_cpy.reserve(features.size()); + + for (const auto& feature : features) { + features_cpy.push_back(feature->NewGeography()); + } + + return absl::make_unique(std::move(features_cpy)); } #endif diff --git a/src/point-geography.h b/src/point-geography.h index da89d266..b68867f7 100644 --- a/src/point-geography.h +++ b/src/point-geography.h @@ -21,7 +21,7 @@ class PointGeography: public Geography { #ifdef S2_R_USE_C_API std::unique_ptr NewGeography() { - return nullptr; + return absl::make_unique(points); } #endif diff --git a/src/polygon-geography.h b/src/polygon-geography.h index adfe5a82..ea409e3d 100644 --- a/src/polygon-geography.h +++ b/src/polygon-geography.h @@ -18,7 +18,7 @@ class PolygonGeography: public Geography { #ifdef S2_R_USE_C_API std::unique_ptr NewGeography() { - return nullptr; + return absl::make_unique(std::unique_ptr(polygon->Clone())); } #endif diff --git a/src/polyline-geography.h b/src/polyline-geography.h index 450b85d3..31aaa865 100644 --- a/src/polyline-geography.h +++ b/src/polyline-geography.h @@ -16,7 +16,13 @@ class PolylineGeography: public Geography { #ifdef S2_R_USE_C_API std::unique_ptr NewGeography() { - return nullptr; + std::vector> polylines_cpy; + + for (const auto& polyline : polylines) { + polylines_cpy.push_back(std::unique_ptr(polyline->Clone())); + } + + return absl::make_unique(std::move(polylines_cpy)); } #endif From 439bfa966ddeb2706f8723cb0d122cb4aef21312 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 5 Feb 2022 13:16:43 -0400 Subject: [PATCH 06/88] add the "status" object --- src/s2-capi/internal/s2_c_common.hpp | 40 ++++++++++++++++++++++++++++ src/s2-capi/s2_c_common.cpp | 18 +++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/s2-capi/internal/s2_c_common.hpp b/src/s2-capi/internal/s2_c_common.hpp index 3c7fb16e..6ef1a268 100644 --- a/src/s2-capi/internal/s2_c_common.hpp +++ b/src/s2-capi/internal/s2_c_common.hpp @@ -11,6 +11,46 @@ class S2CAPIError: public std::runtime_error { S2CAPIError(std::string what): std::runtime_error(what.c_str()) {} }; +class S2Status { +public: + S2Status(): code_(0) { + memset(message_, 0, sizeof(message_)); + } + + bool ok() { + return code_ != 0; + } + + void set_error(const std::string& message) { + code_ = -1; + int copy_size = std::min(message.size(), sizeof(message_) - 1); + memcpy(message_, message.data(), copy_size); + } + + void set_error(const char* fmt, ...) { + memset(message_, 0, sizeof(message_)); + va_list args; + va_start(args, fmt); + vsnprintf(message_, sizeof(message_) - 1, fmt, args); + va_end(args); + } + + const char* message() { + if (ok()) { + return ""; + } else { + return message_; + } + } + +private: + int code_; + char message_[8096]; +}; + +// capi typedef start +typedef struct s2_status_t s2_status_t; +// capi typedef end // An S2Geography is an abstraction of S2 types that is designed to closely match // the scope of a GEOS Geometry. Its methods are limited to those needed to diff --git a/src/s2-capi/s2_c_common.cpp b/src/s2-capi/s2_c_common.cpp index f0dcf7ba..3e09b15b 100644 --- a/src/s2-capi/s2_c_common.cpp +++ b/src/s2-capi/s2_c_common.cpp @@ -13,6 +13,24 @@ #include "internal/s2_c_common.hpp" +extern "C" { +// capi func start +s2_status_t* s2_status_create(); +void s2_status_destroy(s2_status_t* status); +// capi func end +} + +s2_status_t* s2_status_create() { + return reinterpret_cast(new S2Status()); +} + +void s2_status_destroy(s2_status_t* status) { + if (status != nullptr) { + S2Status* status_cpp = reinterpret_cast(status); + delete status_cpp; + } +} + void S2Geography::GetCellUnionBound(std::vector* cell_ids) { MutableS2ShapeIndex index; From ced79eba3eae012a672044fa7cdfd2767e767dfc Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 5 Feb 2022 14:31:14 -0400 Subject: [PATCH 07/88] rename 'capi' to 'geography' --- src/Makevars.in | 6 +- src/Makevars.win | 6 +- src/geography.h | 3 +- src/s2-geography/accessors.cpp | 40 ++++++++ src/s2-geography/accessors.hpp | 18 ++++ src/s2-geography/capi/status.cpp | 30 ++++++ .../capi/tessellator.cpp} | 6 -- .../geography.cpp} | 51 ++++------ .../geography.hpp} | 97 ++++++------------- src/s2-geography/s2-geography.hpp | 5 + .../s2_c.h => s2-geography/s2-geography_c.h} | 1 - src/wk-c-utils.c | 2 +- 12 files changed, 152 insertions(+), 113 deletions(-) create mode 100644 src/s2-geography/accessors.cpp create mode 100644 src/s2-geography/accessors.hpp create mode 100644 src/s2-geography/capi/status.cpp rename src/{s2-capi/capi-tessellator.cpp => s2-geography/capi/tessellator.cpp} (99%) rename src/{s2-capi/s2_c_common.cpp => s2-geography/geography.cpp} (67%) rename src/{s2-capi/internal/s2_c_common.hpp => s2-geography/geography.hpp} (66%) create mode 100644 src/s2-geography/s2-geography.hpp rename src/{s2-capi/s2_c.h => s2-geography/s2-geography_c.h} (96%) diff --git a/src/Makevars.in b/src/Makevars.in index 9b848af6..ed64e545 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -103,8 +103,10 @@ OBJECTS = $(ABSL_LIBS) \ s2-xptr.o \ wk-impl.o \ wk-c-utils.o \ - s2-capi/capi-tessellator.o \ - s2-capi/s2_c_common.o \ + s2-geography/capi/tessellator.o \ + s2-geography/capi/status.o \ + s2-geography/accessors.o \ + s2-geography/geography.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ s2/encoded_s2cell_id_vector.o \ diff --git a/src/Makevars.win b/src/Makevars.win index b2c95696..d4615c1a 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -88,8 +88,10 @@ ABSL_LIBS = absl/base/internal/cycleclock.o \ absl/types/bad_variant_access.o S2LIBS = $(ABSL_LIBS) \ - s2-capi/capi-tessellator.o \ - s2-capi/s2_c_common.o \ + s2-geography/capi/tessellator.o \ + s2-geography/capi/status.o \ + s2-geography/accessors.o \ + s2-geography/geography.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ s2/encoded_s2cell_id_vector.o \ diff --git a/src/geography.h b/src/geography.h index f7d93c2f..99f8af9e 100644 --- a/src/geography.h +++ b/src/geography.h @@ -15,7 +15,8 @@ #include #ifdef S2_R_USE_C_API -#include "s2-capi/internal/s2_c_common.hpp" +#include "s2-geography/s2-geography.hpp" +using namespace s2geography; #endif class Geography { diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp new file mode 100644 index 00000000..3b486e7d --- /dev/null +++ b/src/s2-geography/accessors.cpp @@ -0,0 +1,40 @@ + +#include "geography.hpp" +#include "accessors.hpp" +using namespace s2geography; + +bool s2_is_collection(const S2Geography& geog) { + return false; +} + +int s2_dimension(const S2Geography& geog) { + return 0; +} + +int s2_num_points(const S2Geography& geog) { + return 0; +} + +bool s2_is_empty(const S2Geography& geog) { + return 0; +} + +double s2_area(const S2Geography& geog) { + return 0; +} + +double s2_length(const S2Geography& geog) { + return 0; +} + +double s2_perimeter(const S2Geography& geog) { + return 0; +} + +double s2_x(const S2Geography& geog) { + return 0; +} + +double s2_y(const S2Geography& geog) { + return 0; +} diff --git a/src/s2-geography/accessors.hpp b/src/s2-geography/accessors.hpp new file mode 100644 index 00000000..929c017f --- /dev/null +++ b/src/s2-geography/accessors.hpp @@ -0,0 +1,18 @@ + +#pragma once + +#include "geography.hpp" + +namespace s2geography { + +bool s2_is_collection(const S2Geography& geog); +int s2_dimension(const S2Geography& geog); +int s2_num_points(const S2Geography& geog); +bool s2_is_empty(const S2Geography& geog); +double s2_area(const S2Geography& geog); +double s2_length(const S2Geography& geog); +double s2_perimeter(const S2Geography& geog); +double s2_x(const S2Geography& geog); +double s2_y(const S2Geography& geog); + +} diff --git a/src/s2-geography/capi/status.cpp b/src/s2-geography/capi/status.cpp new file mode 100644 index 00000000..1d47a9bc --- /dev/null +++ b/src/s2-geography/capi/status.cpp @@ -0,0 +1,30 @@ + +#include +#include +#include + + +// capi typedef start +typedef struct { + int code; + char message[8096]; + void* private_data; +} s2_status_t; +// capi typedef end + + +void s2_status_reset(s2_status_t* status) { + memset(status->message, 0, sizeof(status->message)); + status->code = 0; +} + + +void s2_status_set_error(s2_status_t* status, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int chars_written = vsnprintf(status->message, sizeof(status->message) - 1, fmt, args); + va_end(args); + status->message[chars_written] = '\0'; +} + + diff --git a/src/s2-capi/capi-tessellator.cpp b/src/s2-geography/capi/tessellator.cpp similarity index 99% rename from src/s2-capi/capi-tessellator.cpp rename to src/s2-geography/capi/tessellator.cpp index 42db4c75..3feb4445 100644 --- a/src/s2-capi/capi-tessellator.cpp +++ b/src/s2-geography/capi/tessellator.cpp @@ -9,10 +9,7 @@ typedef struct s2_projection_t s2_projection_t; typedef struct s2_tessellator_t s2_tessellator_t; // capi typedef end -#ifdef __cplusplus extern "C" { -#endif - // capi func start s2_projection_t* s2_projection_create_plate_carree(double scale); s2_projection_t* s2_projection_create_mercator(double max_x); @@ -30,10 +27,7 @@ int s2_tessellator_s2_points_size(s2_tessellator_t* tessellator); int s2_tessellator_r2_point(s2_tessellator_t* tessellator, int i, double* coord); int s2_tessellator_s2_point(s2_tessellator_t* tessellator, int i, double* coord); // capi func end - -#ifdef __cplusplus } -#endif s2_projection_t* s2_projection_create_plate_carree(double scale) { return (s2_projection_t*) new S2::PlateCarreeProjection(scale); diff --git a/src/s2-capi/s2_c_common.cpp b/src/s2-geography/geography.cpp similarity index 67% rename from src/s2-capi/s2_c_common.cpp rename to src/s2-geography/geography.cpp index 3e09b15b..b69ae32e 100644 --- a/src/s2-capi/s2_c_common.cpp +++ b/src/s2-geography/geography.cpp @@ -11,28 +11,11 @@ #include "s2/mutable_s2shape_index.h" #include "s2/s2shape_index_region.h" -#include "internal/s2_c_common.hpp" - -extern "C" { -// capi func start -s2_status_t* s2_status_create(); -void s2_status_destroy(s2_status_t* status); -// capi func end -} - -s2_status_t* s2_status_create() { - return reinterpret_cast(new S2Status()); -} - -void s2_status_destroy(s2_status_t* status) { - if (status != nullptr) { - S2Status* status_cpp = reinterpret_cast(status); - delete status_cpp; - } -} +#include "geography.hpp" +using namespace s2geography; -void S2Geography::GetCellUnionBound(std::vector* cell_ids) { +void S2Geography::GetCellUnionBound(std::vector* cell_ids) const { MutableS2ShapeIndex index; for (int i = 0; i < num_shapes(); i++) { index.Add(Shape(i)); @@ -41,11 +24,11 @@ void S2Geography::GetCellUnionBound(std::vector* cell_ids) { MakeS2ShapeIndexRegion(&index).GetCellUnionBound(cell_ids); } -std::unique_ptr S2GeographyOwningPoint::Shape(int id) { +std::unique_ptr S2GeographyOwningPoint::Shape(int id) const { return absl::make_unique(points_); } -std::unique_ptr S2GeographyOwningPoint::Region() { +std::unique_ptr S2GeographyOwningPoint::Region() const { auto region = absl::make_unique(); for (const S2Point& point: points_) { region->Add(absl::make_unique(point)); @@ -54,7 +37,7 @@ std::unique_ptr S2GeographyOwningPoint::Region() { return region; } -void S2GeographyOwningPoint::GetCellUnionBound(std::vector* cell_ids) { +void S2GeographyOwningPoint::GetCellUnionBound(std::vector* cell_ids) const { if (points_.size() < 10) { for (const S2Point& point: points_) { cell_ids->push_back(S2CellId(point)); @@ -65,13 +48,13 @@ void S2GeographyOwningPoint::GetCellUnionBound(std::vector* cell_ids) } -int S2GeographyOwningPolyline::num_shapes() { return polylines_.size(); } +int S2GeographyOwningPolyline::num_shapes() const { return polylines_.size(); } -std::unique_ptr S2GeographyOwningPolyline::Shape(int id) { +std::unique_ptr S2GeographyOwningPolyline::Shape(int id) const { return absl::make_unique(polylines_[id].get()); } -std::unique_ptr S2GeographyOwningPolyline::Region() { +std::unique_ptr S2GeographyOwningPolyline::Region() const { auto region = absl::make_unique(); for (const auto& polyline: polylines_) { region->Add(std::unique_ptr(polyline->Clone())); @@ -79,28 +62,28 @@ std::unique_ptr S2GeographyOwningPolyline::Region() { return region; } -void S2GeographyOwningPolyline::GetCellUnionBound(std::vector* cell_ids) { +void S2GeographyOwningPolyline::GetCellUnionBound(std::vector* cell_ids) const { for (const auto& polyline: polylines_) { polyline->GetCellUnionBound(cell_ids); } } -std::unique_ptr S2GeographyOwningPolygon::Shape(int id) { +std::unique_ptr S2GeographyOwningPolygon::Shape(int id) const { return absl::make_unique(polygon_.get()); } -std::unique_ptr S2GeographyOwningPolygon::Region() { +std::unique_ptr S2GeographyOwningPolygon::Region() const { return std::unique_ptr(polygon_->Clone()); } -void S2GeographyOwningPolygon::GetCellUnionBound(std::vector* cell_ids) { +void S2GeographyOwningPolygon::GetCellUnionBound(std::vector* cell_ids) const { polygon_->GetCellUnionBound(cell_ids); } -int S2GeographyCollection::num_shapes() { return total_shapes_; } +int S2GeographyCollection::num_shapes() const { return total_shapes_; } -std::unique_ptr S2GeographyCollection::Shape(int id) { +std::unique_ptr S2GeographyCollection::Shape(int id) const { int sum_shapes_ = 0; for (int i = 0; i < total_shapes_; i++) { sum_shapes_ += num_shapes_[i]; @@ -109,10 +92,10 @@ std::unique_ptr S2GeographyCollection::Shape(int id) { } } - throw S2CAPIError("shape id out of bounds"); + throw S2GeographyException("shape id out of bounds"); } -std::unique_ptr S2GeographyCollection::Region() { +std::unique_ptr S2GeographyCollection::Region() const { auto region = absl::make_unique(); for (const auto& feature: features_) { region->Add(feature->Region()); diff --git a/src/s2-capi/internal/s2_c_common.hpp b/src/s2-geography/geography.hpp similarity index 66% rename from src/s2-capi/internal/s2_c_common.hpp rename to src/s2-geography/geography.hpp index 6ef1a268..9dd661b9 100644 --- a/src/s2-capi/internal/s2_c_common.hpp +++ b/src/s2-geography/geography.hpp @@ -1,4 +1,6 @@ +#pragma once + #include #include #include @@ -6,52 +8,13 @@ #include "s2/s2polygon.h" #include "s2/s2polyline.h" -class S2CAPIError: public std::runtime_error { -public: - S2CAPIError(std::string what): std::runtime_error(what.c_str()) {} -}; +namespace s2geography { -class S2Status { +class S2GeographyException: public std::runtime_error { public: - S2Status(): code_(0) { - memset(message_, 0, sizeof(message_)); - } - - bool ok() { - return code_ != 0; - } - - void set_error(const std::string& message) { - code_ = -1; - int copy_size = std::min(message.size(), sizeof(message_) - 1); - memcpy(message_, message.data(), copy_size); - } - - void set_error(const char* fmt, ...) { - memset(message_, 0, sizeof(message_)); - va_list args; - va_start(args, fmt); - vsnprintf(message_, sizeof(message_) - 1, fmt, args); - va_end(args); - } - - const char* message() { - if (ok()) { - return ""; - } else { - return message_; - } - } - -private: - int code_; - char message_[8096]; + S2GeographyException(std::string what): std::runtime_error(what.c_str()) {} }; -// capi typedef start -typedef struct s2_status_t s2_status_t; -// capi typedef end - // An S2Geography is an abstraction of S2 types that is designed to closely match // the scope of a GEOS Geometry. Its methods are limited to those needed to // implement C API functions. From an S2 perspective, an S2Geography is @@ -65,31 +28,31 @@ class S2Geography { virtual ~S2Geography() {} // The number of S2Shape objects needed to represent this S2Geography - virtual int num_shapes() = 0; + virtual int num_shapes() const = 0; // Returns the given S2Shape (where 0 <= id < num_shapes()). The // caller retains ownership of the S2Shape but the data pointed to // by the object requires that the underlying S2Geography outlives // the returned object. - virtual std::unique_ptr Shape(int id) = 0; + virtual std::unique_ptr Shape(int id) const = 0; // Returns an S2Region that represents the object. The caller retains // ownership of the S2Region but the data pointed to by the object // requires that the underlying S2Geography outlives the returned // object. - virtual std::unique_ptr Region() = 0; + virtual std::unique_ptr Region() const = 0; // Adds an unnormalized set of S2CellIDs to `cell_ids`. This is intended // to be faster than using Region().GetCovering() directly and to // return a small number of cells that can be used to compute a possible // intersection quickly. - virtual void GetCellUnionBound(std::vector* cell_ids); -}; - + virtual void GetCellUnionBound(std::vector* cell_ids) const; -// capi typedef start -typedef struct s2_geography_t s2_geography_t; -// capi typedef end + // Most operations should use the abstract Shape, Region, or GetCellUnionBound() + // methods to implement operators. Occasionally it is necessary or more efficient + // to compute on the underlying data type. + const void* data() const { return nullptr; } +}; // An S2Geography representing zero or more points using a std::vector @@ -100,10 +63,10 @@ class S2GeographyOwningPoint: public S2Geography { S2GeographyOwningPoint(S2Point point): points_(1) { points_.push_back(point); } S2GeographyOwningPoint(std::vector points): points_(std::move(points)) {} - int num_shapes() { return 1; } - std::unique_ptr Shape(int id); - std::unique_ptr Region(); - void GetCellUnionBound(std::vector* cell_ids); + int num_shapes() const { return 1; } + std::unique_ptr Shape(int id) const; + std::unique_ptr Region() const; + void GetCellUnionBound(std::vector* cell_ids) const; private: std::vector points_; @@ -118,10 +81,10 @@ class S2GeographyOwningPolyline: public S2Geography { S2GeographyOwningPolyline(std::vector> polylines): polylines_(std::move(polylines)) {} - int num_shapes(); - std::unique_ptr Shape(int id); - std::unique_ptr Region(); - void GetCellUnionBound(std::vector* cell_ids); + int num_shapes() const; + std::unique_ptr Shape(int id) const; + std::unique_ptr Region() const; + void GetCellUnionBound(std::vector* cell_ids) const; private: std::vector> polylines_; @@ -138,10 +101,10 @@ class S2GeographyOwningPolygon: public S2Geography { S2GeographyOwningPolygon(std::unique_ptr polygon): polygon_(std::move(polygon)) {} - int num_shapes() { return 1; } - std::unique_ptr Shape(int id); - std::unique_ptr Region(); - void GetCellUnionBound(std::vector* cell_ids); + int num_shapes() const { return 1; } + std::unique_ptr Shape(int id) const; + std::unique_ptr Region() const; + void GetCellUnionBound(std::vector* cell_ids) const; private: std::unique_ptr polygon_; @@ -161,12 +124,14 @@ class S2GeographyCollection: public S2Geography { } } - int num_shapes(); - std::unique_ptr Shape(int id); - std::unique_ptr Region(); + int num_shapes() const; + std::unique_ptr Shape(int id) const; + std::unique_ptr Region() const; private: std::vector> features_; std::vector num_shapes_; int total_shapes_; }; + +} diff --git a/src/s2-geography/s2-geography.hpp b/src/s2-geography/s2-geography.hpp new file mode 100644 index 00000000..f17fe3a1 --- /dev/null +++ b/src/s2-geography/s2-geography.hpp @@ -0,0 +1,5 @@ + +#pragma once + +#include "geography.hpp" +#include "accessors.hpp" diff --git a/src/s2-capi/s2_c.h b/src/s2-geography/s2-geography_c.h similarity index 96% rename from src/s2-capi/s2_c.h rename to src/s2-geography/s2-geography_c.h index 2deb99bc..3e06c161 100644 --- a/src/s2-capi/s2_c.h +++ b/src/s2-geography/s2-geography_c.h @@ -3,7 +3,6 @@ extern "C" { #endif -typedef struct s2_geography_t s2_geography_t; typedef struct s2_projection_t s2_projection_t; typedef struct s2_tessellator_t s2_tessellator_t; diff --git a/src/wk-c-utils.c b/src/wk-c-utils.c index 080376a4..d37b915c 100644 --- a/src/wk-c-utils.c +++ b/src/wk-c-utils.c @@ -3,7 +3,7 @@ #include #include #include "wk-v1.h" -#include "s2-capi/s2_c.h" +#include "s2-geography/s2-geography_c.h" // Expose projections as external pointers so that they can theoretically be generated // by other packages. From d4e3033c66c22c19c431129dd1a67edff5ba13be Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 7 Feb 2022 21:41:57 -0400 Subject: [PATCH 08/88] implement accessors --- src/s2-geography/accessors.cpp | 143 ++++++++++++++++++++++++++++++--- src/s2-geography/geography.hpp | 21 +++-- 2 files changed, 149 insertions(+), 15 deletions(-) diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 3b486e7d..28f11ec7 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -1,40 +1,163 @@ #include "geography.hpp" #include "accessors.hpp" -using namespace s2geography; + +namespace s2geography { bool s2_is_collection(const S2Geography& geog) { - return false; + int dimension = s2_dimension(geog); + if (dimension == 0) { + return s2_num_points(geog) > 1; + } + + if (dimension == 1) { + int num_chains = 0; + for (int i = 0; i < geog.num_shapes(); i++) { + std::unique_ptr shape = geog.Shape(i); + num_chains += shape->num_chains(); + if (num_chains > 1) { + return true; + } + } + + return false; + } + + auto polygon_geog_ptr = dynamic_cast(&geog); + if (polygon_geog_ptr != nullptr) { + int num_outer_loops = 0; + for (int i = 0; i < polygon_geog_ptr->Polygon()->num_loops(); i++) { + S2Loop* loop = polygon_geog_ptr->Polygon()->loop(i); + num_outer_loops += loop->depth() == 0; + if (num_outer_loops > 1) { + return true; + } + } + + return false; + } else { + // if custom subclasses are ever a thing, we can go through the builder + // to build a polygon + throw S2GeographyException("s2_area() not implemented for custom S2Geography()"); + } } int s2_dimension(const S2Geography& geog) { - return 0; + int dimension = -1; + for (int i = 0; i < geog.num_shapes(); i++) { + std::unique_ptr shape = geog.Shape(i); + if (shape->dimension() > dimension) { + dimension = shape->dimension(); + } + } + + return dimension; } int s2_num_points(const S2Geography& geog) { - return 0; + int num_points = 0; + for (int i = 0; i < geog.num_shapes(); i++) { + std::unique_ptr shape = geog.Shape(i); + switch (shape->dimension()) { + case 0: + case 2: + num_points += shape->num_edges(); + break; + case 1: + num_points += shape->num_edges() - shape->num_chains(); + break; + } + } + + return num_points; } bool s2_is_empty(const S2Geography& geog) { - return 0; + for (int i = 0; i < geog.num_shapes(); i++) { + std::unique_ptr shape = geog.Shape(i); + if (!shape->is_empty()) { + return false; + } + } + + return true; } double s2_area(const S2Geography& geog) { - return 0; + if (s2_dimension(geog) != 2) { + return 0; + } + + auto polygon_geog_ptr = dynamic_cast(&geog); + if (polygon_geog_ptr != nullptr) { + return polygon_geog_ptr->Polygon()->GetArea(); + } else { + // if custom subclasses are ever a thing, we can go through the builder + // to build a polygon + throw S2GeographyException("s2_area() not implemented for custom S2Geography()"); + } } double s2_length(const S2Geography& geog) { - return 0; + double length = 0; + + if (s2_dimension(geog) == 1) { + for (int i = 0; i < geog.num_shapes(); i++) { + std::unique_ptr shape = geog.Shape(i); + for (int j = 0; j < shape->num_edges(); j++) { + S2Shape::Edge e = shape->edge(j); + S1ChordAngle angle(e.v0, e.v1); + length += angle.radians(); + } + } + } + + return length; } double s2_perimeter(const S2Geography& geog) { - return 0; + double length = 0; + + if (s2_dimension(geog) == 2) { + for (int i = 0; i < geog.num_shapes(); i++) { + std::unique_ptr shape = geog.Shape(i); + for (int j = 0; j < shape->num_edges(); j++) { + S2Shape::Edge e = shape->edge(j); + S1ChordAngle angle(e.v0, e.v1); + length += angle.radians(); + } + } + } + + return length; } double s2_x(const S2Geography& geog) { - return 0; + double out = NAN; + for (int i = 0; i < geog.num_shapes(); i++) { + std::unique_ptr shape = geog.Shape(i); + if (shape->dimension() == 0 && shape->num_edges() == 1 && std::isnan(out)) { + out = shape->edge(0).v0.x(); + } else if (shape->dimension() == 0 && shape->num_edges() == 1) { + return NAN; + } + } + + return out; } double s2_y(const S2Geography& geog) { - return 0; + double out = NAN; + for (int i = 0; i < geog.num_shapes(); i++) { + std::unique_ptr shape = geog.Shape(i); + if (shape->dimension() == 0 && shape->num_edges() == 1 && std::isnan(out)) { + out = shape->edge(0).v0.y(); + } else if (shape->dimension() == 0 && shape->num_edges() == 1) { + return NAN; + } + } + + return out; +} + } diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index 9dd661b9..dcf679e8 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -47,11 +47,6 @@ class S2Geography { // return a small number of cells that can be used to compute a possible // intersection quickly. virtual void GetCellUnionBound(std::vector* cell_ids) const; - - // Most operations should use the abstract Shape, Region, or GetCellUnionBound() - // methods to implement operators. Occasionally it is necessary or more efficient - // to compute on the underlying data type. - const void* data() const { return nullptr; } }; @@ -68,6 +63,10 @@ class S2GeographyOwningPoint: public S2Geography { std::unique_ptr Region() const; void GetCellUnionBound(std::vector* cell_ids) const; + const std::vector& Points() const { + return points_; + } + private: std::vector points_; }; @@ -86,6 +85,10 @@ class S2GeographyOwningPolyline: public S2Geography { std::unique_ptr Region() const; void GetCellUnionBound(std::vector* cell_ids) const; + const std::vector>& Polylines() const { + return polylines_; + } + private: std::vector> polylines_; }; @@ -106,6 +109,10 @@ class S2GeographyOwningPolygon: public S2Geography { std::unique_ptr Region() const; void GetCellUnionBound(std::vector* cell_ids) const; + const std::unique_ptr& Polygon() const { + return polygon_; + } + private: std::unique_ptr polygon_; }; @@ -128,6 +135,10 @@ class S2GeographyCollection: public S2Geography { std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; + const std::vector>& Features() const { + return features_; + } + private: std::vector> features_; std::vector num_shapes_; From 631bd1e4b16dc3ec24173ea3ac9222dd8e6cb287 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 10 Feb 2022 20:45:15 -0400 Subject: [PATCH 09/88] don't make the new geography conditional on comnpile --- .github/workflows/check-s2-c-api.yaml | 35 --------------------------- configure | 5 ---- src/geography-collection.h | 2 -- src/geography.h | 4 --- src/point-geography.h | 2 -- src/polygon-geography.h | 2 -- src/polyline-geography.h | 2 -- 7 files changed, 52 deletions(-) delete mode 100644 .github/workflows/check-s2-c-api.yaml diff --git a/.github/workflows/check-s2-c-api.yaml b/.github/workflows/check-s2-c-api.yaml deleted file mode 100644 index 83b40520..00000000 --- a/.github/workflows/check-s2-c-api.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# Workflow derived from https://github.com/r-lib/actions/tree/master/examples -# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help -on: - push: - branches: [main, master] - pull_request: - branches: [main, master] - -name: R-CMD-check - -jobs: - R-CMD-check: - runs-on: ubuntu-latest - env: - GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} - R_KEEP_PKG_SOURCE: yes - steps: - - uses: actions/checkout@v2 - - - uses: r-lib/actions/setup-r@v1 - with: - use-public-rspm: true - - - uses: r-lib/actions/setup-r-dependencies@v1 - with: - extra-packages: rcmdcheck - - - uses: r-lib/actions/check-r-package@v1 - env: - S2_R_USE_C_API: "true" - - - name: Show install output - if: always() - run: find check -name '00install.out*' -exec cat '{}' \; || true - shell: bash diff --git a/configure b/configure index cf50b7d9..57f73a01 100755 --- a/configure +++ b/configure @@ -106,11 +106,6 @@ else PKG_CFLAGS="$PKG_CFLAGS -DIS_BIG_ENDIAN" fi -# Flag for developers to turn on the new C API -if [ "$S2_R_USE_C_API" ]; then - PKG_CFLAGS="$PKG_CFLAGS -DS2_R_USE_C_API" -fi - echo "Using PKG_LIBS=$PKG_LIBS" echo "Using PKG_CFLAGS=$PKG_CFLAGS" diff --git a/src/geography-collection.h b/src/geography-collection.h index e1efce08..28f7430a 100644 --- a/src/geography-collection.h +++ b/src/geography-collection.h @@ -13,7 +13,6 @@ class GeographyCollection: public Geography { GeographyCollection(std::vector> features): features(std::move(features)) {} -#ifdef S2_R_USE_C_API std::unique_ptr NewGeography() { std::vector> features_cpy; features_cpy.reserve(features.size()); @@ -24,7 +23,6 @@ class GeographyCollection: public Geography { return absl::make_unique(std::move(features_cpy)); } -#endif Geography::Type GeographyType() { return Geography::Type::GEOGRAPHY_COLLECTION; diff --git a/src/geography.h b/src/geography.h index 99f8af9e..8976ade3 100644 --- a/src/geography.h +++ b/src/geography.h @@ -14,10 +14,8 @@ #include "wk/geometry-handler.hpp" #include -#ifdef S2_R_USE_C_API #include "s2-geography/s2-geography.hpp" using namespace s2geography; -#endif class Geography { public: @@ -56,9 +54,7 @@ class Geography { virtual S2Point Centroid() = 0; virtual std::unique_ptr Boundary() = 0; -#ifdef S2_R_USE_C_API virtual std::unique_ptr NewGeography() = 0; -#endif // every type will build the index differently based on // the underlying data, and this can (should?) be done diff --git a/src/point-geography.h b/src/point-geography.h index b68867f7..0e660517 100644 --- a/src/point-geography.h +++ b/src/point-geography.h @@ -19,11 +19,9 @@ class PointGeography: public Geography { } PointGeography(std::vector points): points(points) {} -#ifdef S2_R_USE_C_API std::unique_ptr NewGeography() { return absl::make_unique(points); } -#endif Geography::Type GeographyType() { return Geography::Type::GEOGRAPHY_POINT; diff --git a/src/polygon-geography.h b/src/polygon-geography.h index ea409e3d..817fa08e 100644 --- a/src/polygon-geography.h +++ b/src/polygon-geography.h @@ -16,11 +16,9 @@ class PolygonGeography: public Geography { PolygonGeography(std::unique_ptr polygon): polygon(std::move(polygon)) {} -#ifdef S2_R_USE_C_API std::unique_ptr NewGeography() { return absl::make_unique(std::unique_ptr(polygon->Clone())); } -#endif Geography::Type GeographyType() { return Geography::Type::GEOGRAPHY_POLYGON; diff --git a/src/polyline-geography.h b/src/polyline-geography.h index 31aaa865..66f36dad 100644 --- a/src/polyline-geography.h +++ b/src/polyline-geography.h @@ -14,7 +14,6 @@ class PolylineGeography: public Geography { PolylineGeography(std::vector> polylines): polylines(std::move(polylines)) {} -#ifdef S2_R_USE_C_API std::unique_ptr NewGeography() { std::vector> polylines_cpy; @@ -24,7 +23,6 @@ class PolylineGeography: public Geography { return absl::make_unique(std::move(polylines_cpy)); } -#endif Geography::Type GeographyType() { return Geography::Type::GEOGRAPHY_POLYLINE; From 1f3e4938f0a97ddc2a88395e3b197979fa15b223 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 10 Feb 2022 21:28:49 -0400 Subject: [PATCH 10/88] actually use new geography accessor API --- src/geography-collection.h | 6 ++--- src/geography.h | 4 ++-- src/point-geography.h | 4 ++-- src/polygon-geography.h | 4 ++-- src/polyline-geography.h | 4 ++-- src/s2-accessors.cpp | 37 ++++++++++++++++++++++-------- src/s2-geography/accessors.cpp | 25 ++++++++++++++------ src/s2-geography/geography.cpp | 2 +- src/s2-geography/geography.hpp | 2 +- tests/testthat/test-s2-accessors.R | 13 +++++++++-- 10 files changed, 70 insertions(+), 31 deletions(-) diff --git a/src/geography-collection.h b/src/geography-collection.h index 28f7430a..9c8c2f01 100644 --- a/src/geography-collection.h +++ b/src/geography-collection.h @@ -13,15 +13,15 @@ class GeographyCollection: public Geography { GeographyCollection(std::vector> features): features(std::move(features)) {} - std::unique_ptr NewGeography() { - std::vector> features_cpy; + std::unique_ptr NewGeography() { + std::vector> features_cpy; features_cpy.reserve(features.size()); for (const auto& feature : features) { features_cpy.push_back(feature->NewGeography()); } - return absl::make_unique(std::move(features_cpy)); + return absl::make_unique(std::move(features_cpy)); } Geography::Type GeographyType() { diff --git a/src/geography.h b/src/geography.h index 8976ade3..f1098692 100644 --- a/src/geography.h +++ b/src/geography.h @@ -15,7 +15,7 @@ #include #include "s2-geography/s2-geography.hpp" -using namespace s2geography; + class Geography { public: @@ -54,7 +54,7 @@ class Geography { virtual S2Point Centroid() = 0; virtual std::unique_ptr Boundary() = 0; - virtual std::unique_ptr NewGeography() = 0; + virtual std::unique_ptr NewGeography() = 0; // every type will build the index differently based on // the underlying data, and this can (should?) be done diff --git a/src/point-geography.h b/src/point-geography.h index 0e660517..b8245442 100644 --- a/src/point-geography.h +++ b/src/point-geography.h @@ -19,8 +19,8 @@ class PointGeography: public Geography { } PointGeography(std::vector points): points(points) {} - std::unique_ptr NewGeography() { - return absl::make_unique(points); + std::unique_ptr NewGeography() { + return absl::make_unique(points); } Geography::Type GeographyType() { diff --git a/src/polygon-geography.h b/src/polygon-geography.h index 817fa08e..56b9e042 100644 --- a/src/polygon-geography.h +++ b/src/polygon-geography.h @@ -16,8 +16,8 @@ class PolygonGeography: public Geography { PolygonGeography(std::unique_ptr polygon): polygon(std::move(polygon)) {} - std::unique_ptr NewGeography() { - return absl::make_unique(std::unique_ptr(polygon->Clone())); + std::unique_ptr NewGeography() { + return absl::make_unique(std::unique_ptr(polygon->Clone())); } Geography::Type GeographyType() { diff --git a/src/polyline-geography.h b/src/polyline-geography.h index 66f36dad..5d3c18f8 100644 --- a/src/polyline-geography.h +++ b/src/polyline-geography.h @@ -14,14 +14,14 @@ class PolylineGeography: public Geography { PolylineGeography(std::vector> polylines): polylines(std::move(polylines)) {} - std::unique_ptr NewGeography() { + std::unique_ptr NewGeography() { std::vector> polylines_cpy; for (const auto& polyline : polylines) { polylines_cpy.push_back(std::unique_ptr(polyline->Clone())); } - return absl::make_unique(std::move(polylines_cpy)); + return absl::make_unique(std::move(polylines_cpy)); } Geography::Type GeographyType() { diff --git a/src/s2-accessors.cpp b/src/s2-accessors.cpp index 0cb9d452..d2328fb7 100644 --- a/src/s2-accessors.cpp +++ b/src/s2-accessors.cpp @@ -5,11 +5,14 @@ #include using namespace Rcpp; +#include "s2-geography/accessors.hpp" + // [[Rcpp::export]] LogicalVector cpp_s2_is_collection(List geog) { class Op: public UnaryGeographyOperator { int processFeature(XPtr feature, R_xlen_t i) { - return feature->IsCollection(); + auto geog = feature->NewGeography(); + return s2geography::s2_is_collection(*geog); } }; @@ -53,7 +56,8 @@ CharacterVector cpp_s2_is_valid_reason(List geog) { IntegerVector cpp_s2_dimension(List geog) { class Op: public UnaryGeographyOperator { int processFeature(XPtr feature, R_xlen_t i) { - return feature->Dimension(); + auto geog = feature->NewGeography(); + return s2geography::s2_dimension(*geog); } }; @@ -65,7 +69,8 @@ IntegerVector cpp_s2_dimension(List geog) { IntegerVector cpp_s2_num_points(List geog) { class Op: public UnaryGeographyOperator { int processFeature(XPtr feature, R_xlen_t i) { - return feature->NumPoints(); + auto geog = feature->NewGeography(); + return s2geography::s2_num_points(*geog); } }; @@ -77,7 +82,8 @@ IntegerVector cpp_s2_num_points(List geog) { LogicalVector cpp_s2_is_empty(List geog) { class Op: public UnaryGeographyOperator { int processFeature(XPtr feature, R_xlen_t i) { - return feature->IsEmpty(); + auto geog = feature->NewGeography(); + return s2geography::s2_is_empty(*geog); } }; @@ -89,7 +95,8 @@ LogicalVector cpp_s2_is_empty(List geog) { NumericVector cpp_s2_area(List geog) { class Op: public UnaryGeographyOperator { double processFeature(XPtr feature, R_xlen_t i) { - return feature->Area(); + auto geog = feature->NewGeography(); + return s2geography::s2_area(*geog); } }; @@ -101,7 +108,8 @@ NumericVector cpp_s2_area(List geog) { NumericVector cpp_s2_length(List geog) { class Op: public UnaryGeographyOperator { double processFeature(XPtr feature, R_xlen_t i) { - return feature->Length(); + auto geog = feature->NewGeography(); + return s2geography::s2_length(*geog); } }; @@ -113,7 +121,8 @@ NumericVector cpp_s2_length(List geog) { NumericVector cpp_s2_perimeter(List geog) { class Op: public UnaryGeographyOperator { double processFeature(XPtr feature, R_xlen_t i) { - return feature->Perimeter(); + auto geog = feature->NewGeography(); + return s2geography::s2_perimeter(*geog); } }; @@ -125,7 +134,12 @@ NumericVector cpp_s2_perimeter(List geog) { NumericVector cpp_s2_x(List geog) { class Op: public UnaryGeographyOperator { double processFeature(XPtr feature, R_xlen_t i) { - return feature->X(); + auto geog = feature->NewGeography(); + if (s2geography::s2_dimension(*geog) != 0) { + Rcpp::stop("Can't compute X value of a non-point geography"); + } + + return s2geography::s2_x(*geog); } }; @@ -137,7 +151,12 @@ NumericVector cpp_s2_x(List geog) { NumericVector cpp_s2_y(List geog) { class Op: public UnaryGeographyOperator { double processFeature(XPtr feature, R_xlen_t i) { - return feature->Y(); + auto geog = feature->NewGeography(); + if (s2geography::s2_dimension(*geog) != 0) { + Rcpp::stop("Can't compute Y value of a non-point geography"); + } + + return s2geography::s2_y(*geog); } }; diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 28f11ec7..5e59109f 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -64,7 +64,7 @@ int s2_num_points(const S2Geography& geog) { num_points += shape->num_edges(); break; case 1: - num_points += shape->num_edges() - shape->num_chains(); + num_points += shape->num_edges() + shape->num_chains(); break; } } @@ -91,11 +91,20 @@ double s2_area(const S2Geography& geog) { auto polygon_geog_ptr = dynamic_cast(&geog); if (polygon_geog_ptr != nullptr) { return polygon_geog_ptr->Polygon()->GetArea(); - } else { - // if custom subclasses are ever a thing, we can go through the builder - // to build a polygon - throw S2GeographyException("s2_area() not implemented for custom S2Geography()"); } + + auto collection_geog_ptr = dynamic_cast(&geog); + if (collection_geog_ptr != nullptr) { + double area = 0; + for (auto& feature: collection_geog_ptr->Features()) { + area += s2_area(*feature); + } + return area; + } + + // if custom subclasses are ever a thing, we can go through the builder + // to build a polygon + throw S2GeographyException("s2_area() not implemented for custom S2Geography()"); } double s2_length(const S2Geography& geog) { @@ -137,7 +146,8 @@ double s2_x(const S2Geography& geog) { for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); if (shape->dimension() == 0 && shape->num_edges() == 1 && std::isnan(out)) { - out = shape->edge(0).v0.x(); + S2LatLng pt(shape->edge(0).v0); + out = pt.lng().degrees(); } else if (shape->dimension() == 0 && shape->num_edges() == 1) { return NAN; } @@ -151,7 +161,8 @@ double s2_y(const S2Geography& geog) { for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); if (shape->dimension() == 0 && shape->num_edges() == 1 && std::isnan(out)) { - out = shape->edge(0).v0.y(); + S2LatLng pt(shape->edge(0).v0); + out = pt.lat().degrees(); } else if (shape->dimension() == 0 && shape->num_edges() == 1) { return NAN; } diff --git a/src/s2-geography/geography.cpp b/src/s2-geography/geography.cpp index b69ae32e..42af5765 100644 --- a/src/s2-geography/geography.cpp +++ b/src/s2-geography/geography.cpp @@ -85,7 +85,7 @@ int S2GeographyCollection::num_shapes() const { return total_shapes_; } std::unique_ptr S2GeographyCollection::Shape(int id) const { int sum_shapes_ = 0; - for (int i = 0; i < total_shapes_; i++) { + for (int i = 0; i < features_.size(); i++) { sum_shapes_ += num_shapes_[i]; if (id < sum_shapes_) { return features_[i]->Shape(id - sum_shapes_ + num_shapes_[i]); diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index dcf679e8..34e5c832 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -123,7 +123,7 @@ class S2GeographyOwningPolygon: public S2Geography { class S2GeographyCollection: public S2Geography { public: S2GeographyCollection(std::vector> features): - features_(std::move(features)), num_shapes_(features_.size()), + features_(std::move(features)), total_shapes_(0) { for (const auto& feature: features_) { num_shapes_.push_back(feature->num_shapes()); diff --git a/tests/testthat/test-s2-accessors.R b/tests/testthat/test-s2-accessors.R index e929f1d2..f0fc7306 100644 --- a/tests/testthat/test-s2-accessors.R +++ b/tests/testthat/test-s2-accessors.R @@ -81,6 +81,10 @@ test_that("s2_num_points works", { expect_identical(s2_num_points("POINT EMPTY"), 0L) expect_identical(s2_num_points("LINESTRING (0 0, 1 1)"), 2L) expect_identical(s2_num_points("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"), 4L) + expect_identical( + s2_num_points("GEOMETRYCOLLECTION (POINT (0 1), LINESTRING (0 0, 1 1))"), + 3L + ) }) test_that("s2_is_empty works", { @@ -104,6 +108,11 @@ test_that("s2_area works", { expect_true( abs(s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 180 / pi) - 100) < 0.27 ) + + expect_identical( + s2_area("POLYGON ((0 0, 90 0, 0 90, 0 0))"), + s2_area("GEOMETRYCOLLECTION(POLYGON ((0 0, 90 0, 0 90, 0 0)))") + ) }) test_that("s2_length works", { @@ -134,8 +143,8 @@ test_that("s2_x and s2_y works", { expect_identical(s2_y(NA_character_), NA_real_) expect_equal(s2_x("POINT (-64 45)"), -64) expect_equal(s2_y("POINT (-64 45)"), 45) - expect_identical(s2_x("POINT EMPTY"), NA_real_) - expect_identical(s2_y("POINT EMPTY"), NA_real_) + expect_identical(s2_x("POINT EMPTY"), NaN) + expect_identical(s2_y("POINT EMPTY"), NaN) expect_error(s2_x("LINESTRING EMPTY"), "Can't compute") expect_error(s2_y("LINESTRING EMPTY"), "Can't compute") expect_error(s2_x("POLYGON EMPTY"), "Can't compute") From 0562ca753b608587d2dac7457f436b3751b60abe Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 12 Feb 2022 13:09:09 -0400 Subject: [PATCH 11/88] use overloads for geog-specific methods --- src/s2-geography/accessors.cpp | 44 +++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 5e59109f..1b82cc57 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -4,6 +4,19 @@ namespace s2geography { +bool s2_is_collection(const S2GeographyOwningPolygon& geog) { + int num_outer_loops = 0; + for (int i = 0; i < geog.Polygon()->num_loops(); i++) { + S2Loop* loop = geog.Polygon()->loop(i); + num_outer_loops += loop->depth() == 0; + if (num_outer_loops > 1) { + return true; + } + } + + return false; +} + bool s2_is_collection(const S2Geography& geog) { int dimension = s2_dimension(geog); if (dimension == 0) { @@ -25,16 +38,7 @@ bool s2_is_collection(const S2Geography& geog) { auto polygon_geog_ptr = dynamic_cast(&geog); if (polygon_geog_ptr != nullptr) { - int num_outer_loops = 0; - for (int i = 0; i < polygon_geog_ptr->Polygon()->num_loops(); i++) { - S2Loop* loop = polygon_geog_ptr->Polygon()->loop(i); - num_outer_loops += loop->depth() == 0; - if (num_outer_loops > 1) { - return true; - } - } - - return false; + return s2_is_collection(*polygon_geog_ptr); } else { // if custom subclasses are ever a thing, we can go through the builder // to build a polygon @@ -83,6 +87,18 @@ bool s2_is_empty(const S2Geography& geog) { return true; } +double s2_area(const S2GeographyOwningPolygon& geog) { + return geog.Polygon()->GetArea(); +} + +double s2_area(const S2GeographyCollection& geog) { + double area = 0; + for (auto& feature: geog.Features()) { + area += s2_area(*feature); + } + return area; +} + double s2_area(const S2Geography& geog) { if (s2_dimension(geog) != 2) { return 0; @@ -90,16 +106,12 @@ double s2_area(const S2Geography& geog) { auto polygon_geog_ptr = dynamic_cast(&geog); if (polygon_geog_ptr != nullptr) { - return polygon_geog_ptr->Polygon()->GetArea(); + return s2_area(*polygon_geog_ptr); } auto collection_geog_ptr = dynamic_cast(&geog); if (collection_geog_ptr != nullptr) { - double area = 0; - for (auto& feature: collection_geog_ptr->Features()) { - area += s2_area(*feature); - } - return area; + return s2_area(*collection_geog_ptr); } // if custom subclasses are ever a thing, we can go through the builder From 6c0307ff4b782b0f995e9eeeb72b8dcb4b01b4d9 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 12 Feb 2022 13:16:16 -0400 Subject: [PATCH 12/88] try to fix R 3.6 on Windows --- src/s2-geography/geography.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/s2-geography/geography.cpp b/src/s2-geography/geography.cpp index 42af5765..387884ca 100644 --- a/src/s2-geography/geography.cpp +++ b/src/s2-geography/geography.cpp @@ -34,7 +34,9 @@ std::unique_ptr S2GeographyOwningPoint::Region() const { region->Add(absl::make_unique(point)); } - return region; + // because Rtools for R 3.6 on Windows complains about a direct + // return region + return std::unique_ptr(region.release()); } void S2GeographyOwningPoint::GetCellUnionBound(std::vector* cell_ids) const { @@ -59,7 +61,9 @@ std::unique_ptr S2GeographyOwningPolyline::Region() const { for (const auto& polyline: polylines_) { region->Add(std::unique_ptr(polyline->Clone())); } - return region; + // because Rtools for R 3.6 on Windows complains about a direct + // return region + return std::unique_ptr(region.release()); } void S2GeographyOwningPolyline::GetCellUnionBound(std::vector* cell_ids) const { @@ -100,5 +104,7 @@ std::unique_ptr S2GeographyCollection::Region() const { for (const auto& feature: features_) { region->Add(feature->Region()); } - return region; + // because Rtools for R 3.6 on Windows complains about a direct + // return region + return std::unique_ptr(region.release()); } From 8b4f6564d100b652907189ac2e296f84eabce963 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 12 Feb 2022 13:44:54 -0400 Subject: [PATCH 13/88] a few more operators --- src/Makevars.in | 1 + src/Makevars.win | 1 + src/s2-accessors.cpp | 26 ++++------------------ src/s2-geography/linear-referencing.cpp | 29 +++++++++++++++++++++++++ src/s2-geography/linear-referencing.hpp | 10 +++++++++ src/s2-geography/s2-geography.hpp | 1 + tests/testthat/test-s2-accessors.R | 12 +++++----- 7 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 src/s2-geography/linear-referencing.cpp create mode 100644 src/s2-geography/linear-referencing.hpp diff --git a/src/Makevars.in b/src/Makevars.in index ed64e545..4ab00561 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -106,6 +106,7 @@ OBJECTS = $(ABSL_LIBS) \ s2-geography/capi/tessellator.o \ s2-geography/capi/status.o \ s2-geography/accessors.o \ + s2-geography/linear-referencing.o \ s2-geography/geography.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ diff --git a/src/Makevars.win b/src/Makevars.win index d4615c1a..00731d67 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -90,6 +90,7 @@ ABSL_LIBS = absl/base/internal/cycleclock.o \ S2LIBS = $(ABSL_LIBS) \ s2-geography/capi/tessellator.o \ s2-geography/capi/status.o \ + s2-geography/linear-referencing.o \ s2-geography/accessors.o \ s2-geography/geography.o \ s2/base/stringprintf.o \ diff --git a/src/s2-accessors.cpp b/src/s2-accessors.cpp index d2328fb7..e8539f97 100644 --- a/src/s2-accessors.cpp +++ b/src/s2-accessors.cpp @@ -5,7 +5,7 @@ #include using namespace Rcpp; -#include "s2-geography/accessors.hpp" +#include "s2-geography/s2-geography.hpp" // [[Rcpp::export]] LogicalVector cpp_s2_is_collection(List geog) { @@ -170,27 +170,9 @@ NumericVector cpp_s2_project_normalized(List geog1, List geog2) { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - if (feature1->IsCollection() || feature2->IsCollection()) { - throw GeographyOperatorException("`x` and `y` must both be simple geographies"); - } - - if (feature1->IsEmpty() || feature2->IsEmpty()) { - return NA_REAL; - } - - if (feature1->GeographyType() == Geography::Type::GEOGRAPHY_POLYLINE) { - if (feature2->GeographyType() == Geography::Type::GEOGRAPHY_POINT) { - S2Point point = feature2->Point()->at(0); - int next_vertex; - S2Point point_on_line = feature1->Polyline()->at(0)->Project(point, &next_vertex); - return feature1->Polyline()->at(0)->UnInterpolate(point_on_line, next_vertex); - } else { - throw GeographyOperatorException("`y` must be a point geography"); - } - } else { - throw GeographyOperatorException("`x` must be a polyline geography"); - } - return NA_REAL; + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + return s2geography::s2_project_normalized(*geog1, *geog2); } }; diff --git a/src/s2-geography/linear-referencing.cpp b/src/s2-geography/linear-referencing.cpp new file mode 100644 index 00000000..5213f4c3 --- /dev/null +++ b/src/s2-geography/linear-referencing.cpp @@ -0,0 +1,29 @@ + +#include "geography.hpp" +#include "linear-referencing.hpp" + +namespace s2geography { + +double s2_project_normalized(const S2GeographyOwningPolyline& geog1, + const S2GeographyOwningPoint& geog2) { + if (geog1.Polylines().size() != 1 || geog2.Points().size() != 1) { + return NAN; + } + + S2Point point = geog2.Points()[0]; + int next_vertex; + S2Point point_on_line = geog1.Polylines()[0]->Project(point, &next_vertex); + return geog1.Polylines()[0]->UnInterpolate(point_on_line, next_vertex); +} + +double s2_project_normalized(const S2Geography& geog1, const S2Geography& geog2) { + auto geog1_poly_ptr = dynamic_cast(&geog1); + auto geog2_point_ptr = dynamic_cast(&geog2); + if (geog1_poly_ptr != nullptr && geog2_point_ptr != nullptr) { + return s2_project_normalized(*geog1_poly_ptr, *geog2_point_ptr); + } else { + return NAN; + } +} + +} diff --git a/src/s2-geography/linear-referencing.hpp b/src/s2-geography/linear-referencing.hpp new file mode 100644 index 00000000..0841c6d0 --- /dev/null +++ b/src/s2-geography/linear-referencing.hpp @@ -0,0 +1,10 @@ + +#pragma once + +#include "geography.hpp" + +namespace s2geography { + +double s2_project_normalized(const S2Geography& geog1, const S2Geography& geog2); + +} diff --git a/src/s2-geography/s2-geography.hpp b/src/s2-geography/s2-geography.hpp index f17fe3a1..2c8520bc 100644 --- a/src/s2-geography/s2-geography.hpp +++ b/src/s2-geography/s2-geography.hpp @@ -3,3 +3,4 @@ #include "geography.hpp" #include "accessors.hpp" +#include "linear-referencing.hpp" diff --git a/tests/testthat/test-s2-accessors.R b/tests/testthat/test-s2-accessors.R index f0fc7306..fa5c6106 100644 --- a/tests/testthat/test-s2-accessors.R +++ b/tests/testthat/test-s2-accessors.R @@ -169,17 +169,17 @@ test_that("s2_project() and s2_project_normalized() work", { c(0, 0.25, 0.75, 1, NA_real_, NA_real_) ) - expect_error( + expect_identical( s2_project_normalized("POINT (0 1)", "POINT (0 1)"), - "must be a polyline" + NaN ) - expect_error( + expect_identical( s2_project_normalized("LINESTRING (0 1, 1 1)", "LINESTRING (0 1, 1 1)"), - "must be a point" + NaN ) - expect_error( + expect_identical( s2_project_normalized("LINESTRING (0 1, 1 1)", "MULTIPOINT (0 1, 1 1)"), - "must both be simple geographies" + NaN ) }) From 46c3d507a48c228f299fc181bf9b6bbcdd3f3f00 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 13 Feb 2022 13:26:03 -0400 Subject: [PATCH 14/88] move distance and max_distance --- src/Makevars.in | 1 + src/Makevars.win | 1 + src/s2-accessors.cpp | 22 +++++++-------- src/s2-geography/distance.cpp | 30 ++++++++++++++++++++ src/s2-geography/distance.hpp | 11 ++++++++ src/s2-geography/geography.cpp | 36 ++++++++++++++++++++++++ src/s2-geography/geography.hpp | 46 +++++++++++++++++++++++++++++++ src/s2-geography/s2-geography.hpp | 1 + 8 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 src/s2-geography/distance.cpp create mode 100644 src/s2-geography/distance.hpp diff --git a/src/Makevars.in b/src/Makevars.in index 4ab00561..b6311e12 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -107,6 +107,7 @@ OBJECTS = $(ABSL_LIBS) \ s2-geography/capi/status.o \ s2-geography/accessors.o \ s2-geography/linear-referencing.o \ + s2-geography/distance.o \ s2-geography/geography.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ diff --git a/src/Makevars.win b/src/Makevars.win index 00731d67..1a108a74 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -91,6 +91,7 @@ S2LIBS = $(ABSL_LIBS) \ s2-geography/capi/tessellator.o \ s2-geography/capi/status.o \ s2-geography/linear-referencing.o \ + s2-geography/distance.o \ s2-geography/accessors.o \ s2-geography/geography.o \ s2/base/stringprintf.o \ diff --git a/src/s2-accessors.cpp b/src/s2-accessors.cpp index e8539f97..8007d4c9 100644 --- a/src/s2-accessors.cpp +++ b/src/s2-accessors.cpp @@ -1,7 +1,5 @@ #include "geography-operator.h" -#include "s2/s2closest_edge_query.h" -#include "s2/s2furthest_edge_query.h" #include using namespace Rcpp; @@ -187,13 +185,13 @@ NumericVector cpp_s2_distance(List geog1, List geog2) { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - S2ClosestEdgeQuery query(feature1->ShapeIndex()); - S2ClosestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex()); + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); - const auto& result = query.FindClosestEdge(&target); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); - S1ChordAngle angle = result.distance(); - double distance = angle.ToAngle().radians(); + double distance = s2geography::s2_distance(index1, index2); if (distance == R_PosInf) { return NA_REAL; @@ -214,13 +212,13 @@ NumericVector cpp_s2_max_distance(List geog1, List geog2) { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - S2FurthestEdgeQuery query(feature1->ShapeIndex()); - S2FurthestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex()); + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); - const auto& result = query.FindFurthestEdge(&target); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); - S1ChordAngle angle = result.distance(); - double distance = angle.ToAngle().radians(); + double distance = s2geography::s2_max_distance(index1, index2); // returns -1 if one of the indexes is empty // NA is more consistent with the BigQuery diff --git a/src/s2-geography/distance.cpp b/src/s2-geography/distance.cpp new file mode 100644 index 00000000..739bc5a5 --- /dev/null +++ b/src/s2-geography/distance.cpp @@ -0,0 +1,30 @@ + +#include "s2/s2closest_edge_query.h" +#include "s2/s2furthest_edge_query.h" + +#include "geography.hpp" +#include "distance.hpp" + +namespace s2geography { + +double s2_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2) { + S2ClosestEdgeQuery query(&geog1.ShapeIndex()); + S2ClosestEdgeQuery::ShapeIndexTarget target(&geog2.ShapeIndex()); + + const auto& result = query.FindClosestEdge(&target); + + S1ChordAngle angle = result.distance(); + return angle.ToAngle().radians(); +} + +double s2_max_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2) { + S2FurthestEdgeQuery query(&geog1.ShapeIndex()); + S2FurthestEdgeQuery::ShapeIndexTarget target(&geog2.ShapeIndex()); + + const auto& result = query.FindFurthestEdge(&target); + + S1ChordAngle angle = result.distance(); + return angle.ToAngle().radians(); +} + +} diff --git a/src/s2-geography/distance.hpp b/src/s2-geography/distance.hpp new file mode 100644 index 00000000..97fe8732 --- /dev/null +++ b/src/s2-geography/distance.hpp @@ -0,0 +1,11 @@ + +#pragma once + +#include "geography.hpp" + +namespace s2geography { + +double s2_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2); +double s2_max_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2); + +} diff --git a/src/s2-geography/geography.cpp b/src/s2-geography/geography.cpp index 387884ca..49d80094 100644 --- a/src/s2-geography/geography.cpp +++ b/src/s2-geography/geography.cpp @@ -15,6 +15,28 @@ using namespace s2geography; +// This class is a shim to allow a class to return a std::unique_ptr(), +// which is required by MutableS2ShapeIndex::Add(), without copying the underlying +// data. S2Shape instances do not typically own their data (e.g., S2Polygon::Shape), +// so this does not change the general relationship (that anything returned by +// S2Geography::Shape() is only valid within the scope of the S2Geography). +class S2ShapeWrapper: public S2Shape { +public: + S2ShapeWrapper(S2Shape* shape): shape_(shape) {} + int num_edges() const { return shape_->num_edges();} + Edge edge(int edge_id) const { return shape_->edge(edge_id); } + int dimension() const { return shape_->dimension(); } + ReferencePoint GetReferencePoint() const { return shape_->GetReferencePoint(); } + int num_chains() const { return shape_->num_chains(); } + Chain chain(int chain_id) const { return shape_->chain(chain_id); } + Edge chain_edge(int chain_id, int offset) const { return shape_->chain_edge(chain_id, offset); } + ChainPosition chain_position(int edge_id) const { return shape_->chain_position(edge_id); } + +private: + S2Shape* shape_; +}; + + void S2Geography::GetCellUnionBound(std::vector* cell_ids) const { MutableS2ShapeIndex index; for (int i = 0; i < num_shapes(); i++) { @@ -108,3 +130,17 @@ std::unique_ptr S2GeographyCollection::Region() const { // return region return std::unique_ptr(region.release()); } + +int S2GeographyShapeIndex::num_shapes() const { return shape_index_.num_shape_ids(); } + +std::unique_ptr S2GeographyShapeIndex::Shape(int id) const { + S2Shape* shape = shape_index_.shape(id); + return std::unique_ptr(new S2ShapeWrapper(shape)); +} + +std::unique_ptr S2GeographyShapeIndex::Region() const { + auto region = absl::make_unique>(&shape_index_); + // because Rtools for R 3.6 on Windows complains about a direct + // return region + return std::unique_ptr(region.release()); +} diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index 34e5c832..c8efb568 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -145,4 +145,50 @@ class S2GeographyCollection: public S2Geography { int total_shapes_; }; + +// An S2Geography with a MutableS2ShapeIndex as the underlying data. +// These are used as inputs for operations that are implemented in S2 +// using the S2ShapeIndex (e.g., boolean operations). If an S2Geography +// instance will be used repeatedly, it will be faster to construct +// one S2GeographyShapeIndex and use it repeatedly. This class does not +// own any S2Geography objects that are added do it and thus is only +// valid for the scope of those objects. +class S2GeographyShapeIndex: public S2Geography { +public: + S2GeographyShapeIndex() {} + + explicit S2GeographyShapeIndex(const S2Geography& geog) { + Add(geog); + } + + // Add a S2Geography to the index, returning the last shape_id + // that was added to the index or -1 if no shapes were added + // to the index. Shape ids are assigned sequentially and the first + // shape_id that was added can be obtained by subtracting + // geog.num_shapes(). + int Add(const S2Geography& geog) { + int id = -1; + for (int i = 0; i < geog.num_shapes(); i++) { + id = shape_index_.Add(geog.Shape(i)); + } + return id; + } + + int num_shapes() const; + std::unique_ptr Shape(int id) const; + std::unique_ptr Region() const; + + const S2ShapeIndex& ShapeIndex() const { + return shape_index_; + } + + S2ShapeIndex& MutableShapeIndex() { + return shape_index_; + } + +private: + MutableS2ShapeIndex shape_index_; +}; + + } diff --git a/src/s2-geography/s2-geography.hpp b/src/s2-geography/s2-geography.hpp index 2c8520bc..b5347aab 100644 --- a/src/s2-geography/s2-geography.hpp +++ b/src/s2-geography/s2-geography.hpp @@ -4,3 +4,4 @@ #include "geography.hpp" #include "accessors.hpp" #include "linear-referencing.hpp" +#include "distance.hpp" From 843764eaa70e3747112b5894c3aa4222474d4f38 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 14 Feb 2022 08:54:19 -0400 Subject: [PATCH 15/88] implement s2_boundary in new api --- src/geography-shim.h | 39 ++++++++++++++++++++ src/s2-geography/accessors.cpp | 67 ++++++++++++++++++++++++++++++++++ src/s2-geography/accessors.hpp | 3 ++ src/s2-geography/geography.hpp | 2 + src/s2-transformers.cpp | 5 ++- 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/geography-shim.h diff --git a/src/geography-shim.h b/src/geography-shim.h new file mode 100644 index 00000000..2eff8471 --- /dev/null +++ b/src/geography-shim.h @@ -0,0 +1,39 @@ + +#include "point-geography.h" +#include "polyline-geography.h" +#include "polygon-geography.h" +#include "geography-collection.h" +#include "s2-geography/s2-geography.hpp" + +std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog) { + auto point = dynamic_cast(&geog); + if (point != nullptr) { + return absl::make_unique(point->Points()); + } + + auto polyline = dynamic_cast(&geog); + if (polyline != nullptr) { + std::vector> polylines; + for (auto& poly: polyline->Polylines()) { + polylines.push_back(std::unique_ptr(poly->Clone())); + } + + return absl::make_unique(std::move(polylines)); + } + + auto polygon = dynamic_cast(&geog); + if (polygon != nullptr) { + return absl::make_unique(std::unique_ptr(polygon->Polygon()->Clone())); + } + + auto collection = dynamic_cast(&geog); + if (collection != nullptr) { + std::vector> features; + for (auto& feat: collection->Features()) { + features.push_back(MakeOldGeography(*feat)); + } + return absl::make_unique(std::move(features)); + } + + throw s2geography::S2GeographyException("Unsupported S2Geography subclass"); +} diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 1b82cc57..458149fe 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -19,6 +19,11 @@ bool s2_is_collection(const S2GeographyOwningPolygon& geog) { bool s2_is_collection(const S2Geography& geog) { int dimension = s2_dimension(geog); + + if (dimension == -1) { + return false; + } + if (dimension == 0) { return s2_num_points(geog) > 1; } @@ -183,4 +188,66 @@ double s2_y(const S2Geography& geog) { return out; } + +S2Point s2_centroid(const S2Geography& geog) { + int dimension = s2_dimension(geog); + if (dimension <= 0) { + return S2Point(NAN, NAN, NAN); + } + + +} + +std::unique_ptr s2_boundary(const S2Geography& geog) { + int dimension = s2_dimension(geog); + + if (dimension == 1) { + std::vector endpoints; + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); + if (shape->dimension() > 1) { + continue; + } + + for (int j = 0; j < shape->num_chains(); j++) { + S2Shape::Chain chain = shape->chain(j); + if (chain.length > 0) { + endpoints.push_back(shape->edge(chain.start).v0); + endpoints.push_back(shape->edge(chain.start + chain.length - 1).v1); + } + } + } + + return absl::make_unique(std::move(endpoints)); + } + + if (dimension == 2) { + std::vector> polylines; + + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); + if (shape->dimension() != 2) { + throw S2GeographyException("Can't extract boundary from heterogeneous collection"); + } + + for (int j = 0; j < shape->num_chains(); j++) { + std::vector points; + S2Shape::Chain chain = shape->chain(j); + + points.push_back(shape->edge(chain.start).v0); + for (int k = 0; k < chain.length; k++) { + points.push_back(shape->edge(chain.start + k).v1); + } + + auto polyline = absl::make_unique(std::move(points)); + polylines.push_back(std::move(polyline)); + } + } + + return absl::make_unique(std::move(polylines)); + } + + return absl::make_unique(); +} + } diff --git a/src/s2-geography/accessors.hpp b/src/s2-geography/accessors.hpp index 929c017f..caf96af6 100644 --- a/src/s2-geography/accessors.hpp +++ b/src/s2-geography/accessors.hpp @@ -15,4 +15,7 @@ double s2_perimeter(const S2Geography& geog); double s2_x(const S2Geography& geog); double s2_y(const S2Geography& geog); +S2Point s2_centroid(const S2Geography& geog); +std::unique_ptr s2_boundary(const S2Geography& geog); + } diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index c8efb568..214dfed8 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -122,6 +122,8 @@ class S2GeographyOwningPolygon: public S2Geography { // can be used to represent a simple features GEOMETRYCOLLECTION. class S2GeographyCollection: public S2Geography { public: + S2GeographyCollection(): total_shapes_(0) {} + S2GeographyCollection(std::vector> features): features_(std::move(features)), total_shapes_(0) { diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index f09fbe9e..a1f949ff 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -22,6 +22,7 @@ #include "polyline-geography.h" #include "polygon-geography.h" #include "geography-collection.h" +#include "geography-shim.h" #include using namespace Rcpp; @@ -521,7 +522,9 @@ List cpp_s2_point_on_surface(List geog) { List cpp_s2_boundary(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { - std::unique_ptr ptr = feature->Boundary(); + auto geog = feature->NewGeography(); + std::unique_ptr result = s2geography::s2_boundary(*geog); + std::unique_ptr ptr = MakeOldGeography(*result); return XPtr(ptr.release()); } }; From 5e79b0a760e6a7978c69569ecc261baf56b2d1c4 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 14 Feb 2022 09:02:06 -0400 Subject: [PATCH 16/88] slightly better boundary logic --- src/s2-geography/accessors.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 458149fe..b216dab2 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -209,6 +209,7 @@ std::unique_ptr s2_boundary(const S2Geography& geog) { continue; } + endpoints.reserve(endpoints.size() + shape->num_chains() * 2); for (int j = 0; j < shape->num_chains(); j++) { S2Shape::Chain chain = shape->chain(j); if (chain.length > 0) { @@ -223,6 +224,7 @@ std::unique_ptr s2_boundary(const S2Geography& geog) { if (dimension == 2) { std::vector> polylines; + polylines.reserve(geog.num_shapes()); for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); @@ -231,16 +233,18 @@ std::unique_ptr s2_boundary(const S2Geography& geog) { } for (int j = 0; j < shape->num_chains(); j++) { - std::vector points; S2Shape::Chain chain = shape->chain(j); + if (chain.length > 0) { + std::vector points(chain.length + 1); - points.push_back(shape->edge(chain.start).v0); - for (int k = 0; k < chain.length; k++) { - points.push_back(shape->edge(chain.start + k).v1); - } + points[0] = shape->edge(chain.start).v0; + for (int k = 0; k < chain.length; k++) { + points[k + 1] = shape->edge(chain.start + k).v1; + } - auto polyline = absl::make_unique(std::move(points)); - polylines.push_back(std::move(polyline)); + auto polyline = absl::make_unique(std::move(points)); + polylines.push_back(std::move(polyline)); + } } } From 2e9909e0050ffeee374901180e9e31de4992103f Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 14 Feb 2022 09:04:55 -0400 Subject: [PATCH 17/88] fix R 3.6 for windows --- src/geography-shim.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/geography-shim.h b/src/geography-shim.h index 2eff8471..7b3e5fe9 100644 --- a/src/geography-shim.h +++ b/src/geography-shim.h @@ -8,7 +8,8 @@ std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog) { auto point = dynamic_cast(&geog); if (point != nullptr) { - return absl::make_unique(point->Points()); + auto ptr = absl::make_unique(point->Points()); + return std::unique_ptr(ptr.get()); } auto polyline = dynamic_cast(&geog); @@ -18,12 +19,14 @@ std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog polylines.push_back(std::unique_ptr(poly->Clone())); } - return absl::make_unique(std::move(polylines)); + auto ptr = absl::make_unique(std::move(polylines)); + return std::unique_ptr(ptr.get()); } auto polygon = dynamic_cast(&geog); if (polygon != nullptr) { - return absl::make_unique(std::unique_ptr(polygon->Polygon()->Clone())); + auto ptr = absl::make_unique(std::unique_ptr(polygon->Polygon()->Clone())); + return std::unique_ptr(ptr.get()); } auto collection = dynamic_cast(&geog); @@ -32,7 +35,8 @@ std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog for (auto& feat: collection->Features()) { features.push_back(MakeOldGeography(*feat)); } - return absl::make_unique(std::move(features)); + auto ptr = absl::make_unique(std::move(features)); + return std::unique_ptr(ptr.get()); } throw s2geography::S2GeographyException("Unsupported S2Geography subclass"); From 826cc6ec9b94d2a62028f49c869a959e80194d63 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 14 Feb 2022 09:08:31 -0400 Subject: [PATCH 18/88] undo kludge that cause segfault --- src/geography-shim.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/geography-shim.h b/src/geography-shim.h index 7b3e5fe9..2eff8471 100644 --- a/src/geography-shim.h +++ b/src/geography-shim.h @@ -8,8 +8,7 @@ std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog) { auto point = dynamic_cast(&geog); if (point != nullptr) { - auto ptr = absl::make_unique(point->Points()); - return std::unique_ptr(ptr.get()); + return absl::make_unique(point->Points()); } auto polyline = dynamic_cast(&geog); @@ -19,14 +18,12 @@ std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog polylines.push_back(std::unique_ptr(poly->Clone())); } - auto ptr = absl::make_unique(std::move(polylines)); - return std::unique_ptr(ptr.get()); + return absl::make_unique(std::move(polylines)); } auto polygon = dynamic_cast(&geog); if (polygon != nullptr) { - auto ptr = absl::make_unique(std::unique_ptr(polygon->Polygon()->Clone())); - return std::unique_ptr(ptr.get()); + return absl::make_unique(std::unique_ptr(polygon->Polygon()->Clone())); } auto collection = dynamic_cast(&geog); @@ -35,8 +32,7 @@ std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog for (auto& feat: collection->Features()) { features.push_back(MakeOldGeography(*feat)); } - auto ptr = absl::make_unique(std::move(features)); - return std::unique_ptr(ptr.get()); + return absl::make_unique(std::move(features)); } throw s2geography::S2GeographyException("Unsupported S2Geography subclass"); From 2b9e709c3a6919059ebcc32d3902ff18dbc13f85 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 14 Feb 2022 09:09:04 -0400 Subject: [PATCH 19/88] remove non-functional stub --- src/s2-geography/accessors.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index b216dab2..2952eba5 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -190,12 +190,7 @@ double s2_y(const S2Geography& geog) { S2Point s2_centroid(const S2Geography& geog) { - int dimension = s2_dimension(geog); - if (dimension <= 0) { - return S2Point(NAN, NAN, NAN); - } - - + throw S2GeographyException("Not implemented: s2_centroid()"); } std::unique_ptr s2_boundary(const S2Geography& geog) { From 6a98cf7ef6061eadd7607a2c85ea593698efd983 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 16 Feb 2022 21:04:20 -0400 Subject: [PATCH 20/88] make s2_dimension() cheaper --- src/s2-geography/accessors.cpp | 6 +++++- src/s2-geography/geography.hpp | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 2952eba5..8afc64af 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -52,7 +52,11 @@ bool s2_is_collection(const S2Geography& geog) { } int s2_dimension(const S2Geography& geog) { - int dimension = -1; + int dimension = geog.dimension(); + if (dimension != -1) { + return dimension; + } + for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); if (shape->dimension() > dimension) { diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index 214dfed8..84cca0af 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -27,6 +27,8 @@ class S2Geography { virtual ~S2Geography() {} + virtual int dimension() const { return -1; } + // The number of S2Shape objects needed to represent this S2Geography virtual int num_shapes() const = 0; @@ -58,6 +60,7 @@ class S2GeographyOwningPoint: public S2Geography { S2GeographyOwningPoint(S2Point point): points_(1) { points_.push_back(point); } S2GeographyOwningPoint(std::vector points): points_(std::move(points)) {} + int dimension() const { return 0; } int num_shapes() const { return 1; } std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; @@ -80,6 +83,7 @@ class S2GeographyOwningPolyline: public S2Geography { S2GeographyOwningPolyline(std::vector> polylines): polylines_(std::move(polylines)) {} + int dimension() const { return 1; } int num_shapes() const; std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; @@ -104,6 +108,7 @@ class S2GeographyOwningPolygon: public S2Geography { S2GeographyOwningPolygon(std::unique_ptr polygon): polygon_(std::move(polygon)) {} + int dimension() const { return 2; } int num_shapes() const { return 1; } std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; @@ -133,6 +138,7 @@ class S2GeographyCollection: public S2Geography { } } + int dimension() const { return -1; } int num_shapes() const; std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; From ed0506eb3005399e2994c8dc141f6fdb341325d2 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 16 Feb 2022 21:47:57 -0400 Subject: [PATCH 21/88] add s2_centroid() to new API --- src/s2-geography/accessors.cpp | 50 ++++++++++++++++++++++++++++++++-- src/s2-geography/geography.hpp | 19 +++++++++++-- src/s2-transformers.cpp | 3 +- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 8afc64af..3cff694c 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -1,4 +1,6 @@ +#include "s2/s2centroids.h" + #include "geography.hpp" #include "accessors.hpp" @@ -194,7 +196,51 @@ double s2_y(const S2Geography& geog) { S2Point s2_centroid(const S2Geography& geog) { - throw S2GeographyException("Not implemented: s2_centroid()"); + S2Point centroid(0, 0, 0); + + if (geog.dimension() == 0) { + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); + for (int j = 0; j < shape->num_edges(); j++) { + centroid += shape->edge(j).v0; + } + } + + return centroid.Normalize(); + } + + if (geog.dimension() == 1) { + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); + for (int j = 0; j < shape->num_edges(); j++) { + S2Shape::Edge e = shape->edge(j); + centroid += S2::TrueCentroid(e.v0, e.v1); + } + } + + return centroid.Normalize(); + } + + if (geog.dimension() == 2) { + auto polygon_ptr = dynamic_cast(&geog); + if (polygon_ptr == nullptr) { + throw S2GeographyException("Can't compute s2_centroid() on custom polygon geography"); + } + + centroid = polygon_ptr->Polygon()->GetCentroid(); + return centroid.Normalize(); + } + + auto collection_ptr = dynamic_cast(&geog); + if (collection_ptr == nullptr) { + throw S2GeographyException("Can't compute s2_centroid() on custom collection geography"); + } + + for (auto& feat: collection_ptr->Features()) { + centroid += s2_centroid(*feat); + } + + return centroid.Normalize(); } std::unique_ptr s2_boundary(const S2Geography& geog) { @@ -204,7 +250,7 @@ std::unique_ptr s2_boundary(const S2Geography& geog) { std::vector endpoints; for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); - if (shape->dimension() > 1) { + if (shape->dimension() < 1) { continue; } diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index 84cca0af..d0e4a372 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -27,7 +27,23 @@ class S2Geography { virtual ~S2Geography() {} - virtual int dimension() const { return -1; } + // Returns 0, 1, or 2 if all Shape()s that are returned will have + // the same dimension (i.e., they are all points, all lines, or + // all polygons). + virtual int dimension() const { + if (num_shapes() == 0) { + return -1; + } + + int dim = Shape(0)->dimension(); + for (int i = 2; i < num_shapes(); i++) { + if (dim != Shape(i)->dimension()) { + return -1; + } + } + + return dim; + } // The number of S2Shape objects needed to represent this S2Geography virtual int num_shapes() const = 0; @@ -138,7 +154,6 @@ class S2GeographyCollection: public S2Geography { } } - int dimension() const { return -1; } int num_shapes() const; std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index a1f949ff..ee8e1d54 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -448,7 +448,8 @@ List cpp_s2_minimum_clearance_line_between(List geog1, List geog2) { List cpp_s2_centroid(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { - S2Point centroid = feature->Centroid(); + auto geog = feature->NewGeography(); + S2Point centroid = s2geography::s2_centroid(*geog); if (centroid.Norm2() == 0) { return XPtr(new PointGeography()); } else { From f9f0642097a44860ce47258200f1e5a9f744bda5 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 16 Feb 2022 21:51:03 -0400 Subject: [PATCH 22/88] s2_centroid_agg() use new s2_centroid() --- src/s2-transformers.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index ee8e1d54..e49d9bff 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -316,7 +316,8 @@ List cpp_s2_centroid_agg(List geog, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); - S2Point centroid = feature->Centroid(); + auto geog = feature->NewGeography(); + S2Point centroid = s2geography::s2_centroid(*geog); if (centroid.Norm2() > 0) { cumCentroid += centroid.Normalize(); } From 5978ca2f3990b7d1507812bc53287fcd8c1062fa Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 16 Feb 2022 22:10:34 -0400 Subject: [PATCH 23/88] move s2_closest_point() and s2_clearance_line_between() to new api --- src/s2-geography/distance.cpp | 39 ++++++++++++++++++ src/s2-geography/distance.hpp | 3 ++ src/s2-transformers.cpp | 77 +++++++++++++---------------------- 3 files changed, 71 insertions(+), 48 deletions(-) diff --git a/src/s2-geography/distance.cpp b/src/s2-geography/distance.cpp index 739bc5a5..5a686b18 100644 --- a/src/s2-geography/distance.cpp +++ b/src/s2-geography/distance.cpp @@ -27,4 +27,43 @@ double s2_max_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShap return angle.ToAngle().radians(); } +S2Point s2_closest_point(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2) { + return s2_minimum_clearance_line_between(geog1, geog2).first; +} + +std::pair s2_minimum_clearance_line_between( + const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2) { + S2ClosestEdgeQuery query1(&geog1.ShapeIndex()); + query1.mutable_options()->set_include_interiors(false); + S2ClosestEdgeQuery::ShapeIndexTarget target(&geog2.ShapeIndex()); + + const auto& result1 = query1.FindClosestEdge(&target); + + if (result1.edge_id() == -1) { + return std::pair(S2Point(0, 0, 0), S2Point(0, 0, 0)); + } + + // Get the edge from index1 (edge1) that is closest to index2. + S2Shape::Edge edge1 = query1.GetEdge(result1); + + // Now find the edge from index2 (edge2) that is closest to edge1. + S2ClosestEdgeQuery query2(&geog2.ShapeIndex()); + query2.mutable_options()->set_include_interiors(false); + S2ClosestEdgeQuery::EdgeTarget target2(edge1.v0, edge1.v1); + auto result2 = query2.FindClosestEdge(&target2); + + // what if result2 has no edges? + if (result2.is_interior()) { + throw S2GeographyException("S2ClosestEdgeQuery result is interior!"); + } + + S2Shape::Edge edge2 = query2.GetEdge(result2); + + // Find the closest point pair on edge1 and edge2. + return S2::GetEdgePairClosestPoints( + edge1.v0, edge1.v1, + edge2.v0, edge2.v1 + ); +} + } diff --git a/src/s2-geography/distance.hpp b/src/s2-geography/distance.hpp index 97fe8732..9e888c4d 100644 --- a/src/s2-geography/distance.hpp +++ b/src/s2-geography/distance.hpp @@ -7,5 +7,8 @@ namespace s2geography { double s2_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2); double s2_max_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2); +S2Point s2_closest_point(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2); +std::pair s2_minimum_clearance_line_between( + const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2); } diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index e49d9bff..9b2d10d0 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -361,57 +361,21 @@ List cpp_s2_rebuild_agg(List geog, List s2options, bool naRm) { return List::create(Rcpp::XPtr(geography.release())); } -std::vector findClosestPoints(S2ShapeIndex* index1, S2ShapeIndex* index2) { - // see http://s2geometry.io/devguide/s2closestedgequery.html section on Modeling Accuracy: - - // Find the edge from index2 that is closest to index1 - S2ClosestEdgeQuery query1(index1); - query1.mutable_options()->set_include_interiors(false); - S2ClosestEdgeQuery::ShapeIndexTarget target1(index2); - auto result1 = query1.FindClosestEdge(&target1); - - if (result1.edge_id() == -1) { - return std::vector(); - } - - // Get the edge from index1 (edge1) that is closest to index2. - S2Shape::Edge edge1 = query1.GetEdge(result1); - - // Now find the edge from index2 (edge2) that is closest to edge1. - S2ClosestEdgeQuery query2(index2); - query2.mutable_options()->set_include_interiors(false); - S2ClosestEdgeQuery::EdgeTarget target2(edge1.v0, edge1.v1); - auto result2 = query2.FindClosestEdge(&target2); - - // what if result2 has no edges? - if (result2.is_interior()) { - stop("S2ClosestEdgeQuery result is interior!"); - } - S2Shape::Edge edge2 = query2.GetEdge(result2); - - // Find the closest point pair on edge1 and edge2. - std::pair closest = S2::GetEdgePairClosestPoints( - edge1.v0, edge1.v1, - edge2.v0, edge2.v1 - ); - - std::vector pts(2); - pts[0] = closest.first; - pts[1] = closest.second; - return pts; -} - // [[Rcpp::export]] List cpp_s2_closest_point(List geog1, List geog2) { class Op: public BinaryGeographyOperator { SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - std::vector pts = findClosestPoints(feature1->ShapeIndex(), feature2->ShapeIndex()); + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); - if (pts.size() == 0) { + S2Point pt = s2geography::s2_closest_point(index1, index2); + if (pt.Norm2() == 0) { return XPtr(new PointGeography()); } else { - return XPtr(new PointGeography(pts[0])); + return XPtr(new PointGeography(pt)); } } }; @@ -425,15 +389,32 @@ List cpp_s2_minimum_clearance_line_between(List geog1, List geog2) { class Op: public BinaryGeographyOperator { SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - std::vector pts = findClosestPoints(feature1->ShapeIndex(), feature2->ShapeIndex()); + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); + + std::pair pts = s2geography::s2_minimum_clearance_line_between( + index1, + index2 + ); - if (pts.size() == 0) { + if (pts.first.Norm2() == 0) { return XPtr(new PolylineGeography()); - } else if (pts[0] == pts[1]) { - return XPtr(new PointGeography(pts)); + } + + std::vector vertices(2); + vertices[0] = pts.first; + vertices[1] = pts.second; + + if (pts.first == pts.second) { + return XPtr(new PointGeography(vertices)); } else { + std::vector vertices(2); + vertices[0] = pts.first; + vertices[1] = pts.second; std::unique_ptr polyline = absl::make_unique(); - polyline->Init(pts); + polyline->Init(vertices); std::vector> polylines(1); polylines[0] = std::move(polyline); return XPtr(new PolylineGeography(std::move(polylines))); From 8dc5272eb281f74e88bb6be4d58027b84610388f Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 20 Feb 2022 21:44:38 -0400 Subject: [PATCH 24/88] use new API for boolean operations --- src/Makevars.in | 1 + src/Makevars.win | 1 + src/s2-geography/build.cpp | 161 ++++++++++++++++++++++++++++++ src/s2-geography/build.hpp | 56 +++++++++++ src/s2-geography/s2-geography.hpp | 1 + src/s2-options.h | 27 +++++ src/s2-transformers.cpp | 21 ++-- 7 files changed, 258 insertions(+), 10 deletions(-) create mode 100644 src/s2-geography/build.cpp create mode 100644 src/s2-geography/build.hpp diff --git a/src/Makevars.in b/src/Makevars.in index b6311e12..2235d25b 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -108,6 +108,7 @@ OBJECTS = $(ABSL_LIBS) \ s2-geography/accessors.o \ s2-geography/linear-referencing.o \ s2-geography/distance.o \ + s2-geography/build.o \ s2-geography/geography.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ diff --git a/src/Makevars.win b/src/Makevars.win index 1a108a74..fe785a86 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -93,6 +93,7 @@ S2LIBS = $(ABSL_LIBS) \ s2-geography/linear-referencing.o \ s2-geography/distance.o \ s2-geography/accessors.o \ + s2-geography/build.o \ s2-geography/geography.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp new file mode 100644 index 00000000..23b5de73 --- /dev/null +++ b/src/s2-geography/build.cpp @@ -0,0 +1,161 @@ + +#include "s2/s2boolean_operation.h" +#include "s2/s2builder.h" +#include "s2/s2builderutil_s2polygon_layer.h" +#include "s2/s2builderutil_s2polyline_vector_layer.h" +#include "s2/s2builderutil_s2point_vector_layer.h" +#include "s2/s2builderutil_closed_set_normalizer.h" + +#include "geography.hpp" +#include "build.hpp" + +namespace s2geography { + +enum BuilderOutputDimension { + OUTPUT_DIMENSION_POINT = 1, + OUTPUT_DIMENSION_POLYLINE = 2, + OUTPUT_DIMENSION_POLYGON = 4 +}; + +std::unique_ptr s2_geography_from_layers(std::vector points, + std::vector> polylines, + std::unique_ptr polygon, + const S2GeographyOptions& options) { + // count non-empty dimensions + bool has_polygon = !polygon->is_empty(); + bool has_polylines = polylines.size() > 0; + bool has_points = points.size() > 0; + + // use the requstested dimensions to produce the right kind of EMTPY + bool include_polygon = options.polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; + bool include_polylines = options.polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; + bool include_points = options.point_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; + + if (has_polygon && options.polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { + throw S2GeographyException("Output contained unexpected polygon"); + } else if (has_polygon && options.polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { + has_polygon = false; + } + + if (has_polylines && options.polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { + throw S2GeographyException("Output contained unexpected polylines"); + } else if (has_polylines && options.polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { + has_polylines = false; + } + + if (has_points && options.point_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { + throw S2GeographyException("Output contained unexpected points"); + } else if (has_points && options.point_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { + has_points = false; + } + + int non_empty_dimensions = has_polygon + has_polylines + has_points; + int included_dimensions = include_polygon + include_polylines + include_points; + + // return mixed dimension output + if (non_empty_dimensions > 1) { + std::vector> features; + + if (has_points) { + features.push_back(absl::make_unique(std::move(points))); + } + + if (has_polylines) { + features.push_back(absl::make_unique(std::move(polylines))); + } + + if (has_polygon) { + features.push_back(absl::make_unique(std::move(polygon))); + } + + return absl::make_unique(std::move(features)); + } + + // return single dimension output + if (has_polygon || (included_dimensions == 1 && include_polygon)) { + return absl::make_unique(std::move(polygon)); + } else if (has_polylines || (included_dimensions == 1 && include_polylines)) { + return absl::make_unique(std::move(polylines)); + } else if (has_points || (included_dimensions == 1 && include_points)) { + return absl::make_unique(std::move(points)); + } else { + return absl::make_unique(); + } +} + +std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + S2BooleanOperation::OpType op_type, + const S2GeographyOptions& options) { + + // Create the data structures that will contain the output. + std::vector points; + std::vector> polylines; + std::unique_ptr polygon = absl::make_unique(); + + s2builderutil::LayerVector layers(3); + layers[0] = absl::make_unique(&points, options.point_layer); + layers[1] = absl::make_unique(&polylines, options.polyline_layer); + layers[2] = absl::make_unique(polygon.get(), options.polygon_layer); + + // specify the boolean operation + S2BooleanOperation op( + op_type, + // Normalizing the closed set here is required for line intersections + // to work in the same way as GEOS + s2builderutil::NormalizeClosedSet(std::move(layers)), + options.boolean_operation + ); + + // do the boolean operation, build layers, and check for errors + S2Error error; + if (!op.Build(geog1.ShapeIndex(), geog2.ShapeIndex(), &error)) { + throw S2GeographyException(error.text()); + } + + // construct output + return s2_geography_from_layers( + std::move(points), + std::move(polylines), + std::move(polygon), + options + ); +} + +std::unique_ptr s2_intersection(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2GeographyOptions& options) { + return s2_boolean_operation( + geog1, geog2, + S2BooleanOperation::OpType::INTERSECTION, + options); +} + +std::unique_ptr s2_union(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2GeographyOptions& options) { + return s2_boolean_operation( + geog1, geog2, + S2BooleanOperation::OpType::UNION, + options); +} + +std::unique_ptr s2_difference(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2GeographyOptions& options) { + return s2_boolean_operation( + geog1, geog2, + S2BooleanOperation::OpType::DIFFERENCE, + options); +} + +std::unique_ptr s2_symmetric_difference(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2GeographyOptions& options) { + return s2_boolean_operation( + geog1, geog2, + S2BooleanOperation::OpType::SYMMETRIC_DIFFERENCE, + options); +} + +} diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp new file mode 100644 index 00000000..ede98bff --- /dev/null +++ b/src/s2-geography/build.hpp @@ -0,0 +1,56 @@ + +#pragma once + +#include "s2/s2builderutil_s2polygon_layer.h" +#include "s2/s2builderutil_s2polyline_vector_layer.h" +#include "s2/s2builderutil_s2point_vector_layer.h" + +#include "geography.hpp" + +namespace s2geography { + +class S2GeographyOptions { +public: + enum OutputAction { + OUTPUT_ACTION_INCLUDE, + OUTPUT_ACTION_IGNORE, + OUTPUT_ACTION_ERROR + }; + + S2GeographyOptions() + : point_layer_action(OUTPUT_ACTION_INCLUDE), + polyline_layer_action(OUTPUT_ACTION_INCLUDE), + polygon_layer_action(OUTPUT_ACTION_INCLUDE) {} + + S2BooleanOperation::Options boolean_operation; + + s2builderutil::S2PointVectorLayer::Options point_layer; + s2builderutil::S2PolylineVectorLayer::Options polyline_layer; + s2builderutil::S2PolygonLayer::Options polygon_layer; + OutputAction point_layer_action; + OutputAction polyline_layer_action; + OutputAction polygon_layer_action; +}; + +std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + S2BooleanOperation::OpType op_type, + const S2GeographyOptions& options); + +std::unique_ptr s2_intersection(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2GeographyOptions& options); + +std::unique_ptr s2_union(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2GeographyOptions& options); + +std::unique_ptr s2_difference(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2GeographyOptions& options); + +std::unique_ptr s2_symmetric_difference(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2GeographyOptions& options); + +} diff --git a/src/s2-geography/s2-geography.hpp b/src/s2-geography/s2-geography.hpp index b5347aab..c9be9dda 100644 --- a/src/s2-geography/s2-geography.hpp +++ b/src/s2-geography/s2-geography.hpp @@ -5,3 +5,4 @@ #include "accessors.hpp" #include "linear-referencing.hpp" #include "distance.hpp" +#include "build.hpp" diff --git a/src/s2-options.h b/src/s2-options.h index 778628d8..7d692c04 100644 --- a/src/s2-options.h +++ b/src/s2-options.h @@ -10,6 +10,8 @@ #include "s2/s2builderutil_s2polyline_vector_layer.h" #include "s2/s2builderutil_s2point_vector_layer.h" +#include "s2-geography/s2-geography.hpp" + // This class wraps several concepts in the S2BooleanOperation, // and S2Layer, parameterized such that these can be specified from R class GeographyOperationOptions { @@ -194,6 +196,31 @@ class GeographyOperationOptions { return options; } + // options for new S2GeographyOptions API + s2geography::S2GeographyOptions geographyOptions() { + s2geography::S2GeographyOptions options; + options.boolean_operation = booleanOperationOptions(); + + LayerOptions layer_options = layerOptions(); + options.point_layer = layer_options.pointLayerOptions; + options.polyline_layer = layer_options.polylineLayerOptions; + options.polygon_layer = layer_options.polygonLayerOptions; + + if (!(layer_options.dimensions & Dimension::POINT)) { + options.point_layer_action = s2geography::S2GeographyOptions::OUTPUT_ACTION_IGNORE; + } + + if (!(layer_options.dimensions & Dimension::POLYLINE)) { + options.polyline_layer_action = s2geography::S2GeographyOptions::OUTPUT_ACTION_IGNORE; + } + + if (!(layer_options.dimensions & Dimension::POLYGON)) { + options.polygon_layer_action = s2geography::S2GeographyOptions::OUTPUT_ACTION_IGNORE; + } + + return options; + } + // build options for S2Builder S2Builder::Options builderOptions() { S2Builder::Options options; diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 9b2d10d0..34589e99 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -172,26 +172,27 @@ class BooleanOperationOp: public BinaryGeographyOperator { BooleanOperationOp(S2BooleanOperation::OpType opType, List s2options): opType(opType) { GeographyOperationOptions options(s2options); - this->options = options.booleanOperationOptions(); - this->layerOptions = options.layerOptions(); + this->geography_options = options.geographyOptions(); } SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - std::unique_ptr geography = doBooleanOperation( - feature1->ShapeIndex(), - feature2->ShapeIndex(), + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); + + std::unique_ptr geog_out = s2geography::s2_boolean_operation( + index1, index2, this->opType, - this->options, - this->layerOptions - ); + this->geography_options); + std::unique_ptr geography = MakeOldGeography(*geog_out); return Rcpp::XPtr(geography.release()); } private: S2BooleanOperation::OpType opType; - S2BooleanOperation::Options options; - GeographyOperationOptions::LayerOptions layerOptions; + s2geography::S2GeographyOptions geography_options; }; // [[Rcpp::export]] From 263d126e0cc75d4772e56fb0120d08ccc2a08d2e Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 20 Feb 2022 22:00:05 -0400 Subject: [PATCH 25/88] remove old boolean operation code --- src/s2-transformers.cpp | 77 ++++++++++++----------------------------- 1 file changed, 23 insertions(+), 54 deletions(-) diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 34589e99..035e0880 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -74,45 +74,6 @@ std::unique_ptr geographyFromLayers(std::vector points, } } -std::unique_ptr doBooleanOperation(S2ShapeIndex* index1, S2ShapeIndex* index2, - S2BooleanOperation::OpType opType, - S2BooleanOperation::Options options, - GeographyOperationOptions::LayerOptions layerOptions) { - - // create the data structures that will contain the output - std::vector points; - std::vector> polylines; - std::unique_ptr polygon = absl::make_unique(); - - s2builderutil::LayerVector layers(3); - layers[0] = absl::make_unique(&points, layerOptions.pointLayerOptions); - layers[1] = absl::make_unique(&polylines, layerOptions.polylineLayerOptions); - layers[2] = absl::make_unique(polygon.get(), layerOptions.polygonLayerOptions); - - // do the boolean operation - S2BooleanOperation booleanOp( - opType, - // normalizing the closed set here is required for line intersections - // to work as expected - s2builderutil::NormalizeClosedSet(std::move(layers)), - options - ); - - // build and check for errors - S2Error error; - if (!booleanOp.Build(*index1, *index2, &error)) { - stop(error.text()); - } - - // construct output - return geographyFromLayers( - std::move(points), - std::move(polylines), - std::move(polygon), - layerOptions.dimensions - ); -} - std::unique_ptr rebuildGeography(S2ShapeIndex* index, S2Builder::Options options, GeographyOperationOptions::LayerOptions layerOptions) { @@ -222,8 +183,9 @@ List cpp_s2_sym_difference(List geog1, List geog2, List s2options) { // [[Rcpp::export]] List cpp_s2_coverage_union_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); + s2geography::S2GeographyShapeIndex index; + std::vector> geographies; - MutableS2ShapeIndex index; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; @@ -233,19 +195,21 @@ List cpp_s2_coverage_union_agg(List geog, List s2options, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); - feature->BuildShapeIndex(&index); + auto geog = feature->NewGeography(); + index.Add(*geog); + geographies.push_back(std::move(geog)); } } - MutableS2ShapeIndex emptyIndex; - std::unique_ptr geography = doBooleanOperation( - &index, - &emptyIndex, + s2geography::S2GeographyShapeIndex emptyIndex; + std::unique_ptr geog_out = s2geography::s2_boolean_operation( + index, + emptyIndex, S2BooleanOperation::OpType::UNION, - options.booleanOperationOptions(), - options.layerOptions() + options.geographyOptions() ); + auto geography = MakeOldGeography(*geog_out); return List::create(Rcpp::XPtr(geography.release())); } @@ -553,6 +517,7 @@ List cpp_s2_unary_union(List geog, List s2options) { GeographyOperationOptions options(s2options); this->options = options.booleanOperationOptions(); this->layerOptions = options.layerOptions(); + this->geographyOptions = options.geographyOptions(); } SEXP processFeature(XPtr feature, R_xlen_t i) { @@ -570,17 +535,20 @@ List cpp_s2_unary_union(List geog, List s2options) { } if (simpleUnionOK) { - MutableS2ShapeIndex emptyIndex; + auto geog = feature->NewGeography(); + s2geography::S2GeographyShapeIndex index(*geog); + s2geography::S2GeographyShapeIndex emptyIndex; - std::unique_ptr ptr = doBooleanOperation( - feature->ShapeIndex(), - &emptyIndex, + std::unique_ptr geog_out = s2geography::s2_boolean_operation( + index, + emptyIndex, S2BooleanOperation::OpType::UNION, - this->options, - this->layerOptions + this->geographyOptions ); - return XPtr(ptr.release()); + auto geography = MakeOldGeography(*geog_out); + + return XPtr(geography.release()); } else if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POLYGON) { // If we've made it here we have an invalid polygon on our hands. A geography with // invalid loops won't work with the S2BooleanOperation we will use to accumulate @@ -647,6 +615,7 @@ List cpp_s2_unary_union(List geog, List s2options) { private: S2BooleanOperation::Options options; GeographyOperationOptions::LayerOptions layerOptions; + s2geography::S2GeographyOptions geographyOptions; }; Op op(s2options); From db1a68b4830558751a5c1287ad6e5f4a397cbc85 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 21 Feb 2022 22:10:32 -0400 Subject: [PATCH 26/88] move rebuild to new api --- src/s2-geography/build.cpp | 53 ++++++++++++++++++++++++++++++++++++++ src/s2-geography/build.hpp | 4 +++ src/s2-options.h | 1 + src/s2-transformers.cpp | 19 +++++++------- 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index 23b5de73..d15547bf 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -158,4 +158,57 @@ std::unique_ptr s2_symmetric_difference(const S2GeographyShapeIndex options); } +std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, + const S2GeographyOptions& options) { + // create the builder + S2Builder builder(options.builder); + + // create the data structures that will contain the output + std::vector points; + std::vector> polylines; + std::unique_ptr polygon = absl::make_unique(); + + // add shapes to the layer with the appropriate dimension + builder.StartLayer( + absl::make_unique(&points, options.point_layer) + ); + for (S2Shape* shape : geog.ShapeIndex()) { + if (shape->dimension() == 0) { + builder.AddShape(*shape); + } + } + + builder.StartLayer( + absl::make_unique(&polylines, options.polyline_layer) + ); + for (S2Shape* shape : geog.ShapeIndex()) { + if (shape->dimension() == 1) { + builder.AddShape(*shape); + } + } + + builder.StartLayer( + absl::make_unique(polygon.get(), options.polygon_layer) + ); + for (S2Shape* shape : geog.ShapeIndex()) { + if (shape->dimension() == 2) { + builder.AddShape(*shape); + } + } + + // build the output + S2Error error; + if (!builder.Build(&error)) { + throw S2GeographyException(error.text()); + } + + // construct output + return s2_geography_from_layers( + std::move(points), + std::move(polylines), + std::move(polygon), + options + ); +} + } diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index ede98bff..b57c17da 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -23,6 +23,7 @@ class S2GeographyOptions { polygon_layer_action(OUTPUT_ACTION_INCLUDE) {} S2BooleanOperation::Options boolean_operation; + S2Builder::Options builder; s2builderutil::S2PointVectorLayer::Options point_layer; s2builderutil::S2PolylineVectorLayer::Options polyline_layer; @@ -53,4 +54,7 @@ std::unique_ptr s2_symmetric_difference(const S2GeographyShapeIndex const S2GeographyShapeIndex& geog2, const S2GeographyOptions& options); +std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, + const S2GeographyOptions& options); + } diff --git a/src/s2-options.h b/src/s2-options.h index 7d692c04..abd282a6 100644 --- a/src/s2-options.h +++ b/src/s2-options.h @@ -200,6 +200,7 @@ class GeographyOperationOptions { s2geography::S2GeographyOptions geographyOptions() { s2geography::S2GeographyOptions options; options.boolean_operation = booleanOperationOptions(); + options.builder = builderOptions(); LayerOptions layer_options = layerOptions(); options.point_layer = layer_options.pointLayerOptions; diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 035e0880..11f46f0b 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -487,22 +487,23 @@ List cpp_s2_rebuild(List geog, List s2options) { public: Op(List s2options) { GeographyOperationOptions options(s2options); - this->options = options.builderOptions(); - this->layerOptions = options.layerOptions(); + this->options = options.geographyOptions(); } SEXP processFeature(XPtr feature, R_xlen_t i) { - std::unique_ptr ptr = rebuildGeography( - feature->ShapeIndex(), - this->options, - this->layerOptions + auto geog = feature->NewGeography(); + s2geography::S2GeographyShapeIndex index(*geog); + std::unique_ptr ptr = s2geography::s2_rebuild( + index, + this->options ); - return XPtr(ptr.release()); + + auto geography = MakeOldGeography(*ptr); + return XPtr(geography.release()); } private: - S2Builder::Options options; - GeographyOperationOptions::LayerOptions layerOptions; + s2geography::S2GeographyOptions options; }; Op op(s2options); From 48019227622c79f6e99647958fef620029a039f3 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 21 Feb 2022 22:15:12 -0400 Subject: [PATCH 27/88] move rebuild_agg to new api --- src/s2-transformers.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 11f46f0b..7a75eb59 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -303,7 +303,9 @@ List cpp_s2_centroid_agg(List geog, bool naRm) { List cpp_s2_rebuild_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); - MutableS2ShapeIndex index; + s2geography::S2GeographyShapeIndex index; + std::vector> geographies; + SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; @@ -313,16 +315,18 @@ List cpp_s2_rebuild_agg(List geog, List s2options, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); - feature->BuildShapeIndex(&index); + auto geog = feature->NewGeography(); + index.Add(*geog); + geographies.push_back(std::move(geog)); } } - std::unique_ptr geography = rebuildGeography( - &index, - options.builderOptions(), - options.layerOptions() + auto geog_out = s2geography::s2_rebuild( + index, + options.geographyOptions() ); + auto geography = MakeOldGeography(*geog_out); return List::create(Rcpp::XPtr(geography.release())); } From 1f9987307d55741e9538005dded857641c609a59 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 21 Feb 2022 22:26:34 -0400 Subject: [PATCH 28/88] remove one more reference to the old rebuildGeography --- src/s2-geography/geography.hpp | 2 +- src/s2-transformers.cpp | 66 ++++------------------------------ 2 files changed, 8 insertions(+), 60 deletions(-) diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index d0e4a372..891a239e 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -205,7 +205,7 @@ class S2GeographyShapeIndex: public S2Geography { return shape_index_; } - S2ShapeIndex& MutableShapeIndex() { + MutableS2ShapeIndex& MutableShapeIndex() { return shape_index_; } diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 7a75eb59..3271f733 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -74,59 +74,6 @@ std::unique_ptr geographyFromLayers(std::vector points, } } -std::unique_ptr rebuildGeography(S2ShapeIndex* index, - S2Builder::Options options, - GeographyOperationOptions::LayerOptions layerOptions) { - // create the builder - S2Builder builder(options); - - // create the data structures that will contain the output - std::vector points; - std::vector> polylines; - std::unique_ptr polygon = absl::make_unique(); - - // add shapes to the layer with the appropriate dimension - builder.StartLayer( - absl::make_unique(&points, layerOptions.pointLayerOptions) - ); - for (S2Shape* shape : *index) { - if (shape->dimension() == 0) { - builder.AddShape(*shape); - } - } - - builder.StartLayer( - absl::make_unique(&polylines, layerOptions.polylineLayerOptions) - ); - for (S2Shape* shape : *index) { - if (shape->dimension() == 1) { - builder.AddShape(*shape); - } - } - - builder.StartLayer( - absl::make_unique(polygon.get(), layerOptions.polygonLayerOptions) - ); - for (S2Shape* shape : *index) { - if (shape->dimension() == 2) { - builder.AddShape(*shape); - } - } - - // build the output - S2Error error; - if (!builder.Build(&error)) { - throw GeographyOperatorException(error.text()); - } - - // construct output - return geographyFromLayers( - std::move(points), - std::move(polylines), - std::move(polygon), - layerOptions.dimensions - ); -} class BooleanOperationOp: public BinaryGeographyOperator { public: @@ -259,12 +206,13 @@ List cpp_s2_union_agg(List geog, List s2options, bool naRm) { } } - std::unique_ptr geography = rebuildGeography( - accumulatedIndex.get(), - options.builderOptions(), - options.layerOptions() - ); - + s2geography::S2GeographyShapeIndex geog_index; + auto shapes = accumulatedIndex->ReleaseAll(); + for (auto& shape : shapes) { + geog_index.MutableShapeIndex().Add(std::move(shape)); + } + auto geog_out = s2geography::s2_rebuild(geog_index, options.geographyOptions()); + auto geography = MakeOldGeography(*geog_out); return List::create(Rcpp::XPtr(geography.release())); } From 04a8de39840be0499772e7a28df0b56916315f90 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 23 Feb 2022 13:44:46 -0400 Subject: [PATCH 29/88] remove a few more references --- src/s2-geography/build.cpp | 6 ----- src/s2-transformers.cpp | 47 -------------------------------------- 2 files changed, 53 deletions(-) diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index d15547bf..c5ae7460 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -11,12 +11,6 @@ namespace s2geography { -enum BuilderOutputDimension { - OUTPUT_DIMENSION_POINT = 1, - OUTPUT_DIMENSION_POLYLINE = 2, - OUTPUT_DIMENSION_POLYGON = 4 -}; - std::unique_ptr s2_geography_from_layers(std::vector points, std::vector> polylines, std::unique_ptr polygon, diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 3271f733..6a63c8d2 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -27,53 +27,6 @@ #include using namespace Rcpp; -std::unique_ptr geographyFromLayers(std::vector points, - std::vector> polylines, - std::unique_ptr polygon, - int dimensions) { - // count non-empty dimensions - bool has_polygon = (dimensions & GeographyOperationOptions::Dimension::POLYGON) && - !polygon->is_empty(); - bool has_polyline = (dimensions & GeographyOperationOptions::Dimension::POLYLINE) && - (polylines.size() > 0); - bool has_points = (dimensions & GeographyOperationOptions::Dimension::POINT) && - (points.size() > 0); - int nonEmptyDimensions = has_polygon + has_polyline + has_points; - - // return empty output - if (nonEmptyDimensions == 0) { - return absl::make_unique(); - } - - // return mixed dimension output - if (nonEmptyDimensions > 1) { - std::vector> features; - - if (has_points) { - features.push_back(absl::make_unique(std::move(points))); - } - - if (has_polyline) { - features.push_back(absl::make_unique(std::move(polylines))); - } - - if (has_polygon) { - features.push_back(absl::make_unique(std::move(polygon))); - } - - return absl::make_unique(std::move(features)); - } - - // return single dimension output - if (has_polygon) { - return absl::make_unique(std::move(polygon)); - } else if (has_polyline) { - return absl::make_unique(std::move(polylines)); - } else { - return absl::make_unique(std::move(points)); - } -} - class BooleanOperationOp: public BinaryGeographyOperator { public: From ce9e5568a76e0e4fe5f5eacc55798a62af736ec1 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 23 Feb 2022 22:16:14 -0400 Subject: [PATCH 30/88] centroid aggregator --- src/s2-geography/accessors.cpp | 21 +++++++++++++++++++++ src/s2-geography/accessors.hpp | 11 +++++++++++ src/s2-geography/aggregator.hpp | 16 ++++++++++++++++ src/s2-transformers.cpp | 14 ++++++-------- 4 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 src/s2-geography/aggregator.hpp diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 3cff694c..2a5ee62d 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -3,6 +3,7 @@ #include "geography.hpp" #include "accessors.hpp" +#include "aggregator.hpp" namespace s2geography { @@ -299,4 +300,24 @@ std::unique_ptr s2_boundary(const S2Geography& geog) { return absl::make_unique(); } + +void S2CentroidAggregator::Add(const S2Geography& geog) { + S2Point centroid = s2_centroid(geog); + if (centroid.Norm2() > 0) { + centroid_ += centroid.Normalize(); + } + } + +void S2CentroidAggregator::Merge(const S2CentroidAggregator& other) { + centroid_ += other.centroid_; +} + +S2Point S2CentroidAggregator::Finalize() { + if (centroid_.Norm2() > 0) { + return centroid_.Normalize(); + } else { + return centroid_; + } +} + } diff --git a/src/s2-geography/accessors.hpp b/src/s2-geography/accessors.hpp index caf96af6..ae73196a 100644 --- a/src/s2-geography/accessors.hpp +++ b/src/s2-geography/accessors.hpp @@ -2,6 +2,7 @@ #pragma once #include "geography.hpp" +#include "aggregator.hpp" namespace s2geography { @@ -18,4 +19,14 @@ double s2_y(const S2Geography& geog); S2Point s2_centroid(const S2Geography& geog); std::unique_ptr s2_boundary(const S2Geography& geog); +class S2CentroidAggregator: public S2Aggregator { +public: + void Add(const S2Geography& geog); + void Merge(const S2CentroidAggregator& other); + S2Point Finalize(); + +private: + S2Point centroid_; +}; + } diff --git a/src/s2-geography/aggregator.hpp b/src/s2-geography/aggregator.hpp new file mode 100644 index 00000000..c41bcfec --- /dev/null +++ b/src/s2-geography/aggregator.hpp @@ -0,0 +1,16 @@ + +#pragma once + +#include "geography.hpp" + +namespace s2geography { + +template +class S2Aggregator { +public: + virtual void Add(const S2Geography& geog, Params... parameters) = 0; + // void Merge(const S2Aggregator& other) = 0; + virtual ReturnType Finalize() = 0; +}; + +} diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 6a63c8d2..0fd50f16 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -171,7 +171,7 @@ List cpp_s2_union_agg(List geog, List s2options, bool naRm) { // [[Rcpp::export]] List cpp_s2_centroid_agg(List geog, bool naRm) { - S2Point cumCentroid; + s2geography::S2CentroidAggregator agg; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { @@ -182,19 +182,17 @@ List cpp_s2_centroid_agg(List geog, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); - auto geog = feature->NewGeography(); - S2Point centroid = s2geography::s2_centroid(*geog); - if (centroid.Norm2() > 0) { - cumCentroid += centroid.Normalize(); - } + agg.Add(*feature->NewGeography()); } } + S2Point centroid = agg.Finalize(); + List output(1); - if (cumCentroid.Norm2() == 0) { + if (centroid.Norm2() == 0) { output[0] = Rcpp::XPtr(new PointGeography()); } else { - output[0] = Rcpp::XPtr(new PointGeography(cumCentroid.Normalize())); + output[0] = Rcpp::XPtr(new PointGeography(centroid)); } return output; From f966edf607d4f69d50b2d2d1680c62c5f2977ac4 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 5 Mar 2022 13:38:02 -0400 Subject: [PATCH 31/88] prepare for more aggregators --- src/s2-geography/aggregator.hpp | 1 + src/s2-geography/build.cpp | 36 --------------------------------- src/s2-geography/build.hpp | 17 +--------------- 3 files changed, 2 insertions(+), 52 deletions(-) diff --git a/src/s2-geography/aggregator.hpp b/src/s2-geography/aggregator.hpp index c41bcfec..ab3a11ac 100644 --- a/src/s2-geography/aggregator.hpp +++ b/src/s2-geography/aggregator.hpp @@ -9,6 +9,7 @@ template class S2Aggregator { public: virtual void Add(const S2Geography& geog, Params... parameters) = 0; + virtual void FinishBatch() {} // void Merge(const S2Aggregator& other) = 0; virtual ReturnType Finalize() = 0; }; diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index c5ae7460..c410d561 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -116,42 +116,6 @@ std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& g ); } -std::unique_ptr s2_intersection(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, - const S2GeographyOptions& options) { - return s2_boolean_operation( - geog1, geog2, - S2BooleanOperation::OpType::INTERSECTION, - options); -} - -std::unique_ptr s2_union(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, - const S2GeographyOptions& options) { - return s2_boolean_operation( - geog1, geog2, - S2BooleanOperation::OpType::UNION, - options); -} - -std::unique_ptr s2_difference(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, - const S2GeographyOptions& options) { - return s2_boolean_operation( - geog1, geog2, - S2BooleanOperation::OpType::DIFFERENCE, - options); -} - -std::unique_ptr s2_symmetric_difference(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, - const S2GeographyOptions& options) { - return s2_boolean_operation( - geog1, geog2, - S2BooleanOperation::OpType::SYMMETRIC_DIFFERENCE, - options); -} - std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, const S2GeographyOptions& options) { // create the builder diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index b57c17da..3b28fe5e 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -6,6 +6,7 @@ #include "s2/s2builderutil_s2point_vector_layer.h" #include "geography.hpp" +#include "aggregator.hpp" namespace s2geography { @@ -38,22 +39,6 @@ std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& g S2BooleanOperation::OpType op_type, const S2GeographyOptions& options); -std::unique_ptr s2_intersection(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, - const S2GeographyOptions& options); - -std::unique_ptr s2_union(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, - const S2GeographyOptions& options); - -std::unique_ptr s2_difference(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, - const S2GeographyOptions& options); - -std::unique_ptr s2_symmetric_difference(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, - const S2GeographyOptions& options); - std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, const S2GeographyOptions& options); From ccc1733bcdfe95afc423235c44fccbfe3155bbd3 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 5 Mar 2022 13:41:38 -0400 Subject: [PATCH 32/88] prepare s2_geography_from_layers --- src/s2-geography/build.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index c410d561..f48bd6db 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -14,32 +14,34 @@ namespace s2geography { std::unique_ptr s2_geography_from_layers(std::vector points, std::vector> polylines, std::unique_ptr polygon, - const S2GeographyOptions& options) { + S2GeographyOptions::OutputAction point_layer_action, + S2GeographyOptions::OutputAction polyline_layer_action, + S2GeographyOptions::OutputAction polygon_layer_action) { // count non-empty dimensions bool has_polygon = !polygon->is_empty(); bool has_polylines = polylines.size() > 0; bool has_points = points.size() > 0; // use the requstested dimensions to produce the right kind of EMTPY - bool include_polygon = options.polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; - bool include_polylines = options.polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; - bool include_points = options.point_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; + bool include_polygon = polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; + bool include_polylines = polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; + bool include_points = point_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; - if (has_polygon && options.polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { + if (has_polygon && polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { throw S2GeographyException("Output contained unexpected polygon"); - } else if (has_polygon && options.polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { + } else if (has_polygon && polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { has_polygon = false; } - if (has_polylines && options.polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { + if (has_polylines && polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { throw S2GeographyException("Output contained unexpected polylines"); - } else if (has_polylines && options.polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { + } else if (has_polylines && polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { has_polylines = false; } - if (has_points && options.point_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { + if (has_points && point_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { throw S2GeographyException("Output contained unexpected points"); - } else if (has_points && options.point_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { + } else if (has_points && point_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { has_points = false; } @@ -112,7 +114,9 @@ std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& g std::move(points), std::move(polylines), std::move(polygon), - options + options.point_layer_action, + options.polyline_layer_action, + options.polygon_layer_action ); } @@ -165,7 +169,9 @@ std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, std::move(points), std::move(polylines), std::move(polygon), - options + options.point_layer_action, + options.polyline_layer_action, + options.polygon_layer_action ); } From 9becb38d171dbad9e46c74d9248cebe9d0baa207 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 5 Mar 2022 14:00:37 -0400 Subject: [PATCH 33/88] allow builder to rebuild bits --- src/s2-geography/build.cpp | 67 +++++++++++++++++++++++++++++++++++--- src/s2-geography/build.hpp | 11 ++++++- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index f48bd6db..18a9cc56 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -120,8 +120,11 @@ std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& g ); } -std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, - const S2GeographyOptions& options) { +std::unique_ptr s2_rebuild(const S2Geography& geog, + const S2GeographyOptions& options, + S2GeographyOptions::OutputAction point_layer_action, + S2GeographyOptions::OutputAction polyline_layer_action, + S2GeographyOptions::OutputAction polygon_layer_action) { // create the builder S2Builder builder(options.builder); @@ -134,7 +137,8 @@ std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, builder.StartLayer( absl::make_unique(&points, options.point_layer) ); - for (S2Shape* shape : geog.ShapeIndex()) { + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); if (shape->dimension() == 0) { builder.AddShape(*shape); } @@ -143,7 +147,8 @@ std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, builder.StartLayer( absl::make_unique(&polylines, options.polyline_layer) ); - for (S2Shape* shape : geog.ShapeIndex()) { + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); if (shape->dimension() == 1) { builder.AddShape(*shape); } @@ -152,7 +157,8 @@ std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, builder.StartLayer( absl::make_unique(polygon.get(), options.polygon_layer) ); - for (S2Shape* shape : geog.ShapeIndex()) { + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); if (shape->dimension() == 2) { builder.AddShape(*shape); } @@ -175,4 +181,55 @@ std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, ); } +std::unique_ptr s2_rebuild(const S2Geography& geog, + const S2GeographyOptions& options) { + return s2_rebuild( + geog, + options, + options.point_layer_action, + options.polyline_layer_action, + options.polygon_layer_action); +} + +std::unique_ptr s2_build_point(const S2Geography& geog, + const S2GeographyOptions& options) { + std::unique_ptr geog_out = s2_rebuild( + geog, + options, + S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE, + S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, + S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR); + + return std::unique_ptr( + dynamic_cast(geog_out.release())); +} + + +std::unique_ptr s2_build_polyline(const S2Geography& geog, + const S2GeographyOptions& options) { + std::unique_ptr geog_out = s2_rebuild( + geog, + options, + S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, + S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE, + S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR); + + return std::unique_ptr( + dynamic_cast(geog_out.release())); +} + + +std::unique_ptr s2_build_polygon(const S2Geography& geog, + const S2GeographyOptions& options) { + std::unique_ptr geog_out = s2_rebuild( + geog, + options, + S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, + S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, + S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE); + + return std::unique_ptr( + dynamic_cast(geog_out.release())); +} + } diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index 3b28fe5e..1d6fb482 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -39,7 +39,16 @@ std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& g S2BooleanOperation::OpType op_type, const S2GeographyOptions& options); -std::unique_ptr s2_rebuild(const S2GeographyShapeIndex& geog, +std::unique_ptr s2_rebuild(const S2Geography& geog, const S2GeographyOptions& options); +std::unique_ptr s2_build_point(const S2Geography& geog, + const S2GeographyOptions& options); + +std::unique_ptr s2_build_polyline(const S2Geography& geog, + const S2GeographyOptions& options); + +std::unique_ptr s2_build_polygon(const S2Geography& geog, + const S2GeographyOptions& options); + } From 4ed1fdea8bd7489d3d67fb30d1ee4b586f2fd4b1 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 5 Mar 2022 14:14:01 -0400 Subject: [PATCH 34/88] enable building polygon as a fallback --- src/s2-geography/accessors.cpp | 19 ++++++++++--------- src/s2-geography/build.cpp | 15 ++++++--------- src/s2-geography/build.hpp | 9 +++------ 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 2a5ee62d..777e6b74 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -4,6 +4,7 @@ #include "geography.hpp" #include "accessors.hpp" #include "aggregator.hpp" +#include "build.hpp" namespace s2geography { @@ -48,9 +49,8 @@ bool s2_is_collection(const S2Geography& geog) { if (polygon_geog_ptr != nullptr) { return s2_is_collection(*polygon_geog_ptr); } else { - // if custom subclasses are ever a thing, we can go through the builder - // to build a polygon - throw S2GeographyException("s2_area() not implemented for custom S2Geography()"); + std::unique_ptr built = s2_build_polygon(geog); + return s2_is_collection(*built); } } @@ -126,9 +126,8 @@ double s2_area(const S2Geography& geog) { return s2_area(*collection_geog_ptr); } - // if custom subclasses are ever a thing, we can go through the builder - // to build a polygon - throw S2GeographyException("s2_area() not implemented for custom S2Geography()"); + std::unique_ptr built = s2_build_polygon(geog); + return s2_area(*built); } double s2_length(const S2Geography& geog) { @@ -224,11 +223,13 @@ S2Point s2_centroid(const S2Geography& geog) { if (geog.dimension() == 2) { auto polygon_ptr = dynamic_cast(&geog); - if (polygon_ptr == nullptr) { - throw S2GeographyException("Can't compute s2_centroid() on custom polygon geography"); + if (polygon_ptr != nullptr) { + centroid = polygon_ptr->Polygon()->GetCentroid(); + } else { + std::unique_ptr built = s2_build_polygon(geog); + centroid = built->Polygon()->GetCentroid(); } - centroid = polygon_ptr->Polygon()->GetCentroid(); return centroid.Normalize(); } diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index 18a9cc56..5fa5b42a 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -191,11 +191,10 @@ std::unique_ptr s2_rebuild(const S2Geography& geog, options.polygon_layer_action); } -std::unique_ptr s2_build_point(const S2Geography& geog, - const S2GeographyOptions& options) { +std::unique_ptr s2_build_point(const S2Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, - options, + S2GeographyOptions(), S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE, S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR); @@ -205,11 +204,10 @@ std::unique_ptr s2_build_point(const S2Geography& geog, } -std::unique_ptr s2_build_polyline(const S2Geography& geog, - const S2GeographyOptions& options) { +std::unique_ptr s2_build_polyline(const S2Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, - options, + S2GeographyOptions(), S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE, S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR); @@ -219,11 +217,10 @@ std::unique_ptr s2_build_polyline(const S2Geography& } -std::unique_ptr s2_build_polygon(const S2Geography& geog, - const S2GeographyOptions& options) { +std::unique_ptr s2_build_polygon(const S2Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, - options, + S2GeographyOptions(), S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE); diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index 1d6fb482..fbdfa4c5 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -42,13 +42,10 @@ std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& g std::unique_ptr s2_rebuild(const S2Geography& geog, const S2GeographyOptions& options); -std::unique_ptr s2_build_point(const S2Geography& geog, - const S2GeographyOptions& options); +std::unique_ptr s2_build_point(const S2Geography& geog); -std::unique_ptr s2_build_polyline(const S2Geography& geog, - const S2GeographyOptions& options); +std::unique_ptr s2_build_polyline(const S2Geography& geog); -std::unique_ptr s2_build_polygon(const S2Geography& geog, - const S2GeographyOptions& options); +std::unique_ptr s2_build_polygon(const S2Geography& geog); } From 033b49e3f4791512436a2486125667b29f9a443d Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 5 Mar 2022 14:40:44 -0400 Subject: [PATCH 35/88] separate aggregate rebuild logic --- src/s2-geography/build.cpp | 17 +++++++++++++++++ src/s2-geography/build.hpp | 14 ++++++++++++++ src/s2-transformers.cpp | 9 +++------ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index 5fa5b42a..cfd8ce11 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -229,4 +229,21 @@ std::unique_ptr s2_build_polygon(const S2Geography& ge dynamic_cast(geog_out.release())); } + +void S2RebuildAggregator::Add(const S2Geography& geog) { + index_.Add(geog); +} + +void S2RebuildAggregator::FinishBatch() { + throw S2GeographyException("Not implemented "); +} + +void S2RebuildAggregator::Merge(const S2RebuildAggregator& other) { + throw S2GeographyException("Not implemented "); +} + +std::unique_ptr S2RebuildAggregator::Finalize() { + return s2_rebuild(index_, options_); +} + } diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index fbdfa4c5..a66cd693 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -48,4 +48,18 @@ std::unique_ptr s2_build_polyline(const S2Geography& std::unique_ptr s2_build_polygon(const S2Geography& geog); +class S2RebuildAggregator: public S2Aggregator> { +public: + S2RebuildAggregator(const S2GeographyOptions& options): options_(options) {} + + void Add(const S2Geography& geog); + void FinishBatch(); + void Merge(const S2RebuildAggregator& other); + std::unique_ptr Finalize(); + +private: + S2GeographyOptions options_; + S2GeographyShapeIndex index_; +}; + } diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 0fd50f16..5058b0a2 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -202,7 +202,7 @@ List cpp_s2_centroid_agg(List geog, bool naRm) { List cpp_s2_rebuild_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); - s2geography::S2GeographyShapeIndex index; + s2geography::S2RebuildAggregator agg(options.geographyOptions()); std::vector> geographies; SEXP item; @@ -215,15 +215,12 @@ List cpp_s2_rebuild_agg(List geog, List s2options, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); auto geog = feature->NewGeography(); - index.Add(*geog); + agg.Add(*geog); geographies.push_back(std::move(geog)); } } - auto geog_out = s2geography::s2_rebuild( - index, - options.geographyOptions() - ); + auto geog_out = agg.Finalize(); auto geography = MakeOldGeography(*geog_out); return List::create(Rcpp::XPtr(geography.release())); From 2d63704089405c28f08cd2730e7cc2b336cd072c Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 5 Mar 2022 14:47:15 -0400 Subject: [PATCH 36/88] coverage union agg logic --- src/s2-geography/build.cpp | 17 +++++++++++++++++ src/s2-geography/build.hpp | 14 ++++++++++++++ src/s2-transformers.cpp | 12 +++--------- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index cfd8ce11..a1b7bc67 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -246,4 +246,21 @@ std::unique_ptr S2RebuildAggregator::Finalize() { return s2_rebuild(index_, options_); } +void S2CoverageUnionAggregator::Add(const S2Geography& geog) { + index_.Add(geog); +} + +void S2CoverageUnionAggregator::FinishBatch() { + throw S2GeographyException("Not implemented "); +} + +void S2CoverageUnionAggregator::Merge(const S2CoverageUnionAggregator& other) { + throw S2GeographyException("Not implemented "); +} + +std::unique_ptr S2CoverageUnionAggregator::Finalize() { + S2GeographyShapeIndex empty_index_; + return s2_boolean_operation(index_, empty_index_, S2BooleanOperation::OpType::UNION, options_); +} + } diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index a66cd693..a4b26ff1 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -62,4 +62,18 @@ class S2RebuildAggregator: public S2Aggregator> { S2GeographyShapeIndex index_; }; +class S2CoverageUnionAggregator: public S2Aggregator> { +public: + S2CoverageUnionAggregator(const S2GeographyOptions& options): options_(options) {} + + void Add(const S2Geography& geog); + void FinishBatch(); + void Merge(const S2CoverageUnionAggregator& other); + std::unique_ptr Finalize(); + +private: + S2GeographyOptions options_; + S2GeographyShapeIndex index_; +}; + } diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 5058b0a2..df45bde2 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -83,7 +83,7 @@ List cpp_s2_sym_difference(List geog1, List geog2, List s2options) { // [[Rcpp::export]] List cpp_s2_coverage_union_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); - s2geography::S2GeographyShapeIndex index; + s2geography::S2CoverageUnionAggregator agg(options.geographyOptions()); std::vector> geographies; SEXP item; @@ -96,18 +96,12 @@ List cpp_s2_coverage_union_agg(List geog, List s2options, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); auto geog = feature->NewGeography(); - index.Add(*geog); + agg.Add(*geog); geographies.push_back(std::move(geog)); } } - s2geography::S2GeographyShapeIndex emptyIndex; - std::unique_ptr geog_out = s2geography::s2_boolean_operation( - index, - emptyIndex, - S2BooleanOperation::OpType::UNION, - options.geographyOptions() - ); + std::unique_ptr geog_out = agg.Finalize(); auto geography = MakeOldGeography(*geog_out); return List::create(Rcpp::XPtr(geography.release())); From 26d34aa89ec03fd605b335737b1b395248899c19 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 5 Mar 2022 22:09:16 -0400 Subject: [PATCH 37/88] sketch union_agg solution --- src/s2-geography/aggregator.hpp | 2 - src/s2-geography/build.cpp | 69 ++++++++++++++++++++++++++------- src/s2-geography/build.hpp | 25 +++++++++--- 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/src/s2-geography/aggregator.hpp b/src/s2-geography/aggregator.hpp index ab3a11ac..8062f9ec 100644 --- a/src/s2-geography/aggregator.hpp +++ b/src/s2-geography/aggregator.hpp @@ -9,8 +9,6 @@ template class S2Aggregator { public: virtual void Add(const S2Geography& geog, Params... parameters) = 0; - virtual void FinishBatch() {} - // void Merge(const S2Aggregator& other) = 0; virtual ReturnType Finalize() = 0; }; diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index a1b7bc67..e52f43d2 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -234,14 +234,6 @@ void S2RebuildAggregator::Add(const S2Geography& geog) { index_.Add(geog); } -void S2RebuildAggregator::FinishBatch() { - throw S2GeographyException("Not implemented "); -} - -void S2RebuildAggregator::Merge(const S2RebuildAggregator& other) { - throw S2GeographyException("Not implemented "); -} - std::unique_ptr S2RebuildAggregator::Finalize() { return s2_rebuild(index_, options_); } @@ -250,17 +242,64 @@ void S2CoverageUnionAggregator::Add(const S2Geography& geog) { index_.Add(geog); } -void S2CoverageUnionAggregator::FinishBatch() { - throw S2GeographyException("Not implemented "); +std::unique_ptr S2CoverageUnionAggregator::Finalize() { + S2GeographyShapeIndex empty_index_; + return s2_boolean_operation(index_, empty_index_, S2BooleanOperation::OpType::UNION, options_); } -void S2CoverageUnionAggregator::Merge(const S2CoverageUnionAggregator& other) { - throw S2GeographyException("Not implemented "); +void S2UnionAggregator::Add(const S2Geography& geog) { + if (geog.dimension() == 0 || geog.dimension() == 1) { + root_.index1.Add(geog); + return; + } + + if (other_.size() == 0) { + other_.push_back(absl::make_unique()); + other_.back()->index1.Add(geog); + return; + } + + Node* last = other_.back().get(); + if (last->index1.num_shapes() == 0) { + last->index1.Add(geog); + } else if (last->index2.num_shapes() == 0) { + last->index2.Add(geog); + } else { + other_.push_back(absl::make_unique()); + other_.back()->index1.Add(geog); + } } -std::unique_ptr S2CoverageUnionAggregator::Finalize() { - S2GeographyShapeIndex empty_index_; - return s2_boolean_operation(index_, empty_index_, S2BooleanOperation::OpType::UNION, options_); +std::unique_ptr S2UnionAggregator::Node::Merge(const S2GeographyOptions& options) { + return s2_boolean_operation( + index1, + index2, + S2BooleanOperation::OpType::UNION, + options); +} + +std::unique_ptr S2UnionAggregator::Finalize() { + while (other_.size() > 1) { + for (size_t i = other_.size() - 1; i >= 1; i = i - 2) { + // merge other_[i] with other_[i - 1] + std::unique_ptr merged = other_[i]->Merge(options_); + std::unique_ptr merged_prev = other_[i - 1]->Merge(options_); + other_.erase(other_.begin() + i - 1, other_.begin() + i); + other_.push_back(absl::make_unique()); + other_.back()->index1.Add(*merged); + other_.back()->index2.Add(*merged_prev); + other_.back()->data.push_back(std::move(merged)); + other_.back()->data.push_back(std::move(merged_prev)); + } + } + + if (other_.size() == 0) { + return root_.Merge(options_); + } else { + std::unique_ptr merged = other_[0]->Merge(options_); + root_.index2.Add(*merged); + return root_.Merge(options_); + } } } diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index a4b26ff1..a81d0853 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -51,10 +51,7 @@ std::unique_ptr s2_build_polygon(const S2Geography& ge class S2RebuildAggregator: public S2Aggregator> { public: S2RebuildAggregator(const S2GeographyOptions& options): options_(options) {} - void Add(const S2Geography& geog); - void FinishBatch(); - void Merge(const S2RebuildAggregator& other); std::unique_ptr Finalize(); private: @@ -67,8 +64,6 @@ class S2CoverageUnionAggregator: public S2Aggregator Finalize(); private: @@ -76,4 +71,24 @@ class S2CoverageUnionAggregator: public S2Aggregator> { +public: + S2UnionAggregator(const S2GeographyOptions& options): options_(options) {} + void Add(const S2Geography& geog); + std::unique_ptr Finalize(); + +private: + class Node { + public: + S2GeographyShapeIndex index1; + S2GeographyShapeIndex index2; + std::vector> data; + std::unique_ptr Merge(const S2GeographyOptions& options); + }; + + S2GeographyOptions options_; + Node root_; + std::vector> other_; +}; + } From ee63077f6de765d0eec69e64216a3a200f42f5f1 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 5 Mar 2022 22:35:15 -0400 Subject: [PATCH 38/88] use and test new aggregator --- src/s2-geography/build.cpp | 16 ++++++++-- src/s2-transformers.cpp | 43 +++++---------------------- tests/testthat/test-s2-transformers.R | 25 ++++++++++++++-- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index e52f43d2..ae4de1fe 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -279,15 +279,25 @@ std::unique_ptr S2UnionAggregator::Node::Merge(const S2GeographyOpt } std::unique_ptr S2UnionAggregator::Finalize() { - while (other_.size() > 1) { - for (size_t i = other_.size() - 1; i >= 1; i = i - 2) { + for (int j = 0; j < 100; j++) { + if (other_.size() <= 1) { + break; + } + + for (int64_t i = static_cast(other_.size()) - 1; i >= 1; i = i - 2) { // merge other_[i] with other_[i - 1] std::unique_ptr merged = other_[i]->Merge(options_); std::unique_ptr merged_prev = other_[i - 1]->Merge(options_); - other_.erase(other_.begin() + i - 1, other_.begin() + i); + + // erase the last two nodes + other_.erase(other_.begin() + i - 1, other_.begin() + i + 1); + + // ..and replace it with a single node other_.push_back(absl::make_unique()); other_.back()->index1.Add(*merged); other_.back()->index2.Add(*merged_prev); + + // making sure to keep the underlying data alive other_.back()->data.push_back(std::move(merged)); other_.back()->data.push_back(std::move(merged_prev)); } diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index df45bde2..16ba3e42 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -107,20 +107,11 @@ List cpp_s2_coverage_union_agg(List geog, List s2options, bool naRm) { return List::create(Rcpp::XPtr(geography.release())); } -// This approach to aggregation is slow but accurate. There is probably a more efficient way -// to accumulate geometries and/or re-use the layers vector but thus far I haven't figured -// out a way to make that work. // [[Rcpp::export]] List cpp_s2_union_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); - GeographyOperationOptions::LayerOptions layerOptions = options.layerOptions(); - S2BooleanOperation::Options unionOptions = options.booleanOperationOptions(); - S2Builder::Options buillderOptions = options.builderOptions(); - - // using smart pointers here so that we can use swap() to - // use replace accumulatedIndex with index after each union - std::unique_ptr index = absl::make_unique(); - std::unique_ptr accumulatedIndex = absl::make_unique(); + s2geography::S2UnionAggregator agg(options.geographyOptions()); + std::vector> geographies; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { @@ -131,34 +122,14 @@ List cpp_s2_union_agg(List geog, List s2options, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); - - index->Clear(); - s2builderutil::LayerVector layers(3); - layers[0] = absl::make_unique(index.get(), layerOptions.pointLayerOptions); - layers[1] = absl::make_unique(index.get(), layerOptions.polylineLayerOptions); - layers[2] = absl::make_unique(index.get(), layerOptions.polygonLayerOptions); - - S2BooleanOperation booleanOp( - S2BooleanOperation::OpType::UNION, - s2builderutil::NormalizeClosedSet(std::move(layers)), - unionOptions - ); - - S2Error error; - if (!booleanOp.Build(*accumulatedIndex, *(feature->ShapeIndex()), &error)) { - stop(error.text()); - } - - accumulatedIndex.swap(index); + auto geog = feature->NewGeography(); + agg.Add(*geog); + geographies.push_back(std::move(geog)); } } - s2geography::S2GeographyShapeIndex geog_index; - auto shapes = accumulatedIndex->ReleaseAll(); - for (auto& shape : shapes) { - geog_index.MutableShapeIndex().Add(std::move(shape)); - } - auto geog_out = s2geography::s2_rebuild(geog_index, options.geographyOptions()); + std::unique_ptr geog_out = agg.Finalize(); + auto geography = MakeOldGeography(*geog_out); return List::create(Rcpp::XPtr(geography.release())); } diff --git a/tests/testthat/test-s2-transformers.R b/tests/testthat/test-s2-transformers.R index 2fa99f45..8d6bdab2 100644 --- a/tests/testthat/test-s2-transformers.R +++ b/tests/testthat/test-s2-transformers.R @@ -388,17 +388,36 @@ test_that("s2_union_agg() works", { # NULL handling expect_identical( - s2_coverage_union_agg(c("POINT (30 10)", NA), na.rm = FALSE), + s2_union_agg(c("POINT (30 10)", NA), na.rm = FALSE), as_s2_geography(NA_character_) ) expect_wkt_equal( - s2_coverage_union_agg(character()), + s2_union_agg(character()), as_s2_geography("GEOMETRYCOLLECTION EMPTY") ) expect_wkt_equal( - s2_coverage_union_agg(c("POINT (30 10)", NA), na.rm = TRUE), + s2_union_agg(c("POINT (30 10)", NA), na.rm = TRUE), "POINT (30 10)" ) + + # make sure this works on polygons since they are handled differently than + # points and linestrings + expect_equal( + s2_area(s2_union_agg(s2_data_countries())), + sum(s2_area(s2_union_agg(s2_data_countries()))) + ) + + # check non-polygons and polygons together + points_and_poly <- s2_union_agg( + c( + s2_data_countries(), + s2_data_cities() + ) + ) + + points <- s2_rebuild(points_and_poly, options = s2_options(dimensions = "point")) + poly <- s2_rebuild(points_and_poly, options = s2_options(dimensions = "polygon")) + expect_false(any(s2_intersects(points, poly))) }) test_that("s2_rebuild_agg() works", { From 5f5add8e0ac3f113ead6e746db363423b23760ff Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 6 Mar 2022 14:57:45 -0400 Subject: [PATCH 39/88] migrate s2_point_on_surface to new api --- src/Makevars.in | 1 + src/Makevars.win | 1 + src/s2-geography/coverings.cpp | 59 +++++++++++++++++++++++++++++++ src/s2-geography/coverings.hpp | 12 +++++++ src/s2-geography/s2-geography.hpp | 1 + src/s2-transformers.cpp | 46 +++--------------------- 6 files changed, 79 insertions(+), 41 deletions(-) create mode 100644 src/s2-geography/coverings.cpp create mode 100644 src/s2-geography/coverings.hpp diff --git a/src/Makevars.in b/src/Makevars.in index 2235d25b..0550716a 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -109,6 +109,7 @@ OBJECTS = $(ABSL_LIBS) \ s2-geography/linear-referencing.o \ s2-geography/distance.o \ s2-geography/build.o \ + s2-geography/coverings.o \ s2-geography/geography.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ diff --git a/src/Makevars.win b/src/Makevars.win index fe785a86..5d883dbe 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -94,6 +94,7 @@ S2LIBS = $(ABSL_LIBS) \ s2-geography/distance.o \ s2-geography/accessors.o \ s2-geography/build.o \ + s2-geography/coverings.o \ s2-geography/geography.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ diff --git a/src/s2-geography/coverings.cpp b/src/s2-geography/coverings.cpp new file mode 100644 index 00000000..62b923a5 --- /dev/null +++ b/src/s2-geography/coverings.cpp @@ -0,0 +1,59 @@ + +#include "s2/s2region_coverer.h" + +#include "geography.hpp" +#include "coverings.hpp" +#include "accessors.hpp" + +namespace s2geography { + +S2Point s2_point_on_surface(const S2Geography& geog, S2RegionCoverer& coverer) { + if (s2_is_empty(geog)) { + return S2Point(); + } + + int dimension = s2_dimension(geog); + if (dimension == 2) { + std::unique_ptr region = geog.Region(); + S2CellUnion covering = coverer.GetInteriorCovering(*region); + + // Take center of cell with smallest level (biggest) + int min_level = 31; + S2Point pt; + for (const S2CellId& id : covering) { + if (id.level() < min_level) { + // Already normalized + pt = id.ToPoint(); + min_level = id.level(); + } + } + + return pt; + } + + if (dimension == 0) { + // For point, return point closest to centroid + S2Point centroid = s2_centroid(geog); + + S1Angle nearest_dist = S1Angle::Infinity(); + S1Angle dist; + S2Point closest_pt; + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); + for (int j = 0; j < shape->num_edges(); j++) { + S2Shape::Edge e = shape->edge(j); + dist = S1Angle(e.v0, centroid); + if (dist < nearest_dist) { + nearest_dist = dist; + closest_pt = e.v0; + } + } + } + + return closest_pt; + } + + throw S2GeographyException("s2_point_on_surface() not implemented for polyline"); +} + +} diff --git a/src/s2-geography/coverings.hpp b/src/s2-geography/coverings.hpp new file mode 100644 index 00000000..69473986 --- /dev/null +++ b/src/s2-geography/coverings.hpp @@ -0,0 +1,12 @@ + +#pragma once + +#include "s2/s2region_coverer.h" + +#include "geography.hpp" + +namespace s2geography { + +S2Point s2_point_on_surface(const S2Geography& geog, S2RegionCoverer& coverer); + +} diff --git a/src/s2-geography/s2-geography.hpp b/src/s2-geography/s2-geography.hpp index c9be9dda..d9ea618b 100644 --- a/src/s2-geography/s2-geography.hpp +++ b/src/s2-geography/s2-geography.hpp @@ -6,3 +6,4 @@ #include "linear-referencing.hpp" #include "distance.hpp" #include "build.hpp" +#include "coverings.hpp" diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 16ba3e42..b3dededd 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -281,48 +281,12 @@ List cpp_s2_point_on_surface(List geog) { S2RegionCoverer coverer; SEXP processFeature(XPtr feature, R_xlen_t i) { - if (feature->IsEmpty()) { + auto geog = feature->NewGeography(); + S2Point result = s2geography::s2_point_on_surface(*geog, coverer); + if (result.Norm2() == 0) { return XPtr(new PointGeography()); - } - else if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POLYGON) { - // Create Interior Covering of Polygon - S2CellUnion cellUnion; - cellUnion = coverer.GetInteriorCovering(*feature->Polygon()); - - // Take center of cell with smallest level (biggest) - int min_level = 31; - S2Point pt; - for (const S2CellId& id : cellUnion) { - if (id.level() < min_level) { - // Already normalized - // https://github.com/r-spatial/s2/blob/a323a74774d0526f1165e334a3d76a9da38316b9/src/s2/s2cell_id.h#L128 - pt = id.ToPoint(); - min_level = id.level(); - } - } - - return XPtr(new PointGeography(pt)); - } - // For point, return point closest to centroid - else if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POINT) { - S2Point centroid = feature->Centroid(); - const std::vector* pts = feature->Point(); - - S1Angle nearest_dist = S1Angle::Infinity(); - S1Angle dist; - S2Point closest_pt; - for (const S2Point& pt : *pts) { - dist = S1Angle(pt, centroid); - if (dist < nearest_dist) { - nearest_dist = dist; - closest_pt = pt; - } - } - - return XPtr(new PointGeography(closest_pt)); - } - else { - stop("POLYLINE/COLLECTION type not supported in s2_point_on_surface()"); + } else { + return XPtr(new PointGeography(result)); } } }; From 3906ba6444602a8eac34d16184fa85f229f7872f Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Tue, 8 Mar 2022 22:03:42 -0400 Subject: [PATCH 40/88] move s2_find_validation_error() to new API --- src/s2-accessors.cpp | 6 ++- src/s2-geography/accessors.cpp | 76 ++++++++++++++++++++++++++++++ src/s2-geography/accessors.hpp | 1 + src/s2-geography/geography.hpp | 2 +- tests/testthat/test-s2-accessors.R | 4 +- 5 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/s2-accessors.cpp b/src/s2-accessors.cpp index 8007d4c9..e847f664 100644 --- a/src/s2-accessors.cpp +++ b/src/s2-accessors.cpp @@ -22,7 +22,8 @@ LogicalVector cpp_s2_is_collection(List geog) { LogicalVector cpp_s2_is_valid(List geog) { class Op: public UnaryGeographyOperator { int processFeature(XPtr feature, R_xlen_t i) { - return !(feature->FindValidationError(&(this->error))); + auto geog = feature->NewGeography(); + return !s2geography::s2_find_validation_error(*geog, &error); } S2Error error; @@ -36,7 +37,8 @@ LogicalVector cpp_s2_is_valid(List geog) { CharacterVector cpp_s2_is_valid_reason(List geog) { class Op: public UnaryGeographyOperator { String processFeature(XPtr feature, R_xlen_t i) { - if (feature->FindValidationError(&(this->error))) { + auto geog = feature->NewGeography(); + if (s2geography::s2_find_validation_error(*geog, &error)) { return this->error.text(); } else { return NA_STRING; diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 777e6b74..1b9fd989 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -194,6 +194,82 @@ double s2_y(const S2Geography& geog) { return out; } +bool s2_find_validation_error(const S2GeographyOwningPolyline& geog, S2Error* error) { + for (const auto& polyline: geog.Polylines()) { + if (polyline->FindValidationError(error)) { + return true; + } + } + + return false; +} + +bool s2_find_validation_error(const S2GeographyOwningPolygon& geog, S2Error* error) { + return geog.Polygon()->FindValidationError(error); +} + +bool s2_find_validation_error(const S2GeographyCollection& geog, S2Error* error) { + for (const auto& feature: geog.Features()) { + if (s2_find_validation_error(*feature, error)) { + return true; + } + } + + return false; +} + +bool s2_find_validation_error(const S2Geography& geog, S2Error* error) { + if (geog.dimension() == 0) { + error->Clear(); + return false; + } + + if (geog.dimension() == 1) { + auto poly_ptr = dynamic_cast(&geog); + if (poly_ptr != nullptr) { + return s2_find_validation_error(*poly_ptr, error); + } else { + try { + auto poly = s2_build_polyline(geog); + return s2_find_validation_error(*poly, error); + } catch (S2GeographyException& e) { + error->Init(S2Error::INTERNAL, "%s", e.what()); + return true; + } + } + } + + if (geog.dimension() == 2) { + auto poly_ptr = dynamic_cast(&geog); + if (poly_ptr != nullptr) { + return s2_find_validation_error(*poly_ptr, error); + } else { + try { + auto poly = s2_build_polygon(geog); + return s2_find_validation_error(*poly, error); + } catch (S2GeographyException& e) { + error->Init(S2Error::INTERNAL, "%s", e.what()); + return true; + } + } + } + + auto collection_ptr = dynamic_cast(&geog); + if (collection_ptr != nullptr) { + return s2_find_validation_error(*collection_ptr, error); + } else { + try { + auto collection = s2_build_polygon(geog); + return s2_find_validation_error(*collection, error); + } catch (S2GeographyException& e) { + error->Init(S2Error::INTERNAL, "%s", e.what()); + return true; + } + } + + throw S2GeographyException("s2_find_validation() error not implemented for this geography type"); +} + S2Point s2_centroid(const S2Geography& geog) { S2Point centroid(0, 0, 0); diff --git a/src/s2-geography/accessors.hpp b/src/s2-geography/accessors.hpp index ae73196a..fee4270e 100644 --- a/src/s2-geography/accessors.hpp +++ b/src/s2-geography/accessors.hpp @@ -15,6 +15,7 @@ double s2_length(const S2Geography& geog); double s2_perimeter(const S2Geography& geog); double s2_x(const S2Geography& geog); double s2_y(const S2Geography& geog); +bool s2_find_validation_error(const S2Geography& geog, S2Error* error); S2Point s2_centroid(const S2Geography& geog); std::unique_ptr s2_boundary(const S2Geography& geog); diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index 891a239e..36374939 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -36,7 +36,7 @@ class S2Geography { } int dim = Shape(0)->dimension(); - for (int i = 2; i < num_shapes(); i++) { + for (int i = 1; i < num_shapes(); i++) { if (dim != Shape(i)->dimension()) { return -1; } diff --git a/tests/testthat/test-s2-accessors.R b/tests/testthat/test-s2-accessors.R index fa5c6106..cac91f00 100644 --- a/tests/testthat/test-s2-accessors.R +++ b/tests/testthat/test-s2-accessors.R @@ -29,7 +29,7 @@ test_that("s2_is_valid() works", { # invalid "LINESTRING (0 0, 0 0, 1 1)", "POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0))", - "GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0)))", + "GEOMETRYCOLLECTION (POINT (0 1), POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0)))", NA ) ), @@ -50,7 +50,7 @@ test_that("s2_is_valid_detail() works", { # invalid "LINESTRING (0 0, 0 0, 1 1)", "POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0))", - "GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0)))", + "GEOMETRYCOLLECTION (POINT (0 1), POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0)))", NA ) ), From f36e518557ef6da338393e3b05f386f84e5816f4 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Tue, 8 Mar 2022 22:52:30 -0400 Subject: [PATCH 41/88] move unary union to new API --- src/s2-geography/build.cpp | 94 ++++++++++++++++++++++++++ src/s2-geography/build.hpp | 3 + src/s2-transformers.cpp | 97 ++------------------------- tests/testthat/test-s2-transformers.R | 2 +- 4 files changed, 104 insertions(+), 92 deletions(-) diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index ae4de1fe..732ccd7e 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -8,6 +8,7 @@ #include "geography.hpp" #include "build.hpp" +#include "accessors.hpp" namespace s2geography { @@ -120,6 +121,99 @@ std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& g ); } +std::unique_ptr s2_unary_union(const S2GeographyOwningPolygon& geog, + const S2GeographyOptions& options) { + // A geography with invalid loops won't work with the S2BooleanOperation + // we will use to accumulate (i.e., union) valid polygons, + // so we need to rebuild each loop as its own polygon, + // splitting crossed edges along the way. + + // Not exposing these options as an argument (except snap function) + // because a particular combiation of them is required for this to work + S2Builder::Options builder_options; + builder_options.set_split_crossing_edges(true); + builder_options.set_snap_function(options.boolean_operation.snap_function()); + s2builderutil::S2PolygonLayer::Options layer_options; + layer_options.set_edge_type(S2Builder::EdgeType::UNDIRECTED); + layer_options.set_validate(false); + + // Rebuild all loops as polygons using the S2Builder() + std::vector> loops; + for (int i = 0; i < geog.Polygon()->num_loops(); i++) { + std::unique_ptr loop = absl::make_unique(); + S2Builder builder(builder_options); + builder.StartLayer( + absl::make_unique(loop.get(), layer_options)); + builder.AddShape(S2Loop::Shape(geog.Polygon()->loop(i))); + S2Error error; + if (!builder.Build(&error)) { + throw S2GeographyException(error.text()); + } + + // Check if the builder created a polygon whose boundary contained more than + // half the earth (and invert it if so) + if (loop->GetArea() > (2 * M_PI)) { + loop->Invert(); + } + + loops.push_back(std::move(loop)); + } + + // Accumulate the union of outer loops (but difference of inner loops) + std::unique_ptr accumulated_polygon = absl::make_unique(); + for (int i = 0; i < geog.Polygon()->num_loops(); i++) { + std::unique_ptr polygon_result = absl::make_unique(); + + // Use original nesting to suggest if this loop should be unioned or diffed. + // For valid polygons loops are arranged such that the biggest loop is on the outside + // followed by holes such that the below strategy should work (since we are + // just iterating along the original loop structure) + if ((geog.Polygon()->loop(i)->depth() % 2) == 0) { + polygon_result->InitToUnion(accumulated_polygon.get(), loops[i].get()); + } else { + polygon_result->InitToDifference(accumulated_polygon.get(), loops[i].get()); + } + + accumulated_polygon.swap(polygon_result); + } + + return absl::make_unique(std::move(accumulated_polygon)); +} + +std::unique_ptr s2_unary_union(const S2GeographyShapeIndex& geog, + const S2GeographyOptions& options) { + // complex union only needed when a polygon is involved + bool simple_union_ok = s2_is_empty(geog) || s2_dimension(geog) < 2; + + // valid polygons that are not part of a collection can also use a + // simple union (common) + if (geog.dimension() == 2) { + S2Error validation_error; + if (!s2_find_validation_error(geog, &validation_error)) { + simple_union_ok = true; + } + } + + if (simple_union_ok) { + S2GeographyShapeIndex empty; + return s2_boolean_operation(geog, empty, S2BooleanOperation::OpType::UNION, options); + } + + if (geog.dimension() == 2) { + // If we've made it here we have an invalid polygon on our hands. + auto poly_ptr = dynamic_cast(&geog); + if (poly_ptr != nullptr) { + return s2_unary_union(*poly_ptr, options); + } else { + auto poly = s2_build_polygon(geog); + return s2_unary_union(*poly, options); + } + } + + throw S2GeographyException( + "s2_unary_union() for multidimensional collections not implemented"); +} + std::unique_ptr s2_rebuild(const S2Geography& geog, const S2GeographyOptions& options, S2GeographyOptions::OutputAction point_layer_action, diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index a81d0853..6d1a5aa8 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -39,6 +39,9 @@ std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& g S2BooleanOperation::OpType op_type, const S2GeographyOptions& options); +std::unique_ptr s2_unary_union(const S2GeographyShapeIndex& geog, + const S2GeographyOptions& options); + std::unique_ptr s2_rebuild(const S2Geography& geog, const S2GeographyOptions& options); diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index b3dededd..2719aa00 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -345,101 +345,16 @@ List cpp_s2_unary_union(List geog, List s2options) { public: Op(List s2options) { GeographyOperationOptions options(s2options); - this->options = options.booleanOperationOptions(); - this->layerOptions = options.layerOptions(); this->geographyOptions = options.geographyOptions(); } SEXP processFeature(XPtr feature, R_xlen_t i) { - // complex union only needed when a polygon is involved - bool simpleUnionOK = feature->IsEmpty() || - (feature->Dimension() < 2); - - // valid polygons that are not part of a collection can also use a - // simple union (common) - if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POLYGON) { - S2Error validationError; - if(!(feature->Polygon()->FindValidationError(&validationError))) { - simpleUnionOK = true; - } - } - - if (simpleUnionOK) { - auto geog = feature->NewGeography(); - s2geography::S2GeographyShapeIndex index(*geog); - s2geography::S2GeographyShapeIndex emptyIndex; - - std::unique_ptr geog_out = s2geography::s2_boolean_operation( - index, - emptyIndex, - S2BooleanOperation::OpType::UNION, - this->geographyOptions - ); - - auto geography = MakeOldGeography(*geog_out); - - return XPtr(geography.release()); - } else if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POLYGON) { - // If we've made it here we have an invalid polygon on our hands. A geography with - // invalid loops won't work with the S2BooleanOperation we will use to accumulate - // (i.e., union) valid polygons, so we need to rebuild each loop as its own polygon, - // splitting crossed edges along the way. - const S2Polygon* originalPoly = feature->Polygon(); - - // Not exposing these options as an argument (except snap function) - // because a particular combiation of them is required for this to work - S2Builder::Options builderOptions; - builderOptions.set_split_crossing_edges(true); - builderOptions.set_snap_function(this->options.snap_function()); - s2builderutil::S2PolygonLayer::Options layerOptions; - layerOptions.set_edge_type(S2Builder::EdgeType::UNDIRECTED); - layerOptions.set_validate(false); - - // Rebuild all loops as polygons using the S2Builder() - std::vector> loops; - for (int i = 0; i < originalPoly->num_loops(); i++) { - std::unique_ptr loop = absl::make_unique(); - S2Builder builder(builderOptions); - builder.StartLayer(absl::make_unique(loop.get())); - builder.AddShape(S2Loop::Shape(originalPoly->loop(i))); - S2Error error; - if (!builder.Build(&error)) { - throw GeographyOperatorException(error.text()); - } - - // Check if the builder created a polygon whose boundary contained more than - // half the earth (and invert it if so) - if (loop->GetArea() > (2 * M_PI)) { - loop->Invert(); - } - - loops.push_back(std::move(loop)); - } - - // Accumulate the union of outer loops (but difference of inner loops) - std::unique_ptr accumulatedPolygon = absl::make_unique(); - for (int i = 0; i < originalPoly->num_loops(); i++) { - std::unique_ptr polygonResult = absl::make_unique(); - - // Use original nesting to suggest if this loop should be unioned or diffed. - // For valid polygons loops are arranged such that the biggest loop is on the outside - // followed by holes such that the below strategy should work (since we are - // just iterating along the original loop structure) - if ((originalPoly->loop(i)->depth() % 2) == 0) { - polygonResult->InitToUnion(accumulatedPolygon.get(), loops[i].get()); - } else { - polygonResult->InitToDifference(accumulatedPolygon.get(), loops[i].get()); - } - - accumulatedPolygon.swap(polygonResult); - } - - return XPtr(new PolygonGeography(std::move(accumulatedPolygon))); - } else { - // This is a less common case (mixed dimension output that includes a polygon). - // In the absence of a clean solution, saving this battle for another day. - throw GeographyOperatorException("Unary union for collections is not implemented"); - } + auto geog = feature->NewGeography(); + s2geography::S2GeographyShapeIndex index(*geog); + std::unique_ptr geog_out = + s2geography::s2_unary_union(index, this->geographyOptions); + auto geography = MakeOldGeography(*geog_out); + return XPtr(geography.release()); } private: diff --git a/tests/testthat/test-s2-transformers.R b/tests/testthat/test-s2-transformers.R index 8d6bdab2..25a1ae0d 100644 --- a/tests/testthat/test-s2-transformers.R +++ b/tests/testthat/test-s2-transformers.R @@ -279,7 +279,7 @@ test_that("s2_union(x) errors for the case of mixed dimension collections", { s2_union( c("GEOMETRYCOLLECTION(POLYGON ((-10 -10, -10 10, 10 10, 10 -10, -10 -10)), LINESTRING (0 -20, 0 20))") ), - "Unary union for collections is not implemented" + "for multidimensional collections not implemented" ) }) From eea70c30b55d80f26bba423254b36c02c28de09b Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 10 Mar 2022 22:28:34 -0400 Subject: [PATCH 42/88] shuffle accessors that return a geography to their own file --- src/Makevars.in | 1 + src/Makevars.win | 1 + src/s2-geography/accessors-geog.cpp | 137 ++++++++++++++++++++++++++++ src/s2-geography/accessors-geog.hpp | 22 +++++ src/s2-geography/accessors.cpp | 129 -------------------------- src/s2-geography/accessors.hpp | 14 --- src/s2-geography/coverings.cpp | 1 + src/s2-geography/s2-geography.hpp | 1 + 8 files changed, 163 insertions(+), 143 deletions(-) create mode 100644 src/s2-geography/accessors-geog.cpp create mode 100644 src/s2-geography/accessors-geog.hpp diff --git a/src/Makevars.in b/src/Makevars.in index 0550716a..c7602298 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -106,6 +106,7 @@ OBJECTS = $(ABSL_LIBS) \ s2-geography/capi/tessellator.o \ s2-geography/capi/status.o \ s2-geography/accessors.o \ + s2-geography/accessors-geog.o \ s2-geography/linear-referencing.o \ s2-geography/distance.o \ s2-geography/build.o \ diff --git a/src/Makevars.win b/src/Makevars.win index 5d883dbe..50962fe4 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -93,6 +93,7 @@ S2LIBS = $(ABSL_LIBS) \ s2-geography/linear-referencing.o \ s2-geography/distance.o \ s2-geography/accessors.o \ + s2-geography/accessors-geog.o \ s2-geography/build.o \ s2-geography/coverings.o \ s2-geography/geography.o \ diff --git a/src/s2-geography/accessors-geog.cpp b/src/s2-geography/accessors-geog.cpp new file mode 100644 index 00000000..71616249 --- /dev/null +++ b/src/s2-geography/accessors-geog.cpp @@ -0,0 +1,137 @@ + +#include "s2/s2centroids.h" + +#include "accessors-geog.hpp" +#include "geography.hpp" +#include "build.hpp" +#include "accessors.hpp" + +namespace s2geography { + +S2Point s2_centroid(const S2Geography& geog) { + S2Point centroid(0, 0, 0); + + if (geog.dimension() == 0) { + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); + for (int j = 0; j < shape->num_edges(); j++) { + centroid += shape->edge(j).v0; + } + } + + return centroid.Normalize(); + } + + if (geog.dimension() == 1) { + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); + for (int j = 0; j < shape->num_edges(); j++) { + S2Shape::Edge e = shape->edge(j); + centroid += S2::TrueCentroid(e.v0, e.v1); + } + } + + return centroid.Normalize(); + } + + if (geog.dimension() == 2) { + auto polygon_ptr = dynamic_cast(&geog); + if (polygon_ptr != nullptr) { + centroid = polygon_ptr->Polygon()->GetCentroid(); + } else { + std::unique_ptr built = s2_build_polygon(geog); + centroid = built->Polygon()->GetCentroid(); + } + + return centroid.Normalize(); + } + + auto collection_ptr = dynamic_cast(&geog); + if (collection_ptr == nullptr) { + throw S2GeographyException("Can't compute s2_centroid() on custom collection geography"); + } + + for (auto& feat: collection_ptr->Features()) { + centroid += s2_centroid(*feat); + } + + return centroid.Normalize(); +} + +std::unique_ptr s2_boundary(const S2Geography& geog) { + int dimension = s2_dimension(geog); + + if (dimension == 1) { + std::vector endpoints; + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); + if (shape->dimension() < 1) { + continue; + } + + endpoints.reserve(endpoints.size() + shape->num_chains() * 2); + for (int j = 0; j < shape->num_chains(); j++) { + S2Shape::Chain chain = shape->chain(j); + if (chain.length > 0) { + endpoints.push_back(shape->edge(chain.start).v0); + endpoints.push_back(shape->edge(chain.start + chain.length - 1).v1); + } + } + } + + return absl::make_unique(std::move(endpoints)); + } + + if (dimension == 2) { + std::vector> polylines; + polylines.reserve(geog.num_shapes()); + + for (int i = 0; i < geog.num_shapes(); i++) { + auto shape = geog.Shape(i); + if (shape->dimension() != 2) { + throw S2GeographyException("Can't extract boundary from heterogeneous collection"); + } + + for (int j = 0; j < shape->num_chains(); j++) { + S2Shape::Chain chain = shape->chain(j); + if (chain.length > 0) { + std::vector points(chain.length + 1); + + points[0] = shape->edge(chain.start).v0; + for (int k = 0; k < chain.length; k++) { + points[k + 1] = shape->edge(chain.start + k).v1; + } + + auto polyline = absl::make_unique(std::move(points)); + polylines.push_back(std::move(polyline)); + } + } + } + + return absl::make_unique(std::move(polylines)); + } + + return absl::make_unique(); +} + + +void S2CentroidAggregator::Add(const S2Geography& geog) { + S2Point centroid = s2_centroid(geog); + if (centroid.Norm2() > 0) { + centroid_ += centroid.Normalize(); + } + } + +void S2CentroidAggregator::Merge(const S2CentroidAggregator& other) { + centroid_ += other.centroid_; +} + +S2Point S2CentroidAggregator::Finalize() { + if (centroid_.Norm2() > 0) { + return centroid_.Normalize(); + } else { + return centroid_; + } +} + +} diff --git a/src/s2-geography/accessors-geog.hpp b/src/s2-geography/accessors-geog.hpp new file mode 100644 index 00000000..4ee362ae --- /dev/null +++ b/src/s2-geography/accessors-geog.hpp @@ -0,0 +1,22 @@ + +#pragma once + +#include "geography.hpp" +#include "aggregator.hpp" + +namespace s2geography { + +S2Point s2_centroid(const S2Geography& geog); +std::unique_ptr s2_boundary(const S2Geography& geog); + +class S2CentroidAggregator: public S2Aggregator { +public: + void Add(const S2Geography& geog); + void Merge(const S2CentroidAggregator& other); + S2Point Finalize(); + +private: + S2Point centroid_; +}; + +} diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 1b9fd989..5499b69f 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -1,6 +1,4 @@ -#include "s2/s2centroids.h" - #include "geography.hpp" #include "accessors.hpp" #include "aggregator.hpp" @@ -270,131 +268,4 @@ bool s2_find_validation_error(const S2Geography& geog, S2Error* error) { throw S2GeographyException("s2_find_validation() error not implemented for this geography type"); } - -S2Point s2_centroid(const S2Geography& geog) { - S2Point centroid(0, 0, 0); - - if (geog.dimension() == 0) { - for (int i = 0; i < geog.num_shapes(); i++) { - auto shape = geog.Shape(i); - for (int j = 0; j < shape->num_edges(); j++) { - centroid += shape->edge(j).v0; - } - } - - return centroid.Normalize(); - } - - if (geog.dimension() == 1) { - for (int i = 0; i < geog.num_shapes(); i++) { - auto shape = geog.Shape(i); - for (int j = 0; j < shape->num_edges(); j++) { - S2Shape::Edge e = shape->edge(j); - centroid += S2::TrueCentroid(e.v0, e.v1); - } - } - - return centroid.Normalize(); - } - - if (geog.dimension() == 2) { - auto polygon_ptr = dynamic_cast(&geog); - if (polygon_ptr != nullptr) { - centroid = polygon_ptr->Polygon()->GetCentroid(); - } else { - std::unique_ptr built = s2_build_polygon(geog); - centroid = built->Polygon()->GetCentroid(); - } - - return centroid.Normalize(); - } - - auto collection_ptr = dynamic_cast(&geog); - if (collection_ptr == nullptr) { - throw S2GeographyException("Can't compute s2_centroid() on custom collection geography"); - } - - for (auto& feat: collection_ptr->Features()) { - centroid += s2_centroid(*feat); - } - - return centroid.Normalize(); -} - -std::unique_ptr s2_boundary(const S2Geography& geog) { - int dimension = s2_dimension(geog); - - if (dimension == 1) { - std::vector endpoints; - for (int i = 0; i < geog.num_shapes(); i++) { - auto shape = geog.Shape(i); - if (shape->dimension() < 1) { - continue; - } - - endpoints.reserve(endpoints.size() + shape->num_chains() * 2); - for (int j = 0; j < shape->num_chains(); j++) { - S2Shape::Chain chain = shape->chain(j); - if (chain.length > 0) { - endpoints.push_back(shape->edge(chain.start).v0); - endpoints.push_back(shape->edge(chain.start + chain.length - 1).v1); - } - } - } - - return absl::make_unique(std::move(endpoints)); - } - - if (dimension == 2) { - std::vector> polylines; - polylines.reserve(geog.num_shapes()); - - for (int i = 0; i < geog.num_shapes(); i++) { - auto shape = geog.Shape(i); - if (shape->dimension() != 2) { - throw S2GeographyException("Can't extract boundary from heterogeneous collection"); - } - - for (int j = 0; j < shape->num_chains(); j++) { - S2Shape::Chain chain = shape->chain(j); - if (chain.length > 0) { - std::vector points(chain.length + 1); - - points[0] = shape->edge(chain.start).v0; - for (int k = 0; k < chain.length; k++) { - points[k + 1] = shape->edge(chain.start + k).v1; - } - - auto polyline = absl::make_unique(std::move(points)); - polylines.push_back(std::move(polyline)); - } - } - } - - return absl::make_unique(std::move(polylines)); - } - - return absl::make_unique(); -} - - -void S2CentroidAggregator::Add(const S2Geography& geog) { - S2Point centroid = s2_centroid(geog); - if (centroid.Norm2() > 0) { - centroid_ += centroid.Normalize(); - } - } - -void S2CentroidAggregator::Merge(const S2CentroidAggregator& other) { - centroid_ += other.centroid_; -} - -S2Point S2CentroidAggregator::Finalize() { - if (centroid_.Norm2() > 0) { - return centroid_.Normalize(); - } else { - return centroid_; - } -} - } diff --git a/src/s2-geography/accessors.hpp b/src/s2-geography/accessors.hpp index fee4270e..5af41058 100644 --- a/src/s2-geography/accessors.hpp +++ b/src/s2-geography/accessors.hpp @@ -2,7 +2,6 @@ #pragma once #include "geography.hpp" -#include "aggregator.hpp" namespace s2geography { @@ -17,17 +16,4 @@ double s2_x(const S2Geography& geog); double s2_y(const S2Geography& geog); bool s2_find_validation_error(const S2Geography& geog, S2Error* error); -S2Point s2_centroid(const S2Geography& geog); -std::unique_ptr s2_boundary(const S2Geography& geog); - -class S2CentroidAggregator: public S2Aggregator { -public: - void Add(const S2Geography& geog); - void Merge(const S2CentroidAggregator& other); - S2Point Finalize(); - -private: - S2Point centroid_; -}; - } diff --git a/src/s2-geography/coverings.cpp b/src/s2-geography/coverings.cpp index 62b923a5..28002722 100644 --- a/src/s2-geography/coverings.cpp +++ b/src/s2-geography/coverings.cpp @@ -4,6 +4,7 @@ #include "geography.hpp" #include "coverings.hpp" #include "accessors.hpp" +#include "accessors-geog.hpp" namespace s2geography { diff --git a/src/s2-geography/s2-geography.hpp b/src/s2-geography/s2-geography.hpp index d9ea618b..7028eee0 100644 --- a/src/s2-geography/s2-geography.hpp +++ b/src/s2-geography/s2-geography.hpp @@ -3,6 +3,7 @@ #include "geography.hpp" #include "accessors.hpp" +#include "accessors-geog.hpp" #include "linear-referencing.hpp" #include "distance.hpp" #include "build.hpp" From f47c55fddfa7def9573b894cac64c1a16fef9122 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 10 Mar 2022 23:12:28 -0400 Subject: [PATCH 43/88] swap out convex hull query --- src/s2-geography/accessors-geog.cpp | 72 +++++++++++++++++++++++++++-- src/s2-geography/accessors-geog.hpp | 13 ++++++ src/s2-transformers.cpp | 53 +++++---------------- 3 files changed, 93 insertions(+), 45 deletions(-) diff --git a/src/s2-geography/accessors-geog.cpp b/src/s2-geography/accessors-geog.cpp index 71616249..fe50cf90 100644 --- a/src/s2-geography/accessors-geog.cpp +++ b/src/s2-geography/accessors-geog.cpp @@ -114,13 +114,19 @@ std::unique_ptr s2_boundary(const S2Geography& geog) { return absl::make_unique(); } +std::unique_ptr s2_convex_hull(const S2Geography& geog) { + S2ConvexHullAggregator agg; + agg.Add(geog); + return agg.Finalize(); +} + void S2CentroidAggregator::Add(const S2Geography& geog) { - S2Point centroid = s2_centroid(geog); - if (centroid.Norm2() > 0) { - centroid_ += centroid.Normalize(); - } + S2Point centroid = s2_centroid(geog); + if (centroid.Norm2() > 0) { + centroid_ += centroid.Normalize(); } +} void S2CentroidAggregator::Merge(const S2CentroidAggregator& other) { centroid_ += other.centroid_; @@ -134,4 +140,62 @@ S2Point S2CentroidAggregator::Finalize() { } } +void S2ConvexHullAggregator::Add(const S2Geography& geog) { + if (geog.dimension() == 0) { + auto point_ptr = dynamic_cast(&geog); + if (point_ptr != nullptr) { + for (const auto& point : point_ptr->Points()) { + query_.AddPoint(point); + } + } else { + keep_alive_.push_back(s2_rebuild(geog, S2GeographyOptions())); + Add(*keep_alive_.back()); + } + + return; + } + + if (geog.dimension() == 1) { + auto poly_ptr = dynamic_cast(&geog); + if (poly_ptr != nullptr) { + for (const auto& polyline : poly_ptr->Polylines()) { + query_.AddPolyline(*polyline); + } + } else { + keep_alive_.push_back(s2_rebuild(geog, S2GeographyOptions())); + Add(*keep_alive_.back()); + } + + return; + } + + if (geog.dimension() == 2) { + auto poly_ptr = dynamic_cast(&geog); + if (poly_ptr != nullptr) { + query_.AddPolygon(*poly_ptr->Polygon()); + } else { + keep_alive_.push_back(s2_rebuild(geog, S2GeographyOptions())); + Add(*keep_alive_.back()); + } + + return; + } + + auto collection_ptr = dynamic_cast(&geog); + if (collection_ptr != nullptr) { + for (const auto& feature : collection_ptr->Features()) { + Add(*feature); + } + } else { + keep_alive_.push_back(s2_rebuild(geog, S2GeographyOptions())); + Add(*keep_alive_.back()); + } +} + +std::unique_ptr S2ConvexHullAggregator::Finalize() { + auto polygon = absl::make_unique(); + polygon->Init(query_.GetConvexHull()); + return absl::make_unique(std::move(polygon)); +} + } diff --git a/src/s2-geography/accessors-geog.hpp b/src/s2-geography/accessors-geog.hpp index 4ee362ae..42885de4 100644 --- a/src/s2-geography/accessors-geog.hpp +++ b/src/s2-geography/accessors-geog.hpp @@ -1,6 +1,8 @@ #pragma once +#include "s2/s2convex_hull_query.h" + #include "geography.hpp" #include "aggregator.hpp" @@ -8,6 +10,7 @@ namespace s2geography { S2Point s2_centroid(const S2Geography& geog); std::unique_ptr s2_boundary(const S2Geography& geog); +std::unique_ptr s2_convex_hull(const S2Geography& geog); class S2CentroidAggregator: public S2Aggregator { public: @@ -19,4 +22,14 @@ class S2CentroidAggregator: public S2Aggregator { S2Point centroid_; }; +class S2ConvexHullAggregator: public S2Aggregator> { +public: + void Add(const S2Geography& geog); + std::unique_ptr Finalize(); + +private: + S2ConvexHullQuery query_; + std::vector> keep_alive_; +}; + } diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 2719aa00..f633e619 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -431,46 +431,15 @@ List cpp_s2_buffer_cells(List geog, NumericVector distance, int maxCells, int mi return op.processVector(geog); } - -class ConvexHullGeographyQuery: public S2ConvexHullQuery { -public: - - void AddGeography(Geography* feature) { - if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POLYGON) { - const S2Polygon* pol = feature->Polygon(); - this->AddPolygon(*pol); - } else if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POINT) { - const std::vector* pts = feature->Point(); - for(const auto& pt: *pts) { - this->AddPoint(pt); - } - } else if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POLYLINE) { - const std::vector>* lins = feature->Polyline(); - for(const auto& lin: *lins) { - this->AddPolyline(*lin); - } - } else if (feature->GeographyType() == Geography::Type::GEOGRAPHY_COLLECTION) { - const std::vector>* features = feature->CollectionFeatures(); - for (const auto& feat: *features) { - this->AddGeography(feat.get()); - } - } - } - - std::unique_ptr GetConvexHullPolygon() { - return absl::make_unique(this->GetConvexHull()); - } -}; - // [[Rcpp::export]] List cpp_s2_convex_hull(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { - ConvexHullGeographyQuery convexHullQuery; - convexHullQuery.AddGeography(feature); - - std::unique_ptr outP = convexHullQuery.GetConvexHullPolygon(); - return XPtr(new PolygonGeography(std::move(outP))); + auto geog = feature->NewGeography(); + std::unique_ptr geog_out = + s2geography::s2_convex_hull(*geog); + auto geography = MakeOldGeography(*geog_out); + return XPtr(geography.release()); } }; @@ -481,8 +450,9 @@ List cpp_s2_convex_hull(List geog) { // [[Rcpp::export]] List cpp_s2_convex_hull_agg(List geog, bool naRm) { - // create the convex hull query - ConvexHullGeographyQuery convexHullQuery; + s2geography::S2ConvexHullAggregator agg; + std::vector> keep_alive_; + SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; @@ -492,11 +462,12 @@ List cpp_s2_convex_hull_agg(List geog, bool naRm) { if (item != R_NilValue) { XPtr feature(item); - convexHullQuery.AddGeography(feature); + keep_alive_.push_back(feature->NewGeography()); + agg.Add(*keep_alive_.back()); } } - std::unique_ptr outP = convexHullQuery.GetConvexHullPolygon(); - XPtr outG(new PolygonGeography(std::move(outP))); + auto geography = MakeOldGeography(*agg.Finalize()); + XPtr outG(geography.release()); return List::create(outG); } From dc220285a602eb3dde94bf1e166000ab9ac553fb Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 16 Mar 2022 22:29:07 -0300 Subject: [PATCH 44/88] more flexible s2_project_normalized(), sketch s2_interpolate_normalized() --- src/s2-geography/linear-referencing.cpp | 59 ++++++++++++++++++++++--- src/s2-geography/linear-referencing.hpp | 1 + 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/s2-geography/linear-referencing.cpp b/src/s2-geography/linear-referencing.cpp index 5213f4c3..f8404f57 100644 --- a/src/s2-geography/linear-referencing.cpp +++ b/src/s2-geography/linear-referencing.cpp @@ -1,29 +1,74 @@ #include "geography.hpp" #include "linear-referencing.hpp" +#include "build.hpp" +#include "accessors.hpp" namespace s2geography { double s2_project_normalized(const S2GeographyOwningPolyline& geog1, - const S2GeographyOwningPoint& geog2) { - if (geog1.Polylines().size() != 1 || geog2.Points().size() != 1) { + const S2Point& point) { + if (geog1.Polylines().size() != 1 || point.Norm2() == 0) { return NAN; } - S2Point point = geog2.Points()[0]; int next_vertex; S2Point point_on_line = geog1.Polylines()[0]->Project(point, &next_vertex); return geog1.Polylines()[0]->UnInterpolate(point_on_line, next_vertex); } double s2_project_normalized(const S2Geography& geog1, const S2Geography& geog2) { + if (geog1.dimension() != 1 || geog2.dimension() != 0) { + return NAN; + } + + S2Point point; + for (int i = 0; i < geog2.num_shapes(); i++) { + auto shape = geog2.Shape(i); + for (int j = 0; j < shape->num_edges(); j++) { + if (point.Norm2() != 0) { + return NAN; + } else { + point = shape->edge(j).v0; + } + } + } + auto geog1_poly_ptr = dynamic_cast(&geog1); - auto geog2_point_ptr = dynamic_cast(&geog2); - if (geog1_poly_ptr != nullptr && geog2_point_ptr != nullptr) { - return s2_project_normalized(*geog1_poly_ptr, *geog2_point_ptr); + if (geog1_poly_ptr != nullptr) { + return s2_project_normalized(*geog1_poly_ptr, point); + } + + std::unique_ptr geog_poly = s2_rebuild(geog1, S2GeographyOptions()); + return s2_project_normalized(*geog_poly, geog2); +} + +S2Point s2_interpolate_normalized(const S2GeographyOwningPolyline& geog, double distance_norm) { + if (s2_is_empty(geog)) { + return S2Point(); + } else if (geog.Polylines().size() == 1) { + return geog.Polylines()[0]->Interpolate(distance_norm); } else { - return NAN; + throw S2GeographyException("`geog` must contain 0 or 1 polyines"); } } +S2Point s2_interpolate_normalized(const S2Geography& geog, double distance_norm) { + if (s2_is_empty(geog)) { + return S2Point(); + } + + if (geog.dimension() != 1 || geog.num_shapes() > 1) { + throw S2GeographyException("`geog` must be a single polyline"); + } + + auto geog_poly_ptr = dynamic_cast(&geog); + if (geog_poly_ptr != nullptr) { + return s2_interpolate_normalized(*geog_poly_ptr, distance_norm); + } + + std::unique_ptr geog_poly = s2_rebuild(geog, S2GeographyOptions()); + return s2_interpolate_normalized(*geog_poly, distance_norm); +} + } diff --git a/src/s2-geography/linear-referencing.hpp b/src/s2-geography/linear-referencing.hpp index 0841c6d0..7dba4a1e 100644 --- a/src/s2-geography/linear-referencing.hpp +++ b/src/s2-geography/linear-referencing.hpp @@ -6,5 +6,6 @@ namespace s2geography { double s2_project_normalized(const S2Geography& geog1, const S2Geography& geog2); +S2Point s2_interpolate_normalized(const S2Geography& geog, double distance_norm); } From d43d258f5a6bf4989ff4809bd7853e18e9d9a2d9 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 16 Mar 2022 22:37:16 -0300 Subject: [PATCH 45/88] use new api for interpolate_normalized --- src/s2-transformers.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index f633e619..d15eec9f 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -378,19 +378,24 @@ List cpp_s2_interpolate_normalized(List geog, NumericVector distanceNormalized) return R_NilValue; } - if (feature->IsCollection()) { - throw GeographyOperatorException("`x` must be a simple geography"); + auto geog = feature->NewGeography(); + + if (s2geography::s2_is_empty(*geog)) { + return XPtr(new PointGeography()); } - if (feature->IsEmpty()) { - return R_NilValue; + if (s2geography::s2_is_collection(*geog)) { + throw GeographyOperatorException("`x` must be a simple geography"); + } else if (geog->dimension() != 1) { + throw GeographyOperatorException("`x` must be a polyline"); } - if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POLYLINE) { - S2Point point = feature->Polyline()->at(0)->Interpolate(this->distanceNormalized[i]); - return XPtr(new PointGeography(point)); + S2Point point = s2geography::s2_interpolate_normalized(*geog, this->distanceNormalized[i]); + + if (point.Norm2() == 0) { + return XPtr(new PointGeography()); } else { - throw GeographyOperatorException("`x` must be a polyline geography"); + return XPtr(new PointGeography(point)); } } }; From f29bc240f9d6ab0fa3ca381dbabb8bd28e81b7fa Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 16 Mar 2022 22:42:33 -0300 Subject: [PATCH 46/88] use new API for buffer_cells() --- src/s2-transformers.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index d15eec9f..f7b323f1 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -419,8 +419,11 @@ List cpp_s2_buffer_cells(List geog, NumericVector distance, int maxCells, int mi } SEXP processFeature(XPtr feature, R_xlen_t i) { + auto geog = feature->NewGeography(); + s2geography::S2GeographyShapeIndex index(*geog); + S2ShapeIndexBufferedRegion region; - region.Init(feature->ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); + region.Init(&index.ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); S2CellUnion cellUnion; cellUnion = coverer.GetCovering(region); From afbdd0cfd7c4f5f9d63c021c18ace4a2f8c37e1c Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 16 Mar 2022 22:46:10 -0300 Subject: [PATCH 47/88] remove unused headers --- src/s2-transformers.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index f7b323f1..70da17e4 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -1,27 +1,9 @@ -#include "s2/s2boolean_operation.h" -#include "s2/s2closest_edge_query.h" -#include "s2/s2polygon.h" -#include "s2/s2polyline.h" -#include "s2/s2point.h" -#include "s2/s2error.h" -#include "s2/s2boolean_operation.h" -#include "s2/s2builder.h" -#include "s2/s2builderutil_s2polygon_layer.h" -#include "s2/s2builderutil_s2polyline_vector_layer.h" -#include "s2/s2builderutil_s2point_vector_layer.h" -#include "s2/s2builderutil_closed_set_normalizer.h" -#include "s2/s2builderutil_snap_functions.h" #include "s2/s2shape_index_buffered_region.h" #include "s2/s2region_coverer.h" -#include "s2/s2convex_hull_query.h" #include "s2-options.h" #include "geography-operator.h" -#include "point-geography.h" -#include "polyline-geography.h" -#include "polygon-geography.h" -#include "geography-collection.h" #include "geography-shim.h" #include From 1fcd5c45892935ff2a35fdc7cd63255ac56b6992 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 17 Mar 2022 22:18:35 -0300 Subject: [PATCH 48/88] bounds use new geographies --- src/s2-bounds.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/s2-bounds.cpp b/src/s2-bounds.cpp index 5e121982..6dd486fa 100644 --- a/src/s2-bounds.cpp +++ b/src/s2-bounds.cpp @@ -24,7 +24,7 @@ DataFrame cpp_s2_bounds_cap(List geog) { lat[i] = lng[i] = angle[i] = NA_REAL; } else { Rcpp::XPtr feature(item); - S2Cap cap = feature->GetCapBound(); + S2Cap cap = feature->NewGeography()->Region()->GetCapBound(); S2LatLng center(cap.center()); lng[i] = center.lng().degrees(); lat[i] = center.lat().degrees(); @@ -51,7 +51,7 @@ DataFrame cpp_s2_bounds_rect(List geog) { lng_lo[i] = lat_lo[i] = lng_hi[i] = lat_hi[i] = NA_REAL; } else { Rcpp::XPtr feature(item); - S2LatLngRect rect = feature->GetRectBound(); + S2LatLngRect rect = feature->NewGeography()->Region()->GetRectBound(); lng_lo[i] = rect.lng_lo().degrees(); lat_lo[i] = rect.lat_lo().degrees(); lng_hi[i] = rect.lng_hi().degrees(); From 636ce8b74a1e4cd22a346c033b0bc7b1a1f5440e Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 17 Mar 2022 22:43:17 -0300 Subject: [PATCH 49/88] coverings --- src/s2-cell-union.cpp | 12 ++++++++++-- src/s2-geography/coverings.cpp | 18 ++++++++++++++++++ src/s2-geography/coverings.hpp | 7 +++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/s2-cell-union.cpp b/src/s2-cell-union.cpp index 38bc6840..3710c656 100644 --- a/src/s2-cell-union.cpp +++ b/src/s2-cell-union.cpp @@ -330,8 +330,11 @@ List cpp_s2_covering_cell_ids(List geog, int min_level, int max_level, distance(distance), coverer(coverer), interior(interior) {} SEXP processFeature(XPtr feature, R_xlen_t i) { + auto geog = feature->NewGeography(); + s2geography::S2GeographyShapeIndex index(*geog); + S2ShapeIndexBufferedRegion region; - region.Init(feature->ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); + region.Init(&index.ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); S2CellUnion cellUnion; if (interior) { @@ -366,6 +369,8 @@ List cpp_s2_covering_cell_ids_agg(List geog, int min_level, int max_level, S1ChordAngle bufferAngle = S1ChordAngle::Radians(buffer); S2RegionUnion regionUnion; + std::vector> keep_alive; + std::vector> index_keep_alive; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { @@ -378,9 +383,12 @@ List cpp_s2_covering_cell_ids_agg(List geog, int min_level, int max_level, if (item != R_NilValue) { Rcpp::XPtr feature(item); + keep_alive.push_back(feature->NewGeography()); + auto index = absl::make_unique(*keep_alive.back()); auto region = absl::make_unique(); - region->Init(feature->ShapeIndex(), bufferAngle); + region->Init(&index->ShapeIndex(), bufferAngle); regionUnion.Add(std::move(region)); + index_keep_alive.push_back(std::move(index)); } } diff --git a/src/s2-geography/coverings.cpp b/src/s2-geography/coverings.cpp index 28002722..700f908a 100644 --- a/src/s2-geography/coverings.cpp +++ b/src/s2-geography/coverings.cpp @@ -1,5 +1,6 @@ #include "s2/s2region_coverer.h" +#include "s2/s2shape_index_buffered_region.h" #include "geography.hpp" #include "coverings.hpp" @@ -57,4 +58,21 @@ S2Point s2_point_on_surface(const S2Geography& geog, S2RegionCoverer& coverer) { throw S2GeographyException("s2_point_on_surface() not implemented for polyline"); } +void s2_covering(const S2Geography& geog, std::vector* covering, + S2RegionCoverer& coverer) { + coverer.GetCovering(*geog.Region(), covering); +} + +void s2_interior_covering(const S2Geography& geog, std::vector* covering, + S2RegionCoverer& coverer) { + coverer.GetInteriorCovering(*geog.Region(), covering); +} + +void s2_covering_buffered(const S2GeographyShapeIndex& geog, double distance_radians, + std::vector* covering, + S2RegionCoverer& coverer) { + S2ShapeIndexBufferedRegion region(&geog.ShapeIndex(), S1ChordAngle::Radians(distance_radians)); + coverer.GetCovering(region, covering); +} + } diff --git a/src/s2-geography/coverings.hpp b/src/s2-geography/coverings.hpp index 69473986..7b7727ca 100644 --- a/src/s2-geography/coverings.hpp +++ b/src/s2-geography/coverings.hpp @@ -8,5 +8,12 @@ namespace s2geography { S2Point s2_point_on_surface(const S2Geography& geog, S2RegionCoverer& coverer); +void s2_covering(const S2Geography& geog, std::vector* covering, + S2RegionCoverer& coverer); +void s2_interior_covering(const S2Geography& geog, std::vector* covering, + S2RegionCoverer& coverer); +void s2_covering_buffered(const S2GeographyShapeIndex& geog, double distance_radians, + std::vector* covering, + S2RegionCoverer& coverer); } From 9374f15cc859078540d526fc7cac7b4568f4e9c1 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 20 Mar 2022 15:09:52 -0300 Subject: [PATCH 50/88] add basic indexer --- src/s2-geography/geography.hpp | 5 +- src/s2-geography/index.hpp | 105 ++++++++++++++++++++++++++++++ src/s2-geography/s2-geography.hpp | 1 + src/s2-matrix.cpp | 42 ++++++++---- 4 files changed, 139 insertions(+), 14 deletions(-) create mode 100644 src/s2-geography/index.hpp diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index 36374939..ab6c76f6 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -178,7 +178,8 @@ class S2GeographyCollection: public S2Geography { // valid for the scope of those objects. class S2GeographyShapeIndex: public S2Geography { public: - S2GeographyShapeIndex() {} + S2GeographyShapeIndex(MutableS2ShapeIndex::Options options = MutableS2ShapeIndex::Options()) + : shape_index_(options) {} explicit S2GeographyShapeIndex(const S2Geography& geog) { Add(geog); @@ -201,7 +202,7 @@ class S2GeographyShapeIndex: public S2Geography { std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; - const S2ShapeIndex& ShapeIndex() const { + const MutableS2ShapeIndex& ShapeIndex() const { return shape_index_; } diff --git a/src/s2-geography/index.hpp b/src/s2-geography/index.hpp new file mode 100644 index 00000000..cf55a776 --- /dev/null +++ b/src/s2-geography/index.hpp @@ -0,0 +1,105 @@ + +#pragma once + +#include + +#include "geography.hpp" + +namespace s2geography { + +class S2GeographyIndex { +public: + S2GeographyIndex(MutableS2ShapeIndex::Options options = MutableS2ShapeIndex::Options()) + : index_(options) {} + + void Add(const S2Geography& geog, int value) { + values_.reserve(values_.size() + geog.num_shapes()); + for (int i = 0; i < geog.num_shapes(); i++) { + int new_shape_id = index_.Add(geog.Shape(i)); + values_.resize(new_shape_id + 1); + values_[new_shape_id] = value; + } + } + + int value(int shape_id) const { + return values_[shape_id]; + } + + const MutableS2ShapeIndex& ShapeIndex() const { + return index_; + } + + MutableS2ShapeIndex& MutableShapeIndex() { + return index_; + } + + class Iterator { + public: + Iterator(const S2GeographyIndex* index): + index_(index), iterator_(&index_->ShapeIndex()) {} + + const std::unordered_set& Query(const std::vector& covering, + bool reset = true) { + if (reset) { + might_intersect_indices_.clear(); + } + + for (const S2CellId& query_cell: covering) { + Query(query_cell, false); + } + + return might_intersect_indices_; + } + + const std::unordered_set& Query(const S2CellId& cell_id, bool reset = true) { + if (reset) { + might_intersect_indices_.clear(); + } + + S2ShapeIndex::CellRelation relation = iterator_.Locate(cell_id); + + if (relation == S2ShapeIndex::CellRelation::INDEXED) { + // We're in luck! these indexes have this cell in common + // add all the shapes it contains as possible intersectors + const S2ShapeIndexCell& index_cell = iterator_.cell(); + for (int k = 0; k < index_cell.num_clipped(); k++) { + int shape_id = index_cell.clipped(k).shape_id(); + might_intersect_indices_.insert(index_->value(shape_id)); + } + } else if(relation == S2ShapeIndex::CellRelation::SUBDIVIDED) { + // Promising! the index has a child cell of iterator_.id() + // (at which iterator_ is now positioned). Keep iterating until the + // iterator is done OR we're no longer at a child cell of + // iterator_.id(). The ordering of the iterator isn't guaranteed anywhere + // in the documentation; however, this ordering would be consistent + // with that of a Normalized S2CellUnion. + while (!iterator_.done() && cell_id.contains(iterator_.id())) { + // add all the shapes the child cell contains as possible intersectors + const S2ShapeIndexCell& index_cell = iterator_.cell(); + for (int k = 0; k < index_cell.num_clipped(); k++) { + int shape_id = index_cell.clipped(k).shape_id(); + might_intersect_indices_.insert(index_->value(shape_id)); + } + + // go to the next cell in the index + iterator_.Next(); + } + } + + // else: relation == S2ShapeIndex::CellRelation::DISJOINT (do nothing) + + return might_intersect_indices_; + } + + private: + const S2GeographyIndex* index_; + MutableS2ShapeIndex::Iterator iterator_; + std::unordered_set might_intersect_indices_; + }; + +private: + MutableS2ShapeIndex index_; + std::vector values_; +}; + +}; diff --git a/src/s2-geography/s2-geography.hpp b/src/s2-geography/s2-geography.hpp index 7028eee0..ecfbc75f 100644 --- a/src/s2-geography/s2-geography.hpp +++ b/src/s2-geography/s2-geography.hpp @@ -2,6 +2,7 @@ #pragma once #include "geography.hpp" +#include "index.hpp" #include "accessors.hpp" #include "accessors-geog.hpp" #include "linear-referencing.hpp" diff --git a/src/s2-matrix.cpp b/src/s2-matrix.cpp index 10a79735..ddc32f33 100644 --- a/src/s2-matrix.cpp +++ b/src/s2-matrix.cpp @@ -99,20 +99,38 @@ class IndexedBinaryGeographyOperator: public UnaryGeographyOperator geog2Index; std::unordered_map geog2IndexSource; + std::unique_ptr geog2_index; + std::vector> keep_alive_; - IndexedBinaryGeographyOperator() { - this->geog2Index = absl::make_unique(); - } - - // maxEdgesPerCell should be between 10 and 50, with lower numbers + // max_edges_per_cell should be between 10 and 50, with lower numbers // leading to more memory usage (but potentially faster query times). Benchmarking // with binary prediates seems to indicate that values on the high end // of the spectrum do a reasonable job of efficient preselection, and that // decreasing this value does little to increase performance. + + IndexedBinaryGeographyOperator() { + MutableS2ShapeIndex::Options index_options; + index_options.set_max_edges_per_cell(50); + this->geog2Index = absl::make_unique(index_options); + geog2_index = absl::make_unique(index_options); + } + virtual void buildIndex(List geog2, int maxEdgesPerCell = 50) { - MutableS2ShapeIndex::Options indexOptions; - indexOptions.set_max_edges_per_cell(maxEdgesPerCell); - this->geog2Index = absl::make_unique(indexOptions); + for (R_xlen_t j = 0; j < geog2.size(); j++) { + checkUserInterrupt(); + SEXP item2 = geog2[j]; + + // build index and store index IDs so that shapeIds can be + // mapped back to the geog index + if (item2 == R_NilValue) { + Rcpp::stop("Missing `y` not allowed in binary indexed operators()"); + } else { + Rcpp::XPtr feature2(item2); + keep_alive_.push_back(std::move(feature2->NewGeography())); + geog2_index->Add(*keep_alive_.back(), j); + } + } + this->geog2IndexSource = buildSourcedIndex(geog2, this->geog2Index.get()); } }; @@ -125,14 +143,14 @@ IntegerVector cpp_s2_closest_feature(List geog1, List geog2) { class Op: public IndexedBinaryGeographyOperator { public: int processFeature(Rcpp::XPtr feature, R_xlen_t i) { - S2ClosestEdgeQuery query(this->geog2Index.get()); + S2ClosestEdgeQuery query(&geog2_index->ShapeIndex()); S2ClosestEdgeQuery::ShapeIndexTarget target(feature->ShapeIndex()); const auto& result = query.FindClosestEdge(&target); if (result.is_empty()) { return NA_INTEGER; } else { // convert to R index (+1) - return this->geog2IndexSource[result.shape_id()] + 1; + return geog2_index->value(result.shape_id()) + 1; } } }; @@ -148,14 +166,14 @@ IntegerVector cpp_s2_farthest_feature(List geog1, List geog2) { class Op: public IndexedBinaryGeographyOperator { public: int processFeature(Rcpp::XPtr feature, R_xlen_t i) { - S2FurthestEdgeQuery query(this->geog2Index.get()); + S2FurthestEdgeQuery query(&geog2_index->ShapeIndex()); S2FurthestEdgeQuery::ShapeIndexTarget target(feature->ShapeIndex()); const auto& result = query.FindFurthestEdge(&target); if (result.is_empty()) { return NA_INTEGER; } else { // convert to R index (+1) - return this->geog2IndexSource[result.shape_id()] + 1; + return geog2_index->value(result.shape_id()) + 1; } } }; From 75b3e1e790879e0893d529b39763cc127fe6b066 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 20 Mar 2022 21:42:41 -0300 Subject: [PATCH 51/88] more using new API for matrix --- src/s2-matrix.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/s2-matrix.cpp b/src/s2-matrix.cpp index ddc32f33..6a56879f 100644 --- a/src/s2-matrix.cpp +++ b/src/s2-matrix.cpp @@ -143,8 +143,9 @@ IntegerVector cpp_s2_closest_feature(List geog1, List geog2) { class Op: public IndexedBinaryGeographyOperator { public: int processFeature(Rcpp::XPtr feature, R_xlen_t i) { + s2geography::S2GeographyShapeIndex index(*feature->NewGeography()); S2ClosestEdgeQuery query(&geog2_index->ShapeIndex()); - S2ClosestEdgeQuery::ShapeIndexTarget target(feature->ShapeIndex()); + S2ClosestEdgeQuery::ShapeIndexTarget target(&index.ShapeIndex()); const auto& result = query.FindClosestEdge(&target); if (result.is_empty()) { return NA_INTEGER; @@ -166,8 +167,9 @@ IntegerVector cpp_s2_farthest_feature(List geog1, List geog2) { class Op: public IndexedBinaryGeographyOperator { public: int processFeature(Rcpp::XPtr feature, R_xlen_t i) { + s2geography::S2GeographyShapeIndex index(*feature->NewGeography()); S2FurthestEdgeQuery query(&geog2_index->ShapeIndex()); - S2FurthestEdgeQuery::ShapeIndexTarget target(feature->ShapeIndex()); + S2FurthestEdgeQuery::ShapeIndexTarget target(&index.ShapeIndex()); const auto& result = query.FindFurthestEdge(&target); if (result.is_empty()) { return NA_INTEGER; @@ -190,17 +192,18 @@ List cpp_s2_closest_edges(List geog1, List geog2, int n, double min_distance, class Op: public IndexedBinaryGeographyOperator { public: IntegerVector processFeature(Rcpp::XPtr feature, R_xlen_t i) { - S2ClosestEdgeQuery query(this->geog2Index.get()); + s2geography::S2GeographyShapeIndex index(*feature->NewGeography()); + S2ClosestEdgeQuery query(&geog2_index->ShapeIndex()); query.mutable_options()->set_max_results(n); query.mutable_options()->set_max_distance(S1ChordAngle::Radians(max_distance)); - S2ClosestEdgeQuery::ShapeIndexTarget target(feature->ShapeIndex()); + S2ClosestEdgeQuery::ShapeIndexTarget target(&index.ShapeIndex()); const auto& result = query.FindClosestEdges(&target); // this code searches edges, which may come from the same feature std::unordered_set features; for (S2ClosestEdgeQuery::Result res : result) { if (res.distance().radians() > this->min_distance) { - features.insert(this->geog2IndexSource[res.shape_id()] + 1); + features.insert(geog2_index->value(res.shape_id()) + 1); } } From a8e5743b74d453266c04856e9db41a46987f898f Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 20 Mar 2022 22:34:34 -0300 Subject: [PATCH 52/88] remove old index infrastructure, use new index for matrix predicates --- src/s2-matrix.cpp | 132 +++++++++------------------------------------- 1 file changed, 25 insertions(+), 107 deletions(-) diff --git a/src/s2-matrix.cpp b/src/s2-matrix.cpp index 6a56879f..ef3c832f 100644 --- a/src/s2-matrix.cpp +++ b/src/s2-matrix.cpp @@ -14,92 +14,12 @@ #include using namespace Rcpp; -std::unordered_map buildSourcedIndex(List geog, MutableS2ShapeIndex* index) { - std::unordered_map indexSource; - std::vector shapeIds; - - for (R_xlen_t j = 0; j < geog.size(); j++) { - checkUserInterrupt(); - SEXP item2 = geog[j]; - - // build index and store index IDs so that shapeIds can be - // mapped back to the geog index - if (item2 == R_NilValue) { - Rcpp::stop("Missing `y` not allowed in binary indexed operators()"); - } else { - Rcpp::XPtr feature2(item2); - shapeIds = feature2->BuildShapeIndex(index); - for (size_t k = 0; k < shapeIds.size(); k ++) { - indexSource[shapeIds[k]] = j; - } - } - } - - return indexSource; -} - -std::unordered_set findPossibleIntersections(const S2Region& region, - const MutableS2ShapeIndex* index, - std::unordered_map& source, - int maxRegionCells) { - - std::unordered_set mightIntersectIndices; - MutableS2ShapeIndex::Iterator indexIterator(index); - - // generate a small covering of the region - S2RegionCoverer coverer; - coverer.mutable_options()->set_max_cells(maxRegionCells); - S2CellUnion covering = coverer.GetCovering(region); - - // iterate over cells in the featureIndex - for (S2CellId featureCellId: covering) { - S2ShapeIndex::CellRelation relation = indexIterator.Locate(featureCellId); - - if (relation == S2ShapeIndex::CellRelation::INDEXED) { - // we're in luck! these indexes have this cell in common - // add all the features it contains as possible intersectors for featureIndex - const S2ShapeIndexCell& cell = indexIterator.cell(); - for (int k = 0; k < cell.num_clipped(); k++) { - int shapeId = cell.clipped(k).shape_id(); - mightIntersectIndices.insert(source[shapeId]); - } - - } else if(relation == S2ShapeIndex::CellRelation::SUBDIVIDED) { - // promising! the geog2 index has a child cell of it.id() - // (at which indexIterator is now positioned) - // keep iterating until the iterator is done OR we're no longer at a child cell of - // it.id(). The ordering of the iterator isn't guaranteed anywhere in the documentation; - // however, this ordering would be consistent with that of a Normalized - // S2CellUnion. - while (!indexIterator.done() && featureCellId.contains(indexIterator.id())) { - // potentially many cells in the indexIterator, so let the user cancel if this is - // running too long - checkUserInterrupt(); - - // add all the features the child cell contains as possible intersectors for featureIndex - const S2ShapeIndexCell& cell = indexIterator.cell(); - for (int k = 0; k < cell.num_clipped(); k++) { - int shapeId = cell.clipped(k).shape_id(); - mightIntersectIndices.insert(source[shapeId]); - } - - // go to the next cell in the index - indexIterator.Next(); - } - } - - // else: relation == S2ShapeIndex::CellRelation::DISJOINT (do nothing) - } - - return mightIntersectIndices; -} template class IndexedBinaryGeographyOperator: public UnaryGeographyOperator { public: - std::unique_ptr geog2Index; - std::unordered_map geog2IndexSource; std::unique_ptr geog2_index; + std::unique_ptr iterator; std::vector> keep_alive_; // max_edges_per_cell should be between 10 and 50, with lower numbers @@ -108,14 +28,13 @@ class IndexedBinaryGeographyOperator: public UnaryGeographyOperatorgeog2Index = absl::make_unique(index_options); + index_options.set_max_edges_per_cell(maxEdgesPerCell); geog2_index = absl::make_unique(index_options); } - virtual void buildIndex(List geog2, int maxEdgesPerCell = 50) { + virtual void buildIndex(List geog2) { for (R_xlen_t j = 0; j < geog2.size(); j++) { checkUserInterrupt(); SEXP item2 = geog2[j]; @@ -131,7 +50,7 @@ class IndexedBinaryGeographyOperator: public UnaryGeographyOperatorgeog2IndexSource = buildSourcedIndex(geog2, this->geog2Index.get()); + iterator = absl::make_unique(geog2_index.get()); } }; @@ -231,48 +150,44 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperator(maxEdgesPerCell), maxFeatureCells(maxFeatureCells) { GeographyOperationOptions options(s2options); this->options = options.booleanOperationOptions(); + this->coverer.mutable_options()->set_max_cells(maxFeatureCells); } - // See IndexedBinaryGeographyOperator::buildIndex() for why 50 is the default value - // for maxEdgesPerCell - void buildIndex(List geog2, int maxEdgesPerCell = 50) { + void buildIndex(List geog2) { this->geog2 = geog2; - IndexedBinaryGeographyOperator::buildIndex(geog2, maxEdgesPerCell); + IndexedBinaryGeographyOperator::buildIndex(geog2); } IntegerVector processFeature(Rcpp::XPtr feature, R_xlen_t i) { S2ShapeIndex* index1 = feature->ShapeIndex(); S2ShapeIndexRegion region = MakeS2ShapeIndexRegion(index1); - // build a list of candidate feature indices - std::unordered_set mightIntersectIndices = findPossibleIntersections( - region, - this->geog2Index.get(), - this->geog2IndexSource, - this->maxFeatureCells - ); + coverer.GetCovering(region, &cell_ids); + const std::unordered_set& might_intersect = iterator->Query(cell_ids); // loop through features from geog2 that might intersect feature // and build a list of indices that actually intersect (based on // this->actuallyIntersects(), which might perform alternative // comparisons) - std::vector actuallyIntersectIndices; - for (R_xlen_t j: mightIntersectIndices) { + indices.clear(); + for (int j: might_intersect) { SEXP item = this->geog2[j]; XPtr feature2(item); if (this->actuallyIntersects(index1, feature2->ShapeIndex(), i, j)) { // convert to R index here + 1 - actuallyIntersectIndices.push_back(j + 1); + indices.push_back(j + 1); } } // return sorted integer vector - std::sort(actuallyIntersectIndices.begin(), actuallyIntersectIndices.end()); - return Rcpp::IntegerVector(actuallyIntersectIndices.begin(), actuallyIntersectIndices.end()); + std::sort(indices.begin(), indices.end()); + return Rcpp::IntegerVector(indices.begin(), indices.end()); }; virtual bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) = 0; @@ -281,6 +196,9 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperator cell_ids; + std::vector indices; }; // [[Rcpp::export]] @@ -288,16 +206,16 @@ List cpp_s2_may_intersect_matrix(List geog1, List geog2, int maxEdgesPerCell, int maxFeatureCells, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: - Op(List s2options, int maxFeatureCells): - IndexedMatrixPredicateOperator(s2options, maxFeatureCells) {} + Op(List s2options, int maxFeatureCells, int maxEdgesPerCell): + IndexedMatrixPredicateOperator(s2options, maxFeatureCells, maxEdgesPerCell) {} bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { return true; }; }; - Op op(s2options, maxFeatureCells); - op.buildIndex(geog2, maxEdgesPerCell); + Op op(s2options, maxFeatureCells, maxEdgesPerCell); + op.buildIndex(geog2); return op.processVector(geog1); } From a2a90054b390eca4431ec6d228685a253e111df8 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 24 Mar 2022 23:01:17 -0300 Subject: [PATCH 53/88] add preeicates --- src/Makevars.in | 1 + src/Makevars.win | 1 + src/s2-geography/predicates.cpp | 95 +++++++++++++++++++++++++++++++ src/s2-geography/predicates.hpp | 31 ++++++++++ src/s2-geography/s2-geography.hpp | 1 + src/s2-predicates.cpp | 55 ++++++++---------- 6 files changed, 153 insertions(+), 31 deletions(-) create mode 100644 src/s2-geography/predicates.cpp create mode 100644 src/s2-geography/predicates.hpp diff --git a/src/Makevars.in b/src/Makevars.in index c7602298..1f95a511 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -112,6 +112,7 @@ OBJECTS = $(ABSL_LIBS) \ s2-geography/build.o \ s2-geography/coverings.o \ s2-geography/geography.o \ + s2-geography/predicates.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ s2/encoded_s2cell_id_vector.o \ diff --git a/src/Makevars.win b/src/Makevars.win index 50962fe4..0ca43b61 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -97,6 +97,7 @@ S2LIBS = $(ABSL_LIBS) \ s2-geography/build.o \ s2-geography/coverings.o \ s2-geography/geography.o \ + s2-geography/predicates.o \ s2/base/stringprintf.o \ s2/base/strtoint.o \ s2/encoded_s2cell_id_vector.o \ diff --git a/src/s2-geography/predicates.cpp b/src/s2-geography/predicates.cpp new file mode 100644 index 00000000..794ada8a --- /dev/null +++ b/src/s2-geography/predicates.cpp @@ -0,0 +1,95 @@ + +#include "s2/s2boolean_operation.h" +#include "s2/s2edge_tessellator.h" +#include "s2/s2lax_loop_shape.h" + +#include "accessors.hpp" +#include "predicates.hpp" + +namespace s2geography { + +bool s2_intersects(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2BooleanOperation::Options& options) { + return S2BooleanOperation::Intersects( + geog1.ShapeIndex(), + geog2.ShapeIndex(), + options); +} + +bool s2_equals(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2BooleanOperation::Options& options) { + return S2BooleanOperation::Equals( + geog1.ShapeIndex(), + geog2.ShapeIndex(), + options); +} + +bool s2_contains(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2BooleanOperation::Options& options) { + if (s2_is_empty(geog2)) { + return false; + } else { + return S2BooleanOperation::Contains( + geog1.ShapeIndex(), + geog2.ShapeIndex(), + options); + } +} + +// Note that 'touches' can be implemeted using: +// +// S2BooleanOperation::Options closedOptions = options; +// closedOptions.set_polygon_model(S2BooleanOperation::PolygonModel::CLOSED); +// closedOptions.set_polyline_model(S2BooleanOperation::PolylineModel::CLOSED); +// S2BooleanOperation::Options openOptions = options; +// openOptions.set_polygon_model(S2BooleanOperation::PolygonModel::OPEN); +// openOptions.set_polyline_model(S2BooleanOperation::PolylineModel::OPEN); +// s2_intersects(geog1, geog2, closed_options) && +// !s2_intersects(geog1, geog2, open_options); +// +// ...it isn't implemented here because the options creation should be done +// outside of any loop. + +bool s2_intersects_box(const S2GeographyShapeIndex& geog1, + S2LatLngRect rect, + const S2BooleanOperation::Options& options, + double tolerance) { + // 99% of this is making a S2Loop out of a S2LatLngRect + // This should probably be implemented elsewhere + S2::PlateCarreeProjection projection(180); + S2EdgeTessellator tessellator(&projection, S1Angle::Degrees(tolerance)); + std::vector vertices; + + tessellator.AppendUnprojected( + R2Point(rect.lng_lo().degrees(), rect.lat_lo().degrees()), + R2Point(rect.lng_hi().degrees(), rect.lat_lo().degrees()), + &vertices); + tessellator.AppendUnprojected( + R2Point(rect.lng_hi().degrees(), rect.lat_lo().degrees()), + R2Point(rect.lng_hi().degrees(), rect.lat_hi().degrees()), + &vertices); + tessellator.AppendUnprojected( + R2Point(rect.lng_hi().degrees(), rect.lat_hi().degrees()), + R2Point(rect.lng_lo().degrees(), rect.lat_hi().degrees()), + &vertices); + tessellator.AppendUnprojected( + R2Point(rect.lng_lo().degrees(), rect.lat_hi().degrees()), + R2Point(rect.lng_lo().degrees(), rect.lat_lo().degrees()), + &vertices); + + vertices.pop_back(); + + auto loop = absl::make_unique(std::move(vertices)); + MutableS2ShapeIndex index; + index.Add(std::move(loop)); + + return S2BooleanOperation::Intersects( + geog1.ShapeIndex(), + index, + options); +} + +} diff --git a/src/s2-geography/predicates.hpp b/src/s2-geography/predicates.hpp new file mode 100644 index 00000000..5731ea6a --- /dev/null +++ b/src/s2-geography/predicates.hpp @@ -0,0 +1,31 @@ + +#pragma once + +#include "s2/s2boolean_operation.h" + +#include "geography.hpp" + +namespace s2geography { + +bool s2_intersects(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2BooleanOperation::Options& options); + +bool s2_equals(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2BooleanOperation::Options& options); + +bool s2_contains(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2BooleanOperation::Options& options); + +bool s2_touches(const S2GeographyShapeIndex& geog1, + const S2GeographyShapeIndex& geog2, + const S2BooleanOperation::Options& options); + +bool s2_intersects_box(const S2GeographyShapeIndex& geog1, + S2LatLngRect rect, + const S2BooleanOperation::Options& options, + double tolerance); + +} diff --git a/src/s2-geography/s2-geography.hpp b/src/s2-geography/s2-geography.hpp index ecfbc75f..ea3d0df7 100644 --- a/src/s2-geography/s2-geography.hpp +++ b/src/s2-geography/s2-geography.hpp @@ -9,3 +9,4 @@ #include "distance.hpp" #include "build.hpp" #include "coverings.hpp" +#include "predicates.hpp" diff --git a/src/s2-predicates.cpp b/src/s2-predicates.cpp index 67aea0bf..39225ac9 100644 --- a/src/s2-predicates.cpp +++ b/src/s2-predicates.cpp @@ -28,11 +28,12 @@ LogicalVector cpp_s2_intersects(List geog1, List geog2, List s2options) { public: Op(List s2options): BinaryPredicateOperator(s2options) {} int processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - return S2BooleanOperation::Intersects( - *feature1->ShapeIndex(), - *feature2->ShapeIndex(), - options - ); + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); + + return s2geography::s2_intersects(index1, index2, options); }; }; @@ -47,11 +48,12 @@ LogicalVector cpp_s2_equals(List geog1, List geog2, List s2options) { public: Op(List s2options): BinaryPredicateOperator(s2options) {} int processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - return S2BooleanOperation::Equals( - *feature1->ShapeIndex(), - *feature2->ShapeIndex(), - this->options - ); + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); + + return s2geography::s2_equals(index1, index2, options); } }; @@ -65,17 +67,11 @@ LogicalVector cpp_s2_contains(List geog1, List geog2, List s2options) { public: Op(List s2options): BinaryPredicateOperator(s2options) {} int processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - // by default Contains() will return true for Contains(x, EMPTY), which is - // not true in BigQuery or GEOS - if (feature2->IsEmpty()) { - return false; - } else { - return S2BooleanOperation::Contains( - *feature1->ShapeIndex(), - *feature2->ShapeIndex(), - this->options - ); - } + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); + return s2geography::s2_contains(index1, index2, options); } }; @@ -98,16 +94,13 @@ LogicalVector cpp_s2_touches(List geog1, List geog2, List s2options) { } int processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - return S2BooleanOperation::Intersects( - *feature1->ShapeIndex(), - *feature2->ShapeIndex(), - this->closedOptions - ) && - !S2BooleanOperation::Intersects( - *feature1->ShapeIndex(), - *feature2->ShapeIndex(), - this->openOptions - ); + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); + + return s2geography::s2_intersects(index1, index2, closedOptions) && + !s2geography::s2_intersects(index1, index1, openOptions); } private: From 13d7000843819abdab8eed59832b75485f5d614b Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 24 Mar 2022 23:06:59 -0300 Subject: [PATCH 54/88] fix touches --- src/s2-predicates.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/s2-predicates.cpp b/src/s2-predicates.cpp index 39225ac9..fd96f4d1 100644 --- a/src/s2-predicates.cpp +++ b/src/s2-predicates.cpp @@ -99,8 +99,8 @@ LogicalVector cpp_s2_touches(List geog1, List geog2, List s2options) { s2geography::S2GeographyShapeIndex index1(*geog1); s2geography::S2GeographyShapeIndex index2(*geog2); - return s2geography::s2_intersects(index1, index2, closedOptions) && - !s2geography::s2_intersects(index1, index1, openOptions); + return s2geography::s2_intersects(index1, index2, this->closedOptions) && + !s2geography::s2_intersects(index1, index2, this->openOptions); } private: From 98b08625f4456c7930ebe7977afcb589db473285 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 26 Mar 2022 21:30:12 -0300 Subject: [PATCH 55/88] actually use new s2_intersects_box() --- src/s2-geography/predicates.cpp | 2 +- src/s2-geography/predicates.hpp | 2 +- src/s2-predicates.cpp | 36 ++++----------------------------- 3 files changed, 6 insertions(+), 34 deletions(-) diff --git a/src/s2-geography/predicates.cpp b/src/s2-geography/predicates.cpp index 794ada8a..6cd47859 100644 --- a/src/s2-geography/predicates.cpp +++ b/src/s2-geography/predicates.cpp @@ -54,7 +54,7 @@ bool s2_contains(const S2GeographyShapeIndex& geog1, // outside of any loop. bool s2_intersects_box(const S2GeographyShapeIndex& geog1, - S2LatLngRect rect, + const S2LatLngRect& rect, const S2BooleanOperation::Options& options, double tolerance) { // 99% of this is making a S2Loop out of a S2LatLngRect diff --git a/src/s2-geography/predicates.hpp b/src/s2-geography/predicates.hpp index 5731ea6a..a3cf810e 100644 --- a/src/s2-geography/predicates.hpp +++ b/src/s2-geography/predicates.hpp @@ -24,7 +24,7 @@ bool s2_touches(const S2GeographyShapeIndex& geog1, const S2BooleanOperation::Options& options); bool s2_intersects_box(const S2GeographyShapeIndex& geog1, - S2LatLngRect rect, + const S2LatLngRect& rect, const S2BooleanOperation::Options& options, double tolerance); diff --git a/src/s2-predicates.cpp b/src/s2-predicates.cpp index fd96f4d1..2b936a0b 100644 --- a/src/s2-predicates.cpp +++ b/src/s2-predicates.cpp @@ -183,39 +183,11 @@ LogicalVector cpp_s2_intersects_box(List geog, return false; } - // create polygon vertices - std::vector points(2 + 2 * detail); - S2LatLng vertex; - - // south edge - for (int i = 0; i <= detail; i++) { - vertex = S2LatLng::FromDegrees(xmin + deltaDegrees * i, ymin).Normalized(); - points[i] = vertex.ToPoint(); - } - - // north edge - for (int i = 0; i <= detail; i++) { - vertex = S2LatLng::FromDegrees(xmax - deltaDegrees * i, ymax).Normalized(); - points[detail + 1 + i] = vertex.ToPoint(); - } + S2LatLngRect rect(S2LatLng::FromDegrees(ymin, xmin), S2LatLng::FromDegrees(ymax, xmax)); - // create polygon - std::unique_ptr loop(new S2Loop()); - loop->set_s2debug_override(S2Debug::DISABLE); - loop->Init(points); - loop->Normalize(); - - std::vector> loops(1); - loops[0] = std::move(loop); - S2Polygon polygon; - polygon.InitOriented(std::move(loops)); - - // test intersection - return S2BooleanOperation::Intersects( - polygon.index(), - *feature->ShapeIndex(), - this->options - ); + auto geog = feature->NewGeography(); + s2geography::S2GeographyShapeIndex index(*geog); + return s2geography::s2_intersects_box(index, rect, options, deltaDegrees); } }; From 69690ca95b26daa3c7a5b5e85aafa95ab79d7789 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 26 Mar 2022 21:48:51 -0300 Subject: [PATCH 56/88] use new predicates in s2-matrix --- src/s2-matrix.cpp | 54 ++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/s2-matrix.cpp b/src/s2-matrix.cpp index ef3c832f..4b84f8a3 100644 --- a/src/s2-matrix.cpp +++ b/src/s2-matrix.cpp @@ -165,12 +165,12 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperator feature, R_xlen_t i) { - S2ShapeIndex* index1 = feature->ShapeIndex(); - S2ShapeIndexRegion region = MakeS2ShapeIndexRegion(index1); - - coverer.GetCovering(region, &cell_ids); + auto geog1 = feature->NewGeography(); + coverer.GetCovering(*geog1->Region(), &cell_ids); const std::unordered_set& might_intersect = iterator->Query(cell_ids); + s2geography::S2GeographyShapeIndex index1(*geog1); + // loop through features from geog2 that might intersect feature // and build a list of indices that actually intersect (based on // this->actuallyIntersects(), which might perform alternative @@ -179,7 +179,10 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperatorgeog2[j]; XPtr feature2(item); - if (this->actuallyIntersects(index1, feature2->ShapeIndex(), i, j)) { + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index2(*geog2); + + if (this->actuallyIntersects(index1, index2, i, j)) { // convert to R index here + 1 indices.push_back(j + 1); } @@ -190,7 +193,9 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperatoroptions); + bool actuallyIntersects(const s2geography::S2GeographyShapeIndex& index1, + const s2geography::S2GeographyShapeIndex& index2, + R_xlen_t i, R_xlen_t j) { + return s2geography::s2_contains(index1, index2, this->options); }; }; @@ -239,9 +248,11 @@ List cpp_s2_within_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} - bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { + bool actuallyIntersects(const s2geography::S2GeographyShapeIndex& index1, + const s2geography::S2GeographyShapeIndex& index2, + R_xlen_t i, R_xlen_t j) { // note reversed index2, index1 - return S2BooleanOperation::Contains(*index2, *index1, this->options); + return s2geography::s2_contains(index2, index1, this->options); }; }; @@ -255,8 +266,10 @@ List cpp_s2_intersects_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} - bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { - return S2BooleanOperation::Intersects(*index1, *index2, this->options); + bool actuallyIntersects(const s2geography::S2GeographyShapeIndex& index1, + const s2geography::S2GeographyShapeIndex& index2, + R_xlen_t i, R_xlen_t j) { + return s2geography::s2_intersects(index1, index2, this->options); }; }; @@ -270,8 +283,10 @@ List cpp_s2_equals_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} - bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { - return S2BooleanOperation::Equals(*index1, *index2, this->options); + bool actuallyIntersects(const s2geography::S2GeographyShapeIndex& index1, + const s2geography::S2GeographyShapeIndex& index2, + R_xlen_t i, R_xlen_t j) { + return s2geography::s2_equals(index1, index2, this->options); }; }; @@ -294,10 +309,11 @@ List cpp_s2_touches_matrix(List geog1, List geog2, List s2options) { this->openOptions.set_polyline_model(S2BooleanOperation::PolylineModel::OPEN); } - bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { - // efficiently re-uses the index on geog2 and takes advantage of short-circuiting && - return S2BooleanOperation::Intersects(*index1, *index2, this->closedOptions) && - !S2BooleanOperation::Intersects(*index1, *index2, this->openOptions); + bool actuallyIntersects(const s2geography::S2GeographyShapeIndex& index1, + const s2geography::S2GeographyShapeIndex& index2, + R_xlen_t i, R_xlen_t j) { + return s2geography::s2_intersects(index1, index2, this->closedOptions) && + !s2geography::s2_intersects(index1, index2, this->openOptions); }; private: From a6e36d2c455367aa3b6d031e6fdb698159940da8 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 26 Mar 2022 22:06:32 -0300 Subject: [PATCH 57/88] wrap regions instead of copying data --- src/s2-geography/geography.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/s2-geography/geography.cpp b/src/s2-geography/geography.cpp index 49d80094..f55df407 100644 --- a/src/s2-geography/geography.cpp +++ b/src/s2-geography/geography.cpp @@ -10,6 +10,7 @@ #include "s2/mutable_s2shape_index.h" #include "s2/s2shape_index_region.h" +#include "s2/s2shapeutil_coding.h" #include "geography.hpp" using namespace s2geography; @@ -23,7 +24,7 @@ using namespace s2geography; class S2ShapeWrapper: public S2Shape { public: S2ShapeWrapper(S2Shape* shape): shape_(shape) {} - int num_edges() const { return shape_->num_edges();} + int num_edges() const { return shape_->num_edges(); } Edge edge(int edge_id) const { return shape_->edge(edge_id); } int dimension() const { return shape_->dimension(); } ReferencePoint GetReferencePoint() const { return shape_->GetReferencePoint(); } @@ -36,6 +37,23 @@ class S2ShapeWrapper: public S2Shape { S2Shape* shape_; }; +class S2RegionWrapper: public S2Region { +public: + S2RegionWrapper(S2Region* region): region_(region) {} + S2Region* Clone() const { return region_->Clone(); } + S2Cap GetCapBound() const { return region_->GetCapBound(); } + S2LatLngRect GetRectBound() const { return region_->GetRectBound(); } + void GetCellUnionBound(std::vector *cell_ids) const { + return region_->GetCellUnionBound(cell_ids); + } + bool Contains(const S2Cell& cell) const { return region_->Contains(cell); } + bool MayIntersect(const S2Cell& cell) const { return region_->MayIntersect(cell); } + bool Contains(const S2Point& p) const { return region_->Contains(p); } + +private: + S2Region* region_; +}; + void S2Geography::GetCellUnionBound(std::vector* cell_ids) const { MutableS2ShapeIndex index; @@ -81,7 +99,7 @@ std::unique_ptr S2GeographyOwningPolyline::Shape(int id) const { std::unique_ptr S2GeographyOwningPolyline::Region() const { auto region = absl::make_unique(); for (const auto& polyline: polylines_) { - region->Add(std::unique_ptr(polyline->Clone())); + region->Add(absl::make_unique(polyline.get())); } // because Rtools for R 3.6 on Windows complains about a direct // return region @@ -99,7 +117,7 @@ std::unique_ptr S2GeographyOwningPolygon::Shape(int id) const { } std::unique_ptr S2GeographyOwningPolygon::Region() const { - return std::unique_ptr(polygon_->Clone()); + return absl::make_unique(polygon_.get()); } void S2GeographyOwningPolygon::GetCellUnionBound(std::vector* cell_ids) const { From b605e14a4a3cc9d703f2504ca443c31438e5161d Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 27 Mar 2022 12:59:52 -0300 Subject: [PATCH 58/88] explain the S2RegionWrapper --- src/s2-geography/geography.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/s2-geography/geography.cpp b/src/s2-geography/geography.cpp index f55df407..34c3d494 100644 --- a/src/s2-geography/geography.cpp +++ b/src/s2-geography/geography.cpp @@ -21,6 +21,7 @@ using namespace s2geography; // data. S2Shape instances do not typically own their data (e.g., S2Polygon::Shape), // so this does not change the general relationship (that anything returned by // S2Geography::Shape() is only valid within the scope of the S2Geography). +// Note that this class is also available (but not exposed) in s2/s2shapeutil_coding.cc. class S2ShapeWrapper: public S2Shape { public: S2ShapeWrapper(S2Shape* shape): shape_(shape) {} @@ -37,6 +38,13 @@ class S2ShapeWrapper: public S2Shape { S2Shape* shape_; }; +// Just like the S2ShapeWrapper, the S2RegionWrapper helps reconcile the differences +// in lifecycle expectation between S2 and S2Geography. We often need access to a +// S2Region to generalize algorithms; however, there are some operations that need +// ownership of the region (e.g., the S2RegionUnion). In S2Geography the assumption +// is that anything returned by a S2Geography is only valid for the lifetime of the +// underlying S2Geography. A different design of the algorithms implemented here might +// well make this unnecessary. class S2RegionWrapper: public S2Region { public: S2RegionWrapper(S2Region* region): region_(region) {} From 0ad573dc3c0254c9b91b3279e6c18fdf38d2fe47 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 28 Mar 2022 21:30:34 -0300 Subject: [PATCH 59/88] remove mutable shape index accessor --- src/s2-geography/geography.hpp | 8 +------- src/s2-geography/index.hpp | 5 +++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index ab6c76f6..7fa56777 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -187,9 +187,7 @@ class S2GeographyShapeIndex: public S2Geography { // Add a S2Geography to the index, returning the last shape_id // that was added to the index or -1 if no shapes were added - // to the index. Shape ids are assigned sequentially and the first - // shape_id that was added can be obtained by subtracting - // geog.num_shapes(). + // to the index. int Add(const S2Geography& geog) { int id = -1; for (int i = 0; i < geog.num_shapes(); i++) { @@ -206,10 +204,6 @@ class S2GeographyShapeIndex: public S2Geography { return shape_index_; } - MutableS2ShapeIndex& MutableShapeIndex() { - return shape_index_; - } - private: MutableS2ShapeIndex shape_index_; }; diff --git a/src/s2-geography/index.hpp b/src/s2-geography/index.hpp index cf55a776..0e0c6dea 100644 --- a/src/s2-geography/index.hpp +++ b/src/s2-geography/index.hpp @@ -7,6 +7,11 @@ namespace s2geography { +// Unlike the S2GeographyShapeIndex, whose function is to index a single S2Geography +// or index multiple S2Geography objects as if they were a single S2Geography, +// the S2GeographyIndex exists to index a vector of S2Geography objects (like a +// GEOSSTRTree index), providing (hopefully) rapid access to possibly intersecting +// features. class S2GeographyIndex { public: S2GeographyIndex(MutableS2ShapeIndex::Options options = MutableS2ShapeIndex::Options()) From 934b1c876174b3022fa7e7a36f0e5f02e2ce8a4a Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 28 Mar 2022 21:47:40 -0300 Subject: [PATCH 60/88] move ownership of index set outside index --- src/s2-geography/index.hpp | 24 +++++------------------- src/s2-matrix.cpp | 6 ++++-- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/s2-geography/index.hpp b/src/s2-geography/index.hpp index 0e0c6dea..2f17bce2 100644 --- a/src/s2-geography/index.hpp +++ b/src/s2-geography/index.hpp @@ -43,24 +43,13 @@ class S2GeographyIndex { Iterator(const S2GeographyIndex* index): index_(index), iterator_(&index_->ShapeIndex()) {} - const std::unordered_set& Query(const std::vector& covering, - bool reset = true) { - if (reset) { - might_intersect_indices_.clear(); - } - + void Query(const std::vector& covering, std::unordered_set* indices) { for (const S2CellId& query_cell: covering) { - Query(query_cell, false); + Query(query_cell, indices); } - - return might_intersect_indices_; } - const std::unordered_set& Query(const S2CellId& cell_id, bool reset = true) { - if (reset) { - might_intersect_indices_.clear(); - } - + void Query(const S2CellId& cell_id, std::unordered_set* indices) { S2ShapeIndex::CellRelation relation = iterator_.Locate(cell_id); if (relation == S2ShapeIndex::CellRelation::INDEXED) { @@ -69,7 +58,7 @@ class S2GeographyIndex { const S2ShapeIndexCell& index_cell = iterator_.cell(); for (int k = 0; k < index_cell.num_clipped(); k++) { int shape_id = index_cell.clipped(k).shape_id(); - might_intersect_indices_.insert(index_->value(shape_id)); + indices->insert(index_->value(shape_id)); } } else if(relation == S2ShapeIndex::CellRelation::SUBDIVIDED) { // Promising! the index has a child cell of iterator_.id() @@ -83,7 +72,7 @@ class S2GeographyIndex { const S2ShapeIndexCell& index_cell = iterator_.cell(); for (int k = 0; k < index_cell.num_clipped(); k++) { int shape_id = index_cell.clipped(k).shape_id(); - might_intersect_indices_.insert(index_->value(shape_id)); + indices->insert(index_->value(shape_id)); } // go to the next cell in the index @@ -92,14 +81,11 @@ class S2GeographyIndex { } // else: relation == S2ShapeIndex::CellRelation::DISJOINT (do nothing) - - return might_intersect_indices_; } private: const S2GeographyIndex* index_; MutableS2ShapeIndex::Iterator iterator_; - std::unordered_set might_intersect_indices_; }; private: diff --git a/src/s2-matrix.cpp b/src/s2-matrix.cpp index 4b84f8a3..f576650f 100644 --- a/src/s2-matrix.cpp +++ b/src/s2-matrix.cpp @@ -167,7 +167,8 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperator feature, R_xlen_t i) { auto geog1 = feature->NewGeography(); coverer.GetCovering(*geog1->Region(), &cell_ids); - const std::unordered_set& might_intersect = iterator->Query(cell_ids); + indices_unsorted.clear(); + iterator->Query(cell_ids, &indices_unsorted); s2geography::S2GeographyShapeIndex index1(*geog1); @@ -176,7 +177,7 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperatoractuallyIntersects(), which might perform alternative // comparisons) indices.clear(); - for (int j: might_intersect) { + for (int j: indices_unsorted) { SEXP item = this->geog2[j]; XPtr feature2(item); auto geog2 = feature2->NewGeography(); @@ -203,6 +204,7 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperator cell_ids; + std::unordered_set indices_unsorted; std::vector indices; }; From f34774e07e633ef5c56a1b5080f03a8ae3362fb9 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 2 Apr 2022 22:37:07 -0300 Subject: [PATCH 61/88] start on new constructors --- src/Makevars.in | 1 + src/s2-constructors-formatters2.cpp | 219 +++++++++++++++++++++++ src/s2-geography/constructor.hpp | 268 ++++++++++++++++++++++++++++ 3 files changed, 488 insertions(+) create mode 100644 src/s2-constructors-formatters2.cpp create mode 100644 src/s2-geography/constructor.hpp diff --git a/src/Makevars.in b/src/Makevars.in index 1f95a511..0bca88e1 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -92,6 +92,7 @@ OBJECTS = $(ABSL_LIBS) \ s2-cell.o \ s2-cell-union.o \ s2-constructors-formatters.o \ + s2-constructors-formatters2.o \ s2-predicates.o \ s2-transformers.o \ init.o \ diff --git a/src/s2-constructors-formatters2.cpp b/src/s2-constructors-formatters2.cpp new file mode 100644 index 00000000..9fe5de82 --- /dev/null +++ b/src/s2-constructors-formatters2.cpp @@ -0,0 +1,219 @@ + +#define R_NO_REMAP +#include +#include + +#include "wk-v1.h" +#include "s2-geography/constructor.hpp" + + +#define CPP_START \ + char cpp_exception_error[8096]; \ + memset(cpp_exception_error, 0, 8096); \ + try { + +#define CPP_END \ + } catch (std::exception& e) { \ + strncpy(cpp_exception_error, e.what(), 8096 - 1); \ + } \ + Rf_error("%s", cpp_exception_error); \ + return R_NilValue; + + +// The other versions of CPP_START and CPP_END stack-allocate the +// error message buffer, which takes a non-trivial amount of time +// when done at this scale (at worst 4 times per coordinate). By +// keeping the buffer in the handler_data struct, we can call C++ +// from every handler method without measurable overhead. +#define WK_METHOD_CPP_START \ + try { + +#define WK_METHOD_CPP_END \ + } catch (std::exception& e) { \ + strncpy(data->cpp_exception_error, e.what(), 8096 - 1); \ + } \ + Rf_error("%s", data->cpp_exception_error); \ + return R_NilValue; + +#define WK_METHOD_CPP_END_INT \ + } catch (std::exception& e) { \ + strncpy(data->cpp_exception_error, e.what(), 8096 - 1); \ + } \ + Rf_error("%s", data->cpp_exception_error); \ + return WK_ABORT; + + +typedef struct { + s2geography::CollectionConstructor* builder; + int coord_size; + char cpp_exception_error[8096]; +} builder_handler_t; + +int builder_vector_start(const wk_vector_meta_t* meta, void* handler_data) { + return WK_CONTINUE; +} + +SEXP builder_vector_end(const wk_vector_meta_t* meta, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + + WK_METHOD_CPP_END +} + +int builder_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + data->builder->start_feature(); + WK_METHOD_CPP_END_INT +} + +int builder_feature_null(void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + // Append NULL + return WK_ABORT_FEATURE; + WK_METHOD_CPP_END_INT +} + +int builder_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + std::unique_ptr feat = data->builder->finish_feature(); + // Append external pointer to feature + WK_METHOD_CPP_END_INT +} + +int builder_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + + auto geometry_type = static_cast(meta->geometry_type); + + int32_t size; + if (meta->size == WK_SIZE_UNKNOWN) { + size = -1; + } else { + size = meta->size; + } + + if (meta->flags & WK_FLAG_HAS_Z && meta->flags & WK_FLAG_HAS_M) { + data->coord_size = 4; + } else if (meta->flags & WK_FLAG_HAS_Z) { + data->coord_size = 3; + } else if (meta->flags & WK_FLAG_HAS_M) { + data->coord_size = 3; + } else { + data->coord_size = 2; + } + + data->builder->geom_start(geometry_type, size); + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} + +int builder_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + data->builder->geom_end(); + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} + +int builder_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + + if (size == WK_SIZE_UNKNOWN) { + data->builder->ring_start(-1); + } else { + data->builder->ring_start(size); + } + + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} + +int builder_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + data->builder->ring_end(); + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} + +int builder_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + data->builder->coords(coord, 1, data->coord_size); + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} + +int builder_error(const char* message, void* handler_data) { + Rf_error("%s", message); + return WK_ABORT; +} + +void builder_finalize(void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + if (data != nullptr) { + free(data); + } +} + +void delete_collection_constructor(SEXP xptr) { + auto ptr = reinterpret_cast(R_ExternalPtrAddr(xptr)); + if (ptr != nullptr) { + delete ptr; + } +} + +extern "C" SEXP s2_c_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { + CPP_START + + auto builder = new s2geography::CollectionConstructor(); + SEXP builder_xptr = PROTECT(R_MakeExternalPtr(builder, R_NilValue, R_NilValue)); + R_RegisterCFinalizer(builder_xptr, &delete_collection_constructor); + + wk_handler_t* handler = wk_handler_create(); + + handler->vector_start = &builder_vector_start; + handler->vector_end = &builder_vector_end; + + handler->feature_start = &builder_feature_start; + handler->null_feature = &builder_feature_null; + handler->feature_end = &builder_feature_end; + + handler->geometry_start = &builder_geometry_start; + handler->geometry_end = &builder_geometry_end; + + handler->ring_start = &builder_ring_start; + handler->ring_end = &builder_ring_end; + + handler->coord = &builder_coord; + + handler->error = &builder_error; + + handler->finalizer = &builder_finalize; + + builder_handler_t* data = (builder_handler_t*) malloc(sizeof(builder_handler_t)); + if (data == NULL) { + wk_handler_destroy(handler); // # nocov + Rf_error("Failed to alloc handler data"); // # nocov + } + + data->coord_size = 2; + data->builder = builder; + memset(data->cpp_exception_error, 0, 8096); + + handler->handler_data = data; + + // include the builder pointer as a tag for this external pointer + // which guarnatees that it will not be garbage collected until + // this object is garbage collected + SEXP handler_xptr = wk_handler_create_xptr(handler, builder_xptr, R_NilValue); + UNPROTECT(1); + return handler_xptr; + + CPP_END +} diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp new file mode 100644 index 00000000..9340005e --- /dev/null +++ b/src/s2-geography/constructor.hpp @@ -0,0 +1,268 @@ + +# pragma once + +#include "geography.h" + +namespace s2geography { + +namespace util { + +enum GeometryType { + GEOMETRY_TYPE_UNKNOWN = 0, + POINT = 1, + LINESTRING = 2, + POLYGON = 3, + MULTIPOINT = 4, + MULTILINESTRING = 5, + MULTIPOLYGON = 6, + GEOMETRYCOLLECTION = 7 +}; + +} + +class Constructor { +public: + + virtual ~Constructor() {} + + virtual void geom_start(util::GeometryType geometry_type, int64_t size) {} + virtual void ring_start(int32_t size) {} + + virtual void coords(const double* coord, int64_t n, int32_t coord_size) { + for (int64_t i = 0; i < n; i++) { + S2LatLng pt = S2LatLng::FromDegrees(coord[i * coord_size], coord[i * coord_size + 1]); + points_.push_back(pt.ToPoint()); + } + } + + virtual void ring_end() {} + virtual void geom_end() {} + virtual std::unique_ptr finish() = 0; + +protected: + std::vector points_; +}; + +class PointConstructor: public Constructor { +public: + + void geom_start(util::GeometryType geometry_type, int64_t size) { + if (size != 0 && + geometry_type != util::GeometryType::POINT && + geometry_type != util::GeometryType::MULTIPOINT && + geometry_type != util::GeometryType::GEOMETRYCOLLECTION) { + throw S2GeographyException( + "PointConstructor input must be empty, point, multipoint, or collection"); + } + + points_.reserve(points_.size() + size); + } + + void coords(const double* coord, int64_t n, int32_t coord_size) { + for (int64_t i = 0; i < n; i++) { + if (coord_empty(coord + (i * coord_size), coord_size)) { + continue; + } + + S2LatLng pt = S2LatLng::FromDegrees(coord[i * coord_size], coord[i * coord_size + 1]); + points_.push_back(pt.ToPoint()); + } + } + + std::unique_ptr finish() { + auto result = absl::make_unique(std::move(points_)); + points_.clear(); + return result; + } + +private: + bool coord_empty(const double* coord, int32_t coord_size) { + for (int32_t i = 0; i < coord_size; i++) { + if (!std::isnan(coord[i])) { + return false; + } + } + + return true; + } +}; + +class PolylineConstructor: public Constructor { +public: + void geom_start(util::GeometryType geometry_type, int64_t size) { + if (geometry_type == util::GeometryType::MULTILINESTRING || + geometry_type == util::GeometryType::GEOMETRYCOLLECTION) { + is_linestring_ = false; + } else if (geometry_type == util::GeometryType::LINESTRING || + size == 0) { + points_.reserve(size); + is_linestring_ = true; + } else { + throw S2GeographyException( + "PolylineConstructor input must be empty, linestring, multilinestring, or collection"); + } + } + + void geom_end() { + if (is_linestring_) { + auto polyline = absl::make_unique(std::move(points_)); + polylines_.push_back(std::move(polyline)); + points_.clear(); + is_linestring_ = false; + } + } + + std::unique_ptr finish() { + auto result = absl::make_unique(std::move(polylines_)); + polylines_.clear(); + return result; + } + +private: + bool is_linestring_; + std::vector> polylines_; +}; + +class PolygonConstructor: public Constructor { +public: + class Options { + public: + Options() : oriented_(false), check_(true) {} + bool oriented() { return oriented_; } + void set_oriented(bool oriented) { oriented_ = oriented; } + bool check() { return check_; } + void set_check(bool check) { check_ = check; } + + private: + bool oriented_; + bool check_; + }; + + PolygonConstructor(const Options& options): options_(options) {} + + void ring_start(int32_t size) { + loops_.clear(); + if (size > 0) { + loops_.reserve(size); + } + } + + void ring_end() { + auto loop = absl::make_unique(std::move(points_)); + loops_.push_back(std::move(loop)); + points_.clear(); + } + + std::unique_ptr finish() { + auto polygon = absl::make_unique(); + polygon->set_s2debug_override(S2Debug::DISABLE); + if (options_.oriented()) { + polygon->InitOriented(std::move(loops_)); + } else { + polygon->InitNested(std::move(loops_)); + } + + loops_.clear(); + return absl::make_unique(std::move(polygon)); + } + +private: + std::vector> loops_; + Options options_; +}; + +class CollectionConstructor: public Constructor { +public: + CollectionConstructor(const PolygonConstructor::Options& options = PolygonConstructor::Options()): + options_(options), level_(0), polygon_constructor_(options) {} + + void geom_start(util::GeometryType geometry_type, int64_t size) { + level_++; + + if (active_constructor_ != nullptr) { + active_constructor_->geom_start(geometry_type, size); + return; + } + + switch (geometry_type) { + case util::GeometryType::POINT: + case util::GeometryType::MULTIPOINT: + active_constructor_ = &point_constructor_; + break; + case util::GeometryType::LINESTRING: + case util::GeometryType::MULTILINESTRING: + active_constructor_ = &polyline_constructor_; + break; + case util::GeometryType::POLYGON: + case util::GeometryType::MULTIPOLYGON: + active_constructor_ = &polygon_constructor_; + break; + case util::GeometryType::GEOMETRYCOLLECTION: + collection_constructor_ = absl::make_unique(); + active_constructor_ = collection_constructor_.get(); + break; + default: + throw S2GeographyException("CollectionConstructor: unsupported geometry type"); + } + + active_constructor_->geom_start(geometry_type, size); + + } + + void ring_start(int32_t size) { + active_constructor_->ring_start(size); + } + + void coords(const double* coord, int64_t n, int32_t coord_size) { + active_constructor_->coords(coord, n, coord_size); + } + + void ring_end() { + active_constructor_->ring_end(); + } + + void geom_end() { + active_constructor_->geom_end(); + level_--; + if (level_ == 0) { + auto feature = absl::make_unique(std::move(features_)); + features_.clear(); + features_.push_back(std::move(feature)); + active_constructor_ = nullptr; + } + } + + std::unique_ptr finish() { + auto result = absl::make_unique(std::move(features_)); + features_.clear(); + return result; + } + + void start_feature() { + level_ = 0; + active_constructor_ = nullptr; + } + + std::unique_ptr finish_feature() { + if (features_.empty()) { + return absl::make_unique(); + } else { + std::unique_ptr feature = std::move(features_.back()); + features_.pop_back(); + return feature; + } + } + +private: + PolygonConstructor::Options options_; + int level_; + PointConstructor point_constructor_; + PolylineConstructor polyline_constructor_; + PolygonConstructor polygon_constructor_; + std::unique_ptr collection_constructor_; + Constructor* active_constructor_; + + std::vector> features_; +}; + +} From 34cbb87ef4c0a42636ff376def16516970a70f52 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 3 Apr 2022 21:27:32 -0300 Subject: [PATCH 62/88] maybe fix Windows/R 3.6 error --- src/s2-geography/constructor.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index 9340005e..21cd4642 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -72,7 +72,7 @@ class PointConstructor: public Constructor { std::unique_ptr finish() { auto result = absl::make_unique(std::move(points_)); points_.clear(); - return result; + return std::unique_ptr(result.release()); } private: @@ -115,7 +115,7 @@ class PolylineConstructor: public Constructor { std::unique_ptr finish() { auto result = absl::make_unique(std::move(polylines_)); polylines_.clear(); - return result; + return std::unique_ptr(result.release()); } private: @@ -163,7 +163,8 @@ class PolygonConstructor: public Constructor { } loops_.clear(); - return absl::make_unique(std::move(polygon)); + auto result = absl::make_unique(std::move(polygon)); + return std::unique_ptr(result.release()); } private: @@ -235,7 +236,7 @@ class CollectionConstructor: public Constructor { std::unique_ptr finish() { auto result = absl::make_unique(std::move(features_)); features_.clear(); - return result; + return std::unique_ptr(result.release()); } void start_feature() { From a22fd04ae7e25913edeae7c2cbdfb4ccdc62db03 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 3 Apr 2022 22:34:48 -0300 Subject: [PATCH 63/88] fix wiring for wk handler --- R/wk-utils.R | 11 +++++++++++ src/RcppExports.cpp | 2 ++ src/s2-constructors-formatters2.cpp | 6 ++++-- src/s2-geography/constructor.hpp | 8 ++++++-- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/R/wk-utils.R b/R/wk-utils.R index 1372a361..18cc9522 100644 --- a/R/wk-utils.R +++ b/R/wk-utils.R @@ -65,6 +65,17 @@ s2_projection_filter <- function(handler, projection = s2_projection_plate_carre ) } +s2_geography_writer <- function(oriented = FALSE, check = TRUE) { + wk::new_wk_handler( + .Call( + c_s2_geography_writer_new, + as.logical(oriented)[1], + as.logical(check)[1] + ), + "s2_geography_writer" + ) +} + #' @rdname s2_unprojection_filter #' @export s2_projection_plate_carree <- function() { diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index c018de11..3317c623 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -1482,6 +1482,7 @@ END_RCPP } RcppExport SEXP c_s2_coord_filter_new(SEXP, SEXP, SEXP, SEXP); +RcppExport SEXP c_s2_geography_writer_new(SEXP, SEXP); RcppExport SEXP c_s2_projection_mercator(); RcppExport SEXP c_s2_projection_plate_carree(); @@ -1609,6 +1610,7 @@ static const R_CallMethodDef CallEntries[] = { {"_s2_s2_xptr_test", (DL_FUNC) &_s2_s2_xptr_test, 1}, {"_s2_s2_xptr_test_op", (DL_FUNC) &_s2_s2_xptr_test_op, 1}, {"c_s2_coord_filter_new", (DL_FUNC) &c_s2_coord_filter_new, 4}, + {"c_s2_geography_writer_new", (DL_FUNC) &c_s2_geography_writer_new, 2}, {"c_s2_projection_mercator", (DL_FUNC) &c_s2_projection_mercator, 0}, {"c_s2_projection_plate_carree", (DL_FUNC) &c_s2_projection_plate_carree, 0}, {NULL, NULL, 0} diff --git a/src/s2-constructors-formatters2.cpp b/src/s2-constructors-formatters2.cpp index 9fe5de82..bcbb5f04 100644 --- a/src/s2-constructors-formatters2.cpp +++ b/src/s2-constructors-formatters2.cpp @@ -56,7 +56,7 @@ int builder_vector_start(const wk_vector_meta_t* meta, void* handler_data) { SEXP builder_vector_end(const wk_vector_meta_t* meta, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START - + return R_NilValue; WK_METHOD_CPP_END } @@ -64,6 +64,7 @@ int builder_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START data->builder->start_feature(); + return WK_CONTINUE; WK_METHOD_CPP_END_INT } @@ -80,6 +81,7 @@ int builder_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* ha WK_METHOD_CPP_START std::unique_ptr feat = data->builder->finish_feature(); // Append external pointer to feature + return WK_CONTINUE; WK_METHOD_CPP_END_INT } @@ -168,7 +170,7 @@ void delete_collection_constructor(SEXP xptr) { } } -extern "C" SEXP s2_c_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { +extern "C" SEXP c_s2_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { CPP_START auto builder = new s2geography::CollectionConstructor(); diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index 21cd4642..bef347f9 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -55,7 +55,9 @@ class PointConstructor: public Constructor { "PointConstructor input must be empty, point, multipoint, or collection"); } - points_.reserve(points_.size() + size); + if (size > 0) { + points_.reserve(points_.size() + size); + } } void coords(const double* coord, int64_t n, int32_t coord_size) { @@ -95,7 +97,9 @@ class PolylineConstructor: public Constructor { is_linestring_ = false; } else if (geometry_type == util::GeometryType::LINESTRING || size == 0) { - points_.reserve(size); + if (size > 0) { + points_.reserve(size); + } is_linestring_ = true; } else { throw S2GeographyException( From f71ba7f93e5fc4759c012c170962be7b177b0c9e Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 7 Apr 2022 22:35:58 -0300 Subject: [PATCH 64/88] basics of the constructor work --- src/geography-shim.h | 7 ++- src/s2-constructors-formatters2.cpp | 75 +++++++++++++++++++++++++++-- src/s2-geography/constructor.hpp | 24 +++++---- tests/testthat/test-wk-utils.R | 9 ++++ 4 files changed, 100 insertions(+), 15 deletions(-) diff --git a/src/geography-shim.h b/src/geography-shim.h index 2eff8471..a7e3bf93 100644 --- a/src/geography-shim.h +++ b/src/geography-shim.h @@ -1,11 +1,14 @@ +#ifndef S2_GEOGRAPHY_SHIM_H_INCLUDED +#define S2_GEOGRAPHY_SHIM_H_INCLUDED + #include "point-geography.h" #include "polyline-geography.h" #include "polygon-geography.h" #include "geography-collection.h" #include "s2-geography/s2-geography.hpp" -std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog) { +static inline std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog) { auto point = dynamic_cast(&geog); if (point != nullptr) { return absl::make_unique(point->Points()); @@ -37,3 +40,5 @@ std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog throw s2geography::S2GeographyException("Unsupported S2Geography subclass"); } + +#endif diff --git a/src/s2-constructors-formatters2.cpp b/src/s2-constructors-formatters2.cpp index bcbb5f04..22dcf173 100644 --- a/src/s2-constructors-formatters2.cpp +++ b/src/s2-constructors-formatters2.cpp @@ -5,6 +5,7 @@ #include "wk-v1.h" #include "s2-geography/constructor.hpp" +#include "geography-shim.h" #define CPP_START \ @@ -45,19 +46,74 @@ typedef struct { s2geography::CollectionConstructor* builder; + SEXP result; + R_xlen_t feat_id; int coord_size; char cpp_exception_error[8096]; } builder_handler_t; + +// TODO: Both of these allocate in a way that could longjmp and possibly leak memory +static inline void builder_result_append(builder_handler_t* data, SEXP value) { + R_xlen_t current_size = Rf_xlength(data->result); + if (data->feat_id >= current_size) { + SEXP new_result = PROTECT(Rf_allocVector(VECSXP, current_size * 2 + 1)); + for (R_xlen_t i = 0; i < current_size; i++) { + SET_VECTOR_ELT(new_result, i, VECTOR_ELT(data->result, i)); + } + R_ReleaseObject(data->result); + data->result = new_result; + R_PreserveObject(data->result); + UNPROTECT(1); + } + + SET_VECTOR_ELT(data->result, data->feat_id, value); + data->feat_id++; +} + +static inline void builder_result_finalize(builder_handler_t* data) { + R_xlen_t current_size = Rf_xlength(data->result); + if (data->feat_id != current_size) { + SEXP new_result = PROTECT(Rf_allocVector(VECSXP, data->feat_id)); + for (R_xlen_t i = 0; i < data->feat_id; i++) { + SET_VECTOR_ELT(new_result, i, VECTOR_ELT(data->result, i)); + } + R_ReleaseObject(data->result); + data->result = new_result; + R_PreserveObject(data->result); + UNPROTECT(1); + } +} + int builder_vector_start(const wk_vector_meta_t* meta, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + + if (data->result != R_NilValue) { + Rf_error("Destination vector was already allocated"); // # nocov + } + + if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { + data->result = PROTECT(Rf_allocVector(VECSXP, 1024)); + } else { + data->result = PROTECT(Rf_allocVector(VECSXP, meta->size)); + } + R_PreserveObject(data->result); + UNPROTECT(1); + + data->feat_id = 0; + return WK_CONTINUE; } SEXP builder_vector_end(const wk_vector_meta_t* meta, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; - WK_METHOD_CPP_START - return R_NilValue; - WK_METHOD_CPP_END + builder_result_finalize(data); + SEXP cls = PROTECT(Rf_allocVector(STRSXP, 2)); + SET_STRING_ELT(cls, 0, Rf_mkChar("s2_geography")); + SET_STRING_ELT(cls, 1, Rf_mkChar("s2_xptr")); + Rf_setAttrib(data->result, R_ClassSymbol, cls); + UNPROTECT(1); + return data->result; } int builder_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { @@ -80,7 +136,8 @@ int builder_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* ha builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START std::unique_ptr feat = data->builder->finish_feature(); - // Append external pointer to feature + auto geog = MakeOldGeography(*feat); + builder_result_append(data, Rcpp::XPtr(geog.release())); return WK_CONTINUE; WK_METHOD_CPP_END_INT } @@ -156,6 +213,14 @@ int builder_error(const char* message, void* handler_data) { return WK_ABORT; } +void builder_deinitialize(void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + if (data->result != R_NilValue) { + R_ReleaseObject(data->result); + data->result = R_NilValue; + } +} + void builder_finalize(void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; if (data != nullptr) { @@ -196,6 +261,7 @@ extern "C" SEXP c_s2_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { handler->error = &builder_error; + handler->deinitialize = &builder_deinitialize; handler->finalizer = &builder_finalize; builder_handler_t* data = (builder_handler_t*) malloc(sizeof(builder_handler_t)); @@ -206,6 +272,7 @@ extern "C" SEXP c_s2_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { data->coord_size = 2; data->builder = builder; + data->result = R_NilValue; memset(data->cpp_exception_error, 0, 8096); handler->handler_data = data; diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index bef347f9..b6bba37f 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -30,7 +30,7 @@ class Constructor { virtual void coords(const double* coord, int64_t n, int32_t coord_size) { for (int64_t i = 0; i < n; i++) { - S2LatLng pt = S2LatLng::FromDegrees(coord[i * coord_size], coord[i * coord_size + 1]); + S2LatLng pt = S2LatLng::FromDegrees(coord[i * coord_size + 1], coord[i * coord_size]); points_.push_back(pt.ToPoint()); } } @@ -179,7 +179,8 @@ class PolygonConstructor: public Constructor { class CollectionConstructor: public Constructor { public: CollectionConstructor(const PolygonConstructor::Options& options = PolygonConstructor::Options()): - options_(options), level_(0), polygon_constructor_(options) {} + options_(options), level_(0), polygon_constructor_(options), + collection_constructor_(nullptr) {} void geom_start(util::GeometryType geometry_type, int64_t size) { level_++; @@ -193,25 +194,26 @@ class CollectionConstructor: public Constructor { case util::GeometryType::POINT: case util::GeometryType::MULTIPOINT: active_constructor_ = &point_constructor_; + active_constructor_->geom_start(geometry_type, size); break; case util::GeometryType::LINESTRING: case util::GeometryType::MULTILINESTRING: active_constructor_ = &polyline_constructor_; + active_constructor_->geom_start(geometry_type, size); break; case util::GeometryType::POLYGON: case util::GeometryType::MULTIPOLYGON: active_constructor_ = &polygon_constructor_; + active_constructor_->geom_start(geometry_type, size); break; case util::GeometryType::GEOMETRYCOLLECTION: - collection_constructor_ = absl::make_unique(); - active_constructor_ = collection_constructor_.get(); + this->collection_constructor_ = absl::make_unique(options_); + this->active_constructor_ = this->collection_constructor_.get(); + // don't call geom_start()! break; default: throw S2GeographyException("CollectionConstructor: unsupported geometry type"); } - - active_constructor_->geom_start(geometry_type, size); - } void ring_start(int32_t size) { @@ -227,11 +229,13 @@ class CollectionConstructor: public Constructor { } void geom_end() { - active_constructor_->geom_end(); + if (active_constructor_ != collection_constructor_.get()) { + active_constructor_->geom_end(); + } + level_--; if (level_ == 0) { - auto feature = absl::make_unique(std::move(features_)); - features_.clear(); + auto feature = active_constructor_->finish(); features_.push_back(std::move(feature)); active_constructor_ = nullptr; } diff --git a/tests/testthat/test-wk-utils.R b/tests/testthat/test-wk-utils.R index f8d657bb..893ab98f 100644 --- a/tests/testthat/test-wk-utils.R +++ b/tests/testthat/test-wk-utils.R @@ -184,3 +184,12 @@ test_that("mercator projection works", { wk::xy(c(0, 20037508), 0) ) }) + +test_that("the s2_geography_writer() works", { + wk::wk_debug(wk::wkt("POLYGON ((0 0, 0 1, 1 0, 0 0))"), s2_geography_writer()) + + wk::wk_debug( + wk::wkt("GEOMETRYCOLLECTION(POLYGON ((0 0, 0 1, 1 0, 0 0)))"), + s2_geography_writer() + ) +}) From 72fc88cb40c9b7d413374adab7f78bc43fc64ce0 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Fri, 8 Apr 2022 21:56:33 -0300 Subject: [PATCH 65/88] valid output for collections --- src/s2-constructors-formatters2.cpp | 10 ++-- src/s2-geography/constructor.hpp | 90 +++++++++++++++++------------ 2 files changed, 57 insertions(+), 43 deletions(-) diff --git a/src/s2-constructors-formatters2.cpp b/src/s2-constructors-formatters2.cpp index 22dcf173..28d854e0 100644 --- a/src/s2-constructors-formatters2.cpp +++ b/src/s2-constructors-formatters2.cpp @@ -45,7 +45,7 @@ typedef struct { - s2geography::CollectionConstructor* builder; + s2geography::VectorConstructor* builder; SEXP result; R_xlen_t feat_id; int coord_size; @@ -228,8 +228,8 @@ void builder_finalize(void* handler_data) { } } -void delete_collection_constructor(SEXP xptr) { - auto ptr = reinterpret_cast(R_ExternalPtrAddr(xptr)); +void delete_vector_constructor(SEXP xptr) { + auto ptr = reinterpret_cast(R_ExternalPtrAddr(xptr)); if (ptr != nullptr) { delete ptr; } @@ -238,9 +238,9 @@ void delete_collection_constructor(SEXP xptr) { extern "C" SEXP c_s2_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { CPP_START - auto builder = new s2geography::CollectionConstructor(); + auto builder = new s2geography::VectorConstructor(); SEXP builder_xptr = PROTECT(R_MakeExternalPtr(builder, R_NilValue, R_NilValue)); - R_RegisterCFinalizer(builder_xptr, &delete_collection_constructor); + R_RegisterCFinalizer(builder_xptr, &delete_vector_constructor); wk_handler_t* handler = wk_handler_create(); diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index b6bba37f..8b37d909 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -23,6 +23,19 @@ enum GeometryType { class Constructor { public: + class Options { + public: + Options() : oriented_(false), check_(true) {} + bool oriented() { return oriented_; } + void set_oriented(bool oriented) { oriented_ = oriented; } + bool check() { return check_; } + void set_check(bool check) { check_ = check; } + + private: + bool oriented_; + bool check_; + }; + virtual ~Constructor() {} virtual void geom_start(util::GeometryType geometry_type, int64_t size) {} @@ -129,19 +142,6 @@ class PolylineConstructor: public Constructor { class PolygonConstructor: public Constructor { public: - class Options { - public: - Options() : oriented_(false), check_(true) {} - bool oriented() { return oriented_; } - void set_oriented(bool oriented) { oriented_ = oriented; } - bool check() { return check_; } - void set_check(bool check) { check_ = check; } - - private: - bool oriented_; - bool check_; - }; - PolygonConstructor(const Options& options): options_(options) {} void ring_start(int32_t size) { @@ -178,13 +178,19 @@ class PolygonConstructor: public Constructor { class CollectionConstructor: public Constructor { public: - CollectionConstructor(const PolygonConstructor::Options& options = PolygonConstructor::Options()): - options_(options), level_(0), polygon_constructor_(options), - collection_constructor_(nullptr) {} + CollectionConstructor(const Options& options = Options()): + options_(options), polygon_constructor_(options), + collection_constructor_(nullptr), + level_(0) {} void geom_start(util::GeometryType geometry_type, int64_t size) { level_++; + if (level_ == 1 && geometry_type == util::GeometryType::GEOMETRYCOLLECTION) { + active_constructor_ = nullptr; + return; + } + if (active_constructor_ != nullptr) { active_constructor_->geom_start(geometry_type, size); return; @@ -194,26 +200,24 @@ class CollectionConstructor: public Constructor { case util::GeometryType::POINT: case util::GeometryType::MULTIPOINT: active_constructor_ = &point_constructor_; - active_constructor_->geom_start(geometry_type, size); break; case util::GeometryType::LINESTRING: case util::GeometryType::MULTILINESTRING: active_constructor_ = &polyline_constructor_; - active_constructor_->geom_start(geometry_type, size); break; case util::GeometryType::POLYGON: case util::GeometryType::MULTIPOLYGON: active_constructor_ = &polygon_constructor_; - active_constructor_->geom_start(geometry_type, size); break; case util::GeometryType::GEOMETRYCOLLECTION: this->collection_constructor_ = absl::make_unique(options_); this->active_constructor_ = this->collection_constructor_.get(); - // don't call geom_start()! break; default: throw S2GeographyException("CollectionConstructor: unsupported geometry type"); } + + active_constructor_->geom_start(geometry_type, size); } void ring_start(int32_t size) { @@ -228,16 +232,15 @@ class CollectionConstructor: public Constructor { active_constructor_->ring_end(); } - void geom_end() { - if (active_constructor_ != collection_constructor_.get()) { - active_constructor_->geom_end(); - } - + virtual void geom_end() { level_--; - if (level_ == 0) { + + if (level_ == 1) { auto feature = active_constructor_->finish(); features_.push_back(std::move(feature)); active_constructor_ = nullptr; + } else if (level_ > 1) { + active_constructor_->geom_end(); } } @@ -247,12 +250,34 @@ class CollectionConstructor: public Constructor { return std::unique_ptr(result.release()); } +private: + Options options_; + PointConstructor point_constructor_; + PolylineConstructor polyline_constructor_; + PolygonConstructor polygon_constructor_; + std::unique_ptr collection_constructor_; + + +protected: + Constructor* active_constructor_; + int level_; + std::vector> features_; +}; + +class VectorConstructor: public CollectionConstructor { +public: + VectorConstructor(const Options& options = Options()): CollectionConstructor(options) {} + void start_feature() { - level_ = 0; active_constructor_ = nullptr; + level_ = 0; + features_.clear(); + geom_start(util::GeometryType::GEOMETRYCOLLECTION, 1); } std::unique_ptr finish_feature() { + geom_end(); + if (features_.empty()) { return absl::make_unique(); } else { @@ -261,17 +286,6 @@ class CollectionConstructor: public Constructor { return feature; } } - -private: - PolygonConstructor::Options options_; - int level_; - PointConstructor point_constructor_; - PolylineConstructor polyline_constructor_; - PolygonConstructor polygon_constructor_; - std::unique_ptr collection_constructor_; - Constructor* active_constructor_; - - std::vector> features_; }; } From ad636567c155b3ead0c80d8c92dcf7c08c25bfc7 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Fri, 8 Apr 2022 22:13:11 -0300 Subject: [PATCH 66/88] small progress towards builder working --- src/s2-constructors-formatters2.cpp | 4 +--- src/s2-geography/constructor.hpp | 2 +- tests/testthat/test-wk-utils.R | 13 +++++++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/s2-constructors-formatters2.cpp b/src/s2-constructors-formatters2.cpp index 28d854e0..f6e6e061 100644 --- a/src/s2-constructors-formatters2.cpp +++ b/src/s2-constructors-formatters2.cpp @@ -126,10 +126,8 @@ int builder_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* int builder_feature_null(void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; - WK_METHOD_CPP_START - // Append NULL + builder_result_append(data, R_NilValue); return WK_ABORT_FEATURE; - WK_METHOD_CPP_END_INT } int builder_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index 8b37d909..d6bbcc5d 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -79,7 +79,7 @@ class PointConstructor: public Constructor { continue; } - S2LatLng pt = S2LatLng::FromDegrees(coord[i * coord_size], coord[i * coord_size + 1]); + S2LatLng pt = S2LatLng::FromDegrees(coord[i * coord_size + 1], coord[i * coord_size]); points_.push_back(pt.ToPoint()); } } diff --git a/tests/testthat/test-wk-utils.R b/tests/testthat/test-wk-utils.R index 893ab98f..35b49292 100644 --- a/tests/testthat/test-wk-utils.R +++ b/tests/testthat/test-wk-utils.R @@ -186,6 +186,19 @@ test_that("mercator projection works", { }) test_that("the s2_geography_writer() works", { + for (name in names(geoarrow::geoarrow_example_wkt["multipolygon"])) { + cat(paste0(name, "\n")) + geog <- wk::wk_handle( + geoarrow::geoarrow_example_wkt[[name]], + s2_geography_writer() + ) + + expect_equal( + wk::wk_coords(as_wkt(geog))[c("x", "y")], + wk::wk_coords(geoarrow::geoarrow_example_wkt[[name]])[c("x", "y")] + ) + } + wk::wk_debug(wk::wkt("POLYGON ((0 0, 0 1, 1 0, 0 0))"), s2_geography_writer()) wk::wk_debug( From ae789f5c4813bdca73177222705ad083a3b3104a Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 9 Apr 2022 15:02:38 -0300 Subject: [PATCH 67/88] maybe fix linestring construction --- src/s2-geography/constructor.hpp | 63 ++++++++++++++++++++++++++++---- tests/testthat/test-wk-utils.R | 9 +---- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index d6bbcc5d..5f822f59 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -104,6 +104,8 @@ class PointConstructor: public Constructor { class PolylineConstructor: public Constructor { public: + PolylineConstructor(const Options& options): options_(options), is_linestring_(false) {} + void geom_start(util::GeometryType geometry_type, int64_t size) { if (geometry_type == util::GeometryType::MULTILINESTRING || geometry_type == util::GeometryType::GEOMETRYCOLLECTION) { @@ -122,7 +124,16 @@ class PolylineConstructor: public Constructor { void geom_end() { if (is_linestring_) { - auto polyline = absl::make_unique(std::move(points_)); + auto polyline = absl::make_unique(); + polyline->Init(std::move(points_)); + + if (options_.check()) { + polyline->FindValidationError(&error_); + if (!error_.ok()) { + throw S2GeographyException(error_.text()); + } + } + polylines_.push_back(std::move(polyline)); points_.clear(); is_linestring_ = false; @@ -136,8 +147,10 @@ class PolylineConstructor: public Constructor { } private: + Options options_; bool is_linestring_; std::vector> polylines_; + S2Error error_; }; class PolygonConstructor: public Constructor { @@ -145,14 +158,35 @@ class PolygonConstructor: public Constructor { PolygonConstructor(const Options& options): options_(options) {} void ring_start(int32_t size) { - loops_.clear(); + points_.clear(); if (size > 0) { - loops_.reserve(size); + points_.reserve(size); } } void ring_end() { - auto loop = absl::make_unique(std::move(points_)); + if (points_.size() == 0) { + return; + } + + // S2Loop is open instead of closed + points_.pop_back(); + auto loop = absl::make_unique(); + loop->set_s2debug_override(S2Debug::DISABLE); + loop->Init(std::move(points_)); + + if (!options_.oriented()) { + loop->Normalize(); + } + + if (options_.check() && !loop->IsValid()) { + std::stringstream err; + err << "Loop " << (loops_.size()) << " is not valid: "; + loop->FindValidationError(&error_); + err << error_.text(); + throw S2GeographyException(err.str()); + } + loops_.push_back(std::move(loop)); points_.clear(); } @@ -161,12 +195,20 @@ class PolygonConstructor: public Constructor { auto polygon = absl::make_unique(); polygon->set_s2debug_override(S2Debug::DISABLE); if (options_.oriented()) { - polygon->InitOriented(std::move(loops_)); + polygon->InitNested(std::move(loops_)); } else { polygon->InitNested(std::move(loops_)); } loops_.clear(); + + if (options_.check()) { + polygon->FindValidationError(&error_); + if (!error_.ok()) { + throw S2GeographyException(error_.text()); + } + } + auto result = absl::make_unique(std::move(polygon)); return std::unique_ptr(result.release()); } @@ -174,12 +216,15 @@ class PolygonConstructor: public Constructor { private: std::vector> loops_; Options options_; + S2Error error_; }; class CollectionConstructor: public Constructor { public: CollectionConstructor(const Options& options = Options()): - options_(options), polygon_constructor_(options), + options_(options), + polyline_constructor_(options), + polygon_constructor_(options), collection_constructor_(nullptr), level_(0) {} @@ -235,12 +280,14 @@ class CollectionConstructor: public Constructor { virtual void geom_end() { level_--; + if (level_ >= 1) { + active_constructor_->geom_end(); + } + if (level_ == 1) { auto feature = active_constructor_->finish(); features_.push_back(std::move(feature)); active_constructor_ = nullptr; - } else if (level_ > 1) { - active_constructor_->geom_end(); } } diff --git a/tests/testthat/test-wk-utils.R b/tests/testthat/test-wk-utils.R index 35b49292..4f3b1531 100644 --- a/tests/testthat/test-wk-utils.R +++ b/tests/testthat/test-wk-utils.R @@ -186,7 +186,7 @@ test_that("mercator projection works", { }) test_that("the s2_geography_writer() works", { - for (name in names(geoarrow::geoarrow_example_wkt["multipolygon"])) { + for (name in names(geoarrow::geoarrow_example_wkt)) { cat(paste0(name, "\n")) geog <- wk::wk_handle( geoarrow::geoarrow_example_wkt[[name]], @@ -198,11 +198,4 @@ test_that("the s2_geography_writer() works", { wk::wk_coords(geoarrow::geoarrow_example_wkt[[name]])[c("x", "y")] ) } - - wk::wk_debug(wk::wkt("POLYGON ((0 0, 0 1, 1 0, 0 0))"), s2_geography_writer()) - - wk::wk_debug( - wk::wkt("GEOMETRYCOLLECTION(POLYGON ((0 0, 0 1, 1 0, 0 0)))"), - s2_geography_writer() - ) }) From 7dbc2b6867ed1ed61bcb6043a64a58b8a11b6356 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sat, 9 Apr 2022 22:24:41 -0300 Subject: [PATCH 68/88] better linestrings maybe but still segfaulting --- src/s2-geography/constructor.hpp | 42 ++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index 5f822f59..cea30252 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -104,26 +104,24 @@ class PointConstructor: public Constructor { class PolylineConstructor: public Constructor { public: - PolylineConstructor(const Options& options): options_(options), is_linestring_(false) {} + PolylineConstructor(const Options& options): options_(options) {} void geom_start(util::GeometryType geometry_type, int64_t size) { - if (geometry_type == util::GeometryType::MULTILINESTRING || - geometry_type == util::GeometryType::GEOMETRYCOLLECTION) { - is_linestring_ = false; - } else if (geometry_type == util::GeometryType::LINESTRING || - size == 0) { - if (size > 0) { - points_.reserve(size); - } - is_linestring_ = true; - } else { + if (size != 0 && + geometry_type != util::GeometryType::LINESTRING && + geometry_type != util::GeometryType::MULTILINESTRING && + geometry_type != util::GeometryType::GEOMETRYCOLLECTION) { throw S2GeographyException( "PolylineConstructor input must be empty, linestring, multilinestring, or collection"); } + + if (size > 0 && geometry_type == util::GeometryType::LINESTRING) { + points_.reserve(size); + } } void geom_end() { - if (is_linestring_) { + if (points_.size() > 0) { auto polyline = absl::make_unique(); polyline->Init(std::move(points_)); @@ -136,19 +134,24 @@ class PolylineConstructor: public Constructor { polylines_.push_back(std::move(polyline)); points_.clear(); - is_linestring_ = false; } } std::unique_ptr finish() { - auto result = absl::make_unique(std::move(polylines_)); - polylines_.clear(); + std::unique_ptr result; + + if (polylines_.size() > 0) { + result = absl::make_unique(std::move(polylines_)); + polylines_.clear(); + } else { + result = absl::make_unique(); + } + return std::unique_ptr(result.release()); } private: Options options_; - bool is_linestring_; std::vector> polylines_; S2Error error_; }; @@ -230,7 +233,6 @@ class CollectionConstructor: public Constructor { void geom_start(util::GeometryType geometry_type, int64_t size) { level_++; - if (level_ == 1 && geometry_type == util::GeometryType::GEOMETRYCOLLECTION) { active_constructor_ = nullptr; return; @@ -284,7 +286,7 @@ class CollectionConstructor: public Constructor { active_constructor_->geom_end(); } - if (level_ == 1) { + if (level_ == 0) { auto feature = active_constructor_->finish(); features_.push_back(std::move(feature)); active_constructor_ = nullptr; @@ -329,6 +331,10 @@ class VectorConstructor: public CollectionConstructor { return absl::make_unique(); } else { std::unique_ptr feature = std::move(features_.back()); + if (feature.get() == nullptr) { + throw S2GeographyException("finish_feature() generated nullptr"); + } + features_.pop_back(); return feature; } From 6c2fd456982b4933edf0d32e977de91043e8c7e4 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 10 Apr 2022 15:10:46 -0300 Subject: [PATCH 69/88] fix export of truly empty (multi)linestring --- src/polyline-geography.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/polyline-geography.h b/src/polyline-geography.h index 5d3c18f8..d9c08942 100644 --- a/src/polyline-geography.h +++ b/src/polyline-geography.h @@ -185,7 +185,7 @@ class PolylineGeography: public Geography { // export empty linestring WKGeometryMeta meta(WKGeometryType::LineString, false, false, false); meta.hasSize = true; - meta.size = this->polylines[0]->num_vertices(); + meta.size = 0; handler->nextGeometryStart(meta, partId); handler->nextGeometryEnd(meta, partId); } From f1bcd098da2a57c0a4d7b06aba2a681e9111ba8b Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 11 Apr 2022 22:27:50 -0300 Subject: [PATCH 70/88] add the example wkt as package data --- R/data.R | 7 ++ data-raw/test-geom.R | 164 +++++++++++++++++++++++++++++++ data/s2_data_example_wkt.rda | Bin 0 -> 24540 bytes man/s2_data_example_wkt.Rd | 17 ++++ src/s2-geography/constructor.hpp | 4 +- tests/testthat/test-wk-utils.R | 4 +- 6 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 data-raw/test-geom.R create mode 100644 data/s2_data_example_wkt.rda create mode 100644 man/s2_data_example_wkt.Rd diff --git a/R/data.R b/R/data.R index 9bd06bb1..23ee0ea1 100644 --- a/R/data.R +++ b/R/data.R @@ -70,3 +70,10 @@ s2_data_cities <- function(name = NULL) { as_s2_geography(wkb) } + +#' Example Geometries +#' +#' These geometries are toy examples useful for testing various coordinate +#' shuffling operations in the s2 package. +#' +"s2_data_example_wkt" diff --git a/data-raw/test-geom.R b/data-raw/test-geom.R new file mode 100644 index 00000000..b8b0c64f --- /dev/null +++ b/data-raw/test-geom.R @@ -0,0 +1,164 @@ + +nc_wkt <- wk::as_wkt(sf::read_sf(system.file("shape/nc.shp", package = "sf"))) +wk::wk_crs(nc_wkt) <- wk::wk_crs_proj_definition(wk::wk_crs(nc_wkt), verbose = TRUE) + +geoarrow_example_wkt <- list( + nc = nc_wkt, + point = c("POINT (30 10)", "POINT EMPTY", NA), + linestring = c( + "LINESTRING (30 10, 10 30, 40 40)", "LINESTRING EMPTY", + NA + ), + polygon = c( + "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))", + "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))", + "POLYGON EMPTY", NA + ), + multipoint = c( + "MULTIPOINT ((30 10))", + "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))", "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))", + "MULTIPOINT EMPTY", NA + ), + multilinestring = c( + "MULTILINESTRING ((30 10, 10 30, 40 40))", + "MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))", + "MULTILINESTRING EMPTY", NA + ), + multipolygon = c( + "MULTIPOLYGON (((30 10, 40 40, 20 40, 10 20, 30 10)))", + "MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))", + "MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))", + "MULTIPOLYGON EMPTY", NA + ), + geometrycollection = c( + "GEOMETRYCOLLECTION (POINT (30 10))", + "GEOMETRYCOLLECTION (LINESTRING (30 10, 10 30, 40 40))", "GEOMETRYCOLLECTION (POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10)))", + "GEOMETRYCOLLECTION (POINT (30 10), LINESTRING (30 10, 10 30, 40 40), POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10)))", + "GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (30 10)))", "GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (LINESTRING (30 10, 10 30, 40 40)))", + "GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))))", + "GEOMETRYCOLLECTION EMPTY", NA + ), + point_z = c( + "POINT Z (30 10 40)", + "POINT Z EMPTY", NA + ), + linestring_z = c( + "LINESTRING Z (30 10 40, 10 30 40, 40 40 80)", + "LINESTRING EMPTY", NA + ), + polygon_z = c( + "POLYGON Z ((30 10 40, 40 40 80, 20 40 60, 10 20 30, 30 10 40))", + "POLYGON Z ((35 10 45, 45 45 90, 15 40 55, 10 20 30, 35 10 45), (20 30 50, 35 35 70, 30 20 50, 20 30 50))", + "POLYGON EMPTY", NA + ), + multipoint_z = c( + "MULTIPOINT Z ((30 10 40))", + "MULTIPOINT Z ((10 40 50), (40 30 70), (20 20 40), (30 10 40))", + "MULTIPOINT Z ((10 40 50), (40 30 70), (20 20 40), (30 10 40))", + "MULTIPOINT EMPTY", NA + ), + multilinestring_z = c( + "MULTILINESTRING Z ((30 10 40, 10 30 40, 40 40 80))", + "MULTILINESTRING Z ((10 10 20, 20 20 40, 10 40 50), (40 40 80, 30 30 60, 40 20 60, 30 10 40))", + "MULTILINESTRING EMPTY", NA + ), + multipolygon_z = c( + "MULTIPOLYGON Z (((30 10 40, 40 40 80, 20 40 60, 10 20 30, 30 10 40)))", + "MULTIPOLYGON Z (((30 20 50, 45 40 85, 10 40 50, 30 20 50)), ((15 5 20, 40 10 50, 10 20 30, 5 10 15, 15 5 20)))", + "MULTIPOLYGON Z (((40 40 80, 20 45 65, 45 30 75, 40 40 80)), ((20 35 55, 10 30 40, 10 10 20, 30 5 35, 45 20 65, 20 35 55), (30 20 50, 20 15 35, 20 25 45, 30 20 50)))", + "MULTIPOLYGON EMPTY", NA + ), + geometrycollection_z = c( + "GEOMETRYCOLLECTION Z (POINT Z (30 10 40))", + "GEOMETRYCOLLECTION Z (LINESTRING Z (30 10 40, 10 30 40, 40 40 80))", + "GEOMETRYCOLLECTION Z (POLYGON Z ((30 10 40, 40 40 80, 20 40 60, 10 20 30, 30 10 40)))", + "GEOMETRYCOLLECTION Z (POINT Z (30 10 40), LINESTRING Z (30 10 40, 10 30 40, 40 40 80), POLYGON Z ((30 10 40, 40 40 80, 20 40 60, 10 20 30, 30 10 40)))", + "GEOMETRYCOLLECTION Z (GEOMETRYCOLLECTION Z (POINT Z (30 10 40)))", + "GEOMETRYCOLLECTION Z (GEOMETRYCOLLECTION Z (LINESTRING Z (30 10 40, 10 30 40, 40 40 80)))", + "GEOMETRYCOLLECTION Z (GEOMETRYCOLLECTION Z (POLYGON Z ((30 10 40, 40 40 80, 20 40 60, 10 20 30, 30 10 40))))", + "GEOMETRYCOLLECTION EMPTY", NA + ), + point_m = c( + "POINT M (30 10 300)", + "POINT M EMPTY", NA + ), linestring_m = c( + "LINESTRING M (30 10 300, 10 30 300, 40 40 1600)", + "LINESTRING EMPTY", NA + ), + polygon_m = c( + "POLYGON M ((30 10 300, 40 40 1600, 20 40 800, 10 20 200, 30 10 300))", + "POLYGON M ((35 10 350, 45 45 2025, 15 40 600, 10 20 200, 35 10 350), (20 30 600, 35 35 1225, 30 20 600, 20 30 600))", + "POLYGON EMPTY", NA + ), + multipoint_m = c( + "MULTIPOINT M ((30 10 300))", + "MULTIPOINT M ((10 40 400), (40 30 1200), (20 20 400), (30 10 300))", + "MULTIPOINT M ((10 40 400), (40 30 1200), (20 20 400), (30 10 300))", + "MULTIPOINT EMPTY", NA + ), + multilinestring_m = c( + "MULTILINESTRING M ((30 10 300, 10 30 300, 40 40 1600))", + "MULTILINESTRING M ((10 10 100, 20 20 400, 10 40 400), (40 40 1600, 30 30 900, 40 20 800, 30 10 300))", + "MULTILINESTRING EMPTY", NA + ), + multipolygon_m = c( + "MULTIPOLYGON M (((30 10 300, 40 40 1600, 20 40 800, 10 20 200, 30 10 300)))", + "MULTIPOLYGON M (((30 20 600, 45 40 1800, 10 40 400, 30 20 600)), ((15 5 75, 40 10 400, 10 20 200, 5 10 50, 15 5 75)))", + "MULTIPOLYGON M (((40 40 1600, 20 45 900, 45 30 1350, 40 40 1600)), ((20 35 700, 10 30 300, 10 10 100, 30 5 150, 45 20 900, 20 35 700), (30 20 600, 20 15 300, 20 25 500, 30 20 600)))", + "MULTIPOLYGON EMPTY", NA + ), + geometrycollection_m = c( + "GEOMETRYCOLLECTION M (POINT M (30 10 300))", + "GEOMETRYCOLLECTION M (LINESTRING M (30 10 300, 10 30 300, 40 40 1600))", + "GEOMETRYCOLLECTION M (POLYGON M ((30 10 300, 40 40 1600, 20 40 800, 10 20 200, 30 10 300)))", + "GEOMETRYCOLLECTION M (POINT M (30 10 300), LINESTRING M (30 10 300, 10 30 300, 40 40 1600), POLYGON M ((30 10 300, 40 40 1600, 20 40 800, 10 20 200, 30 10 300)))", + "GEOMETRYCOLLECTION M (GEOMETRYCOLLECTION M (POINT M (30 10 300)))", + "GEOMETRYCOLLECTION M (GEOMETRYCOLLECTION M (LINESTRING M (30 10 300, 10 30 300, 40 40 1600)))", + "GEOMETRYCOLLECTION M (GEOMETRYCOLLECTION M (POLYGON M ((30 10 300, 40 40 1600, 20 40 800, 10 20 200, 30 10 300))))", + "GEOMETRYCOLLECTION EMPTY", NA + ), + point_zm = c( + "POINT ZM (30 10 40 300)", + "POINT ZM EMPTY", NA + ), + linestring_zm = c( + "LINESTRING ZM (30 10 40 300, 10 30 40 300, 40 40 80 1600)", + "LINESTRING EMPTY", NA + ), + polygon_zm = c( + "POLYGON ZM ((30 10 40 300, 40 40 80 1600, 20 40 60 800, 10 20 30 200, 30 10 40 300))", + "POLYGON ZM ((35 10 45 350, 45 45 90 2025, 15 40 55 600, 10 20 30 200, 35 10 45 350), (20 30 50 600, 35 35 70 1225, 30 20 50 600, 20 30 50 600))", + "POLYGON EMPTY", NA + ), + multipoint_zm = c( + "MULTIPOINT ZM ((30 10 40 300))", + "MULTIPOINT ZM ((10 40 50 400), (40 30 70 1200), (20 20 40 400), (30 10 40 300))", + "MULTIPOINT ZM ((10 40 50 400), (40 30 70 1200), (20 20 40 400), (30 10 40 300))", + "MULTIPOINT EMPTY", NA + ), + multilinestring_zm = c( + "MULTILINESTRING ZM ((30 10 40 300, 10 30 40 300, 40 40 80 1600))", + "MULTILINESTRING ZM ((10 10 20 100, 20 20 40 400, 10 40 50 400), (40 40 80 1600, 30 30 60 900, 40 20 60 800, 30 10 40 300))", + "MULTILINESTRING EMPTY", NA + ), + multipolygon_zm = c( + "MULTIPOLYGON ZM (((30 10 40 300, 40 40 80 1600, 20 40 60 800, 10 20 30 200, 30 10 40 300)))", + "MULTIPOLYGON ZM (((30 20 50 600, 45 40 85 1800, 10 40 50 400, 30 20 50 600)), ((15 5 20 75, 40 10 50 400, 10 20 30 200, 5 10 15 50, 15 5 20 75)))", + "MULTIPOLYGON ZM (((40 40 80 1600, 20 45 65 900, 45 30 75 1350, 40 40 80 1600)), ((20 35 55 700, 10 30 40 300, 10 10 20 100, 30 5 35 150, 45 20 65 900, 20 35 55 700), (30 20 50 600, 20 15 35 300, 20 25 45 500, 30 20 50 600)))", + "MULTIPOLYGON EMPTY", NA + ), + geometrycollection_zm = c( + "GEOMETRYCOLLECTION ZM (POINT ZM (30 10 40 300))", + "GEOMETRYCOLLECTION ZM (LINESTRING ZM (30 10 40 300, 10 30 40 300, 40 40 80 1600))", + "GEOMETRYCOLLECTION ZM (POLYGON ZM ((30 10 40 300, 40 40 80 1600, 20 40 60 800, 10 20 30 200, 30 10 40 300)))", + "GEOMETRYCOLLECTION ZM (POINT ZM (30 10 40 300), LINESTRING ZM (30 10 40 300, 10 30 40 300, 40 40 80 1600), POLYGON ZM ((30 10 40 300, 40 40 80 1600, 20 40 60 800, 10 20 30 200, 30 10 40 300)))", + "GEOMETRYCOLLECTION ZM (GEOMETRYCOLLECTION ZM (POINT ZM (30 10 40 300)))", + "GEOMETRYCOLLECTION ZM (GEOMETRYCOLLECTION ZM (LINESTRING ZM (30 10 40 300, 10 30 40 300, 40 40 80 1600)))", + "GEOMETRYCOLLECTION ZM (GEOMETRYCOLLECTION ZM (POLYGON ZM ((30 10 40 300, 40 40 80 1600, 20 40 60 800, 10 20 30 200, 30 10 40 300))))", + "GEOMETRYCOLLECTION EMPTY", NA + ) +) + +s2_data_example_wkt <- lapply(geoarrow_example_wkt, wk::wkt, geodesic = TRUE) + +usethis::use_data(s2_data_example_wkt, overwrite = TRUE) diff --git a/data/s2_data_example_wkt.rda b/data/s2_data_example_wkt.rda new file mode 100644 index 0000000000000000000000000000000000000000..23820bbf6ab0d921ca0cf8cf64446a12b1047813 GIT binary patch literal 24540 zcmX`S30RWZ|32KesTnJ?ur&7)wGx3`(9EnU!wr{GSp>zxLzwDD&pG$~`P`2e zF8+kmS?u93EN}1ajV-^!|GSy~{rkm#|EvA^xBvdz{_f?6K)rXz58n$-yRhxniRkaR zfIph&PF?FH?EZ7hX8O;X=iToAt!(*E*B1BPTW`g0*}diHmaVt8+@e+Q^xI#18(IGg z=vTvExA^V%_rA34chq)Uzx@DvkfH5`mMvRDYx69ANdDyvcV`H&WZRk09RIU=4f|P_ ze2yZ{{EFIPc(nE`V*9p?k{w6ewmkQ{xAWdnqtKoE{r2zJnqz;aR{!(Dt37tnF7*ACCcV*Eehp-R!AteY0)tnb4ZAbhpeyhS}byTu;?c zjGR#&Y5s7_t!^S-KWNW|_${6n#u_JnA6xnE_g@;vvRmrL4|2_{YFLz07s&T-8-Z%- zxBUKpzRmxeIsc!+Fut~EC&j)H72@U2_-=pDFzNQ`{- zyYbOOq`6!AXZHMH28|p;pZVpYPqrV%-S}3>J$&^RTjO*8G~W1c?$`BrbFKV8G(XpA^*_b_ z?D<$Ej0;OVBrv?W)bpSWygk>pYKPI*nlIydH6}*ILq3=1GIqQ>-zyPAKHs{0Gk@pJ zvHx)l3>wWch{||+E3YE2y3S+&cS7OA5`jbY#**=;xduCVWTU$lyWRhILD>f|zk7@MpF_8TEFu3H>H#4i ztzYsxyW{q*15gruI!5~T{xvMIid-~!fRL%d%5(bHi}l3;wS&6^8d^gsHE*#qg7U}( zgKHmkQvj!6_-p!(vpW4rULpasL2MJKTPv{XW2JxyO6*Mh=nN(9KvJ7@)NTICNvh~o zCRw*`oIF7$HCMskIg`0A>sK1OvEc00nfC&bkk0d0#qF)W~TaNHCjSCbgY<5HtYaQIs zBQUMrZ{}B(f$_4$Hib?UCCVj<%K}=7oB&bmuv-93M5R?k=DDdCUr^`?L8ulHc3`hE zv7&2-p4MQj|4UFIbC2f(7rBGs=G+^PL)Qe)Mns4AifkxSe=jvlU5IY~OE zDyXr5dPJTZP9ilH>r8|w3HnMB#Eg0yHAYtH^++c(O_#zbuTo-GiE$RKZSqz@T&^3* z9Co)NP9Fm5>QA6uHo#_F$t~q|;fWgQadf1>S+yIVDCjeO*o$!+KhEzO4Ix(bl8A5d zS;oRZmHCiOE^NqxaNuqQmiB@VQ!85|vA5r_xg)p^3J95~iQA2r`q&=`AECs=`5Xx7 z41_UBG&S|^O!y-WiB=}_3(u?l_GmZ1^>}gMfm{+Tk3RjP&c;11hAx@>*I5RFyWi&H(Xc{Z&(CiD*8s(-4f4BKtK zbHDwr+aq-Zue;XR#KfZ)>W`iJ^`C89uY3s&07bZ+3kwSi1^&kjf(`h;7lru8DpEZZlpF7-amwfv2Xu9{)x9i>Qg_QL5im=)K=qb0q zr`;;j5|p0Urv>7=!6RgH(97cd*ypnwH{W?ISEYEo9mu*Jl8t0}-KS#gqn_PdDwSZq z@-Mm=21flmcYHmdhA05dPu0!n$pX7NA}<^b80J{{e@=-#QPi>VLemy0hBfoks@9DDUSnTHuk=uLHpxjfyGV+Klmqj9rHE!O-+BDaFwrB$4AnW zyEv8e*@?redoEja5_cM=woIpgqAGG4^G;JND_9rc8S!t`&JNRUA18dt2w7DKM%MXY zSWxOW?HoIB!s6^gWo|AxHfE3)hJ6;5QC|}6y*r|IYp34w9&12ExiD!2_t%SHA}}o= zD5&m|KhpIY&fer#ZVeGzz9+1jxjuET^>f*cyVXNvVDs;gNn-W6kx)=Koz%RV- zPo52n&1*`rUr7CchE2S2+1qVs=;POUSu$$cwwP3Gb098R)nv<~H;lha*3|nw9L=?= zo}8n%UGKbFH8(kSt`IcXtC&fyUMM#vT`#XdH$1Yg+*kE0tfpWlc!>BGyR&L3?6UCC zv92WApZA;o?Gm(KS1%5oTKwr%(#bN%K)$RHy~7_-){6)4Pb2_!EbxuCtb1U%!Hp z&sJPH5P0q3=?6}NZC8W39}_=#_SOQLFI9I}_j|c@4(86DvVYxiXZ*C;zO3Oxj~KOp z?i&eKM_JMBrb_XG-t)b_gxF3K=UtS{7*m-!Hrs~4zmnpq_ea-RnRp~xe_*U*Z`sw< z@EZ?k(vaGZDYE*dy5N|E*v@I?u@FnzYRji2n=&^a@)z%7jKJ+0O{>pZn=noCTzWF!e>#8`q1lGb!Y*asN zK02XraIQp8J`8VE^i}Sk@tMr`afrRJKUy*q*f@hNiRBZWAD(HPyc_##<46RW9NRc_ zsZ~s2Ka7A0iG+xqiLLdMITUP53DF5BnG|0jKX*XS;3|o+6T__$u4a;n8J__A+m+Fx ze6}DHJrY6gt8^BU38=gxNu#TqgV}GD+R4uM-<};Dk-pp`7=Gj|I6VrUcgS=Xe7ji{ z#S0d7r}d%8CRpjYau-yH#GW|am>a^(OIodc(!gsw{)>1T%kV2OCQT=6cVjG7IrHv! zs~*Z6GZRSIh7a;CSps^&i$W|iCpGADT_TjGRMwMLRLmmiEyi%ImjBw=%OwEzsFXz1 zG5bPjE}!r^r{n*gi^Eag8Wk@`m3#!P9G??Od+0^YGe zsWhcCa_Gq;&dOI`JdvisErnz{nPQEprKYsn0ekvXBm|i1k2%(8V4Va zGGKf{VH7)sS`$}e6)VHg`GtzyV`yssuv-C878$M>=~CU@$A5qrnt5aY`1QHk5YMd4 z2O$+X;||;Gii~OY{ll<4$3B@c-oa=v(AW;I7w9ps>!juNIk=GXX|qv{#TWdmpVZnsIio}*%I$~oWWM7W?89RSuO zh-7@p%m63a4NF!VpF@h#`LpRHcBOZ%IduLFlN4J@xy#!n9r>g&aAebRF@V^6<7R}& z1T;Pt^afjurI`9+b8;OY#|E{{-Ym@Z4J;H_0gB42fiq4dj(~r->&JRmg)E85QfdbN90nd`sKhrP+#nTFe;m(=v zvb?DT`&i?oj`25YWqSlR*=;07Sq0Mu&Gy^5jrFU&DO09|dXkuJPo^j|+h^c>C=V=l zd}J;UypZn^I{4K7kHD*h-^UAc5KjG&2}))uk;Z}Mcl2$Vh{}5b-bNG;I}48z4)`2L zLw~yrc2`W&DhL@qnCLQMDv_RRC?L_YbH{a~`QrwD)7Y20@SZajASfDdLC)fl)(|!U8o6Me=#{k0N zx?B!Ipe(Il8DMcg#Rv}&Qs*5tNsb|se~d^amPTxgs&LE|;*T+_m*bgs;N?3e zLP53bzW@1@qVGg*UQq^t6*^z?OhFyM49uNLTRD*ua(4#2GC4qJr#4w)EBYpsJkcc{ zSg3bA4ow(9s34?FJdOTWsXSl-5RPbr41`12Wu3-ygKAO%Cq+v)%XSy)h202fCGLC_crD=KYYe<#8Fm~pO1pV}F zQ+O{vHM${|FW4|nmdsKIs#}Zb)L<09nnDi(PAEC(Pb+lp7a%0`HA@zxmSHZ&3-t${ zu2}l`$r(}Yc(12CsZwY*NTl)LZHN$ zhN1yAEz6~$lxWi%IYQ>o$YY5xfY0J=1JgFL?`S&gut z%*0Zs0AdcUL7_>UoXJ0qM_0rMCI?Nh{rr5p;}K3Fqiq6blKeQV(j}>T;LkR@m`u~w zw%7;J$0aWaF^?yiI-}`uWwS28F-Ie&4Tg)=6#DSnIr~m*KYY_z@psysdnE}2ak(0wz(hPMdDZVb))i7C{*Ng$BvGA$Ne37hc7+OH0dxj z$smw-*4{xJh>v&Qd}d(JnQnF0Sk(9IE~5aafYu_3aaHqTfjD4WDl45(5g~iC@>NxX zd`F^yA9(Xuy7xd;Jq`B3n(`n>yM1Pv@2@_2X8%*?Wp$=+hflVLa-PMI5AM+Z0tO$w zcw|h~dyKpoXK$}SkXhruw|*!`saGp1Ie8yO=0@f|zwU9`UxW)?nRI;Ydf?Gvx1*yL zkBTA49DYGz)9YD};N=80N8;pfpXi_Y96RA)USG2ITAm125rInSKQ2$Mjt|Uj*y#p~ z4DCoimEs5Nm%^R0c=jQn25;^gl4AZ%^XOD0Td`snlhLXotu4=eOX+fF7~*Vk{)aa- zMCJP?6UHQ|7*!euUXVtKX1{5kSsT_jPhLiqitFb4liLF7QqS*-k^12ZOe@sSzh0sD0j7@W#`R(-9J2Y%k$74 ziAZB{ZP;j%x&X$U#gw0u`W85Tm8HJtC`k~`Lif$ozkZ7=H#7q5SmKUO%`slmM26y& zV2j9^#pxJLU`HYyus7FndF@zG-7qHs0$=bPw;2TOU3KnO*!tT}jfkNgY z#wfPD;Ag*ewmp@%gC}WnFus#ig3QT4z?)kje^wn03d0)u7m*xQGEyV=8OqDL)kB_t zD*P=Q=wQlN;J@4Sj<}Xq;mOY`sJE@_+xNVb(S6l-i%g9i&s=7F7=ILhT< z2Qe=8;TWBJoZ-#y(;z)R40oGmnqX^}qmyTsRZEqn1^4e8MCst!5dOIOumJc3jVki| z_TB-~{Zv_CT{k-6p*-(S@!sqjv9rczj|-kxo}eVY_V?4!>6KhnaGC2XD}1SiCo==V z2AMx32VC1_ma^+4iW5$J9ne1KIBL$Hq=@HKs8o1;bDD7ASp@q|QU#2uYGzh`k@LFL zp2EfJ%IaP{(IJM+`3)}ZVC&*H zt#;7vS;6OYmcK2>ueq(QJ9dUP0f~cs>Cf+`TOh8<;53nM*E6`{v z0Js<76$TbJ}*eqE!U0eZLYOdV9;sH@hUO>MXRU$C|u*} zg5g#3VGQ$Y`&WR#Uo#Mw`6!P2#*0WDVHfHTa+cBK_<+H> zPFn0J98ODDIdsetXqD-F_BLRWV8vy?tPW8G3bi)^7)NcGhc1Rw8yOL^7|wd4$B>>6 z4m!RtqvVU^wAe55Al`1l28}p6FX{)b5P%3a0H_BCYj>3*+uW*&Ptwbp3D9l`+Wb!#t2hA(Pa;-vk zJ5%GjbOlnuTF78Em^eJmlD;^*i?2GW=vZ0hcgLDzW9QxyoATQjZ)r_9>a@WZYZY0=E>}Yi6c7UaIJhB3KP5=C)Ccrc4%5}Hv zMzBo>sjfNO0gU274|C9M-Agf%{Lkqa7R!v47Y8!Kd{4 znDI#X6R-S&=khk%@7BNAnWP+^-HSL}1%y8`_jAAz2o!bKN>fDQY^dB?fBVQ|dQ zAnF0AHqOCaIH;#AbRUxJzpW-n@Q9S78#Zmd!7tO((y*E_T_}mv+Dfd?XcNg3I%1g3 z6yp#8aSkFcosKBX#hQ7+i{HQh_()NrUY+FSBQg;6K(fGRO`*^_@U`5kMU@qzNOB-W zEU9M9@9+y7mgYkRCT%C9M=XH2t^zlWJxWBV{p$k?eztK+$Qlxj!j?NC%j za!U!mwCS14XE%-)eC<_CgbzpL=Mzp^Ni1+zwKDpQ2`1R&#mSd=HFwpP`-jWQsxBEDeuC^n*uW!yk z($Y0r)Q7qzj~jT)`T(=wN1mGFMD&d|K7_-}Mu~N8r@LPs>}{v1Y5|gK*K>OUJYZ940ceraK@h(*7E``bCDw8;rlwd^> zrkewhbc{xL$b7F~to)Q+Kh7Gm;{-(b?`x(k;*b>D@{5NSsqYt_zti``=4f&@tc~(d zJDo3BSXs|Meb9IW_WZs+$b&3=FEMR<_?-3NXbzLrV8izij(VyoE1yUW(WSIfJgzC6 zZ~}*kXREQi>^Uuy(oVY12B14F<3WOhL;j5~3 z%GTeN2wyJ!KB0^z!I63SXk|MVyF%nVZMWVB6)BdRIAoD9HWK-n#{-PMynlSp#rp-3 z|7!Xr;_Mh!iYBPe$BYt-Zadk@swNFMl&rKyD#1j#?Zev};#q7{*;FKzgZD|8pL(;@ z**5V{k3WhepZD+kDOqPpo|?)iiqB0=|FR(`3)V5zxEgrw>=HHd~__?U;A+BR1NbUy>(ANf}cnE;ZwR9J`bw2Kz`2 zki){o$bu9a49U#$b=az1#&DrfbX!;y#>l_?Dk1X}N~ z4>9VW;kU{3=A}aYs-_wwH}K&gc|!P9L&0Fq7s>q9#UtvH?PT%uQ1J-=~t%<@_}~VoZe{efSVH9u|CYX{LIp)rd2l0xqbzO)nhR; zSod3^)iZ931FtJd>f8gk-`8d?oSQ{zvwYeW3T*F1Xd zuID|Rj&hzdX%lIAWJL*Us@#?{)wy)NDcF4Z^xAoVV=ruku`+p%PF3kwMGH}Ny{)X; z_RJ*jyyLjviqu*Q+6z&i^Lr3@wtC(s*99+aKt?nRk2dblZs9;+0XXWAg~h2kcX}b< z+70fILy+58TW?dYA<8$u=eH1HK3^e{XO8n_l-kiwoTgPOyh+u|;KJb#WSgO0soT1H z`kr4lf-^mLob7WSSD3)N;G4&-Q?2b#PJH&A__BV-=z@|UnEr+SZAVM;ItHAAOya{# z8uslkF}xjbjflS;qJOE@0NjVO?RN@{&iww?r(zjLqZ|2DDZ(VA7iK(6V(Ub?%jW`r zBK#D1ZMyb6DE7Dg?@M%%)7zhWt3!XflR|w>_3I72h{rxkD(R(PT5`GRaIuAP`}a$~ z+?lR*PX8X}VlvSaG1oCT2k;+!qmesv)UWDD6FWjD0CAZDgY`4oKYZKa!)3=W? zUeJA~l%v}DYA=Tp_7xkxbPeu_Xlx-0RAN6Y4N>5+Kx(X^C|;@8pPBZCtF1w`_IiA` z5p)$^!!X9xHejx}X$r7Y=2SL@$CnC&4#b+LK5VLpkqrat^aUHfslxb~;wVh09nueR zPvvi0^j??cV2=VoD0lWt2do&^FkYwtH{~c4J5@$aMc6SB{DZ)B3BN1iqWP ze=PdVDr50EPf9Y{7A}b4R2{5I!jtqk692s{c!4NNjJ;AY4)l-**B%CQrog#i{}#P+ zBs^PBz2+$o=HD&H2{r*S(oLst?$P$Ozu0IYz*7sE(RBz>gZU;g&A0VRaxw!r45>V} zNO5Vh%8gf%5XkXHWG~KA;tGyo`kgiBj+hXt(fN!Zpffz%aOQ|yyR`Hy_pnf>V6K;x zPJ-58GCsN*_|`#bA|cK)VH8*^YIovlbv6HrL)HGU&Rw0spT0G;Mk9$} zyjs{$MvJY%b4EZLCaD_83AtW&$lgaYv=wXyqv923?zlbZfjQO0`x3hkT?jPvheV62 zZ>HWan0xf-U?jw@eriIEI<|St{&U}|=kJZbCwgd|<|i|VQ$>5(OG){HSN&Waic04? zk586HUd={%(zF!!RhkL{eaN}zJx4TJ#F9EKS00%8lkdI%xT>8dJ$V@xDWywCP6jx1 zVPB_G0EP{~zf?*><8$E^bZ*BwuJsg`=dro*AQd{}&bLPfeH|#q@QPN(hH`p<_rtx1 zi<1Losj6V}>&;9e2sUfhX6KVPJ{d!(Kosp=rehO72T59Kk&_VxCtf2mu~&i|D`~Zx zP;5s?2)FftR8vS(<#I@>To`GPH`cf^($=k>Y2rcnT7wNEP%Y3#U%)H~9d&1T%(|lP z?50kmugp=VP2YzW0c;y~9L~0n&y5#l@AJ#?@@}=w^#|G$f6N~=A|X}&Hptf67LbiL zktn_N^*qM1iGw!tluz6q?oeryI#OD}3y9A`|>czhf_?r1;sHqxr zpdD>{b{iZf%yVOEAz;mJObBQ=OphHh7)4wn4>+q(^WI*r7)BE+Cka)dYa|UCHa!42 zVTn!I(r0Mw0i6RTA(L$c1clZf&aryrzT~~hSl)km2mbs1MaHm?W%_0);KllpiuO3@ za-(_skN?^vXJr2H1bm~(DeIsqeo0Y+O`ji3`utwmIT&|@^Db`OhdBJI&|}cPtn%#^hGE9g9tCzBH8Ifhl2n<0AaRZX_=c zNuP9x{8;T+o+F`gR<7?TgdCtXnG-9-o*4mr61Q7ebXVB~ClcYj|~fz`np_n(rc zd?c)=ENW-ZKUZ(8@>xNK5yeeEUBS!XT-6>Tz2iN|CeW-Mdi$;Whv8|MR&1KkgfeX% z-vk}Ao6aUQ?xbq@y=G0qvbJl;5a4i*FU^v8Bd3bX6*d$PU(0Bo-q>tdp-1NCG@Cr6 zo0AsRl&L)i9z%+OLVM8Ra7HM1WWn<8xIzWp`?`mFeC|mWTSGrSfZ?M)FLR1z& z@mOL>u}yxLy8ihX-rZ~HK{zW$>;UE<9Ng19UwqY!TJ@$Dzj|dmt|Z=L3c^d?ucxO> zF|+rJOAES_B`gqf@1r@GYz~2K^qfsD)jegtpR7u)q8S17GG|Bp=?2`V^`p>2JG$z_5bN z0MYfblLJ%H1C@-?CAl_PIN=Ymj?X^KZGew8YVZ{_>HKhB?Qtyiu1+NXU9LpX1}*3; zmVVvtv1)V_x4;EWKwm5LASFKwQ}%oYJ+MQdS#SyKX8NDYKgLCr%3cm1`cThh)#0N( zRzf|Z>-dYkTD@lP!1w9OTr`d^>07lzUIV|oQaDiLfJ2Ytp8gcMpEK?cfi%=hKEE3H z_T}B@fbl_-c)>t9Svc6Go7C^=yY90Bva@l$#$AIg17dYhPuzI}2vAkBku61CRS{m-|V|HV%p@@|2S{n=)7#|->*=^N0 zPRtw?$bHEho6glpMq;VEkXVguv7+4QB9&0mXUs{Q8>vd33l8X^dv-1p6uP;ftgRvo zFb>`LMJr;bo2Geru5k4J2~V0}4CoL7+_Ye^a}dhdk6ohPh)Z||D&)e*hWt~5+ zR(ent-_diZGRDV(e{MuX_!WEepN;mo}rxHL3j= zJVRXL%ZV+J=DT%9@XT6JF*N-t9JbNG!L{B|vx}ejF1i8PoWsmvvcW{Vw-O5~nP-+WoNwb*07cE~(QZxzvJMcXZ>QftlF+j@1YQ4`s7&)s*`3 zo)dmdcmwHp$mOGt%~i*)%JVUn^wN_J)XW~a(Wo91j?n(#7TqOQd#r1u1^`8RR3G_ z8WIloBg(m*7c{w^_@=Vc?(6~Vt5{NhIo~c^b{$x22QMipFy@;!RWi^%rC~u5&3Nl@ zXGH3Te<++;;ztrXc)T>beBSxy&FdTfGn$F_34@hYtWe1XPKB+gu;4x?Wukmv@E@Zy zJ{l*S6Fym>==$LPXc!4;yb7vBpttLRjBu9aEX$yiR7Z8i0ni zJ_{=Cd*G8X+wpOre@rvVEbNQidG68a_*YHIYv+&0_=F>gAMP7i-k{b69V?B3BSYU& z`fGfn;>TCJ`qsFU9FUs0l)g%PXX4SF46sAM!Pz8#uvqlZdZ|Z6(0=P}{yLkugrtEG z`7p~<$%n~BFB{HnOLsx7+yVD^2yZ~!ZP<$jp1mu>lIfzH@m@PP^;)5(bo$sugZlCD zexNEyD-((h%@d#=h1i8?gipRi7@oSklrRJ`yT0RE;r&p5+uC0)`hCUSXm1MH_}G1Y z0R)znFvaP0d8P53uU$XyIry}~-yd+eq;{~qr+WxMYH4-g z5zQoGi(Y+g2*84ovr3#Q8-HgAm7n%&%mMRkzPOlfeEoJ%KG`XEHTt34Lkl_P#$G>G z(4!-AGFni^S5R>vq6I~QA2s7)P6Y!|)9^&$Nt(qq95F5z2mm!8y5+tQucddd;> zuubl!wRh-vj`=_(V?|5}AQ$I1*Nc z3Yu@unDnW>QZSWZR1%{N)OJ&I+9<}-q`jmwgya$!$}sd=AJ6MA7v?fuPQD4p#=-$ za5w=m+08*DkZ7XCmdUm^lxlmi>UAaI4uCkB!w&U7Pi?5yH8Z0+72^ZX`$lI$)%pE;aT@tNt6-w?}h5ZJ&z{D0q!;tG>r$r`~lRQbKRklqqnRY;gYw~21 zCqg^e_GvvLfaL7jSiA(KDgySw?QBJ?O_vlj~5Xr`CC=I{>uE-5{Ke2+$AdzF-v-(nj>bB z3;2dPe1($B4l#A#AdTyUc+K|AY=;Cwsa0|X3+dGW)fS#eTUD#jqtki;xG6vXjZEX2 z>A=;d8`%eZ%8#%7%^6@xzFhD*Tzg@-5%$m?Zni(t>&4;`OS?X&=BT$njPQHM6Aef-mCu_8Picjl2o1x|I4Z=-P=UimcfFp#rzqu#I@k zprL0r{lsklJ7gOp@$uL`VYd}}sj9Vy5ye$>{(2;xn(p32wEADSBBq^km0Ml+*vZ$c zhi!5Aq&v`Q^Z$gM056Hjzd`qhtywb=){Nazn1-~ZnddFH3-x|ZK^M?Z!n%DsM)`i?-(xJhk(kgf}g2=A&t-4~-SOj#6_=*0d z>PXz+zO06U*-jm)ekvPcX7w%ouNyZwBvP~=lZVa=Ux>(s%5l^T*jQ(z8^3jNpD!M} zq7L=n^pufht<-mzO1ueGAsf6blACSH%K9eBotE(Z0-zcm{mM*15S&vAUjTodGxZNzu}WJ$@aJf5b+Fw_J^c4)2Z zXvfBL9ETzbCD8XcG8S})i*m!bML^av+O5~=JoG&W+qz1JBvJG<6qqXVC7I86`;*20 z_jW|yD|uGo#lh78e>-x69pg_$$GZwUT+b@CNx>6P242wr`r;=E+GQItqOGpt!*kBTFJ_VetCbTMPFtR1u4abUL9&P^UkE9DC0Nhc$?89+p1g*KSup$QZQS=4=R zn%tus*=RBl^mQc*OV1;b_3-Aw@^;wBRc#*BI@T1g+EtmT8*Y`%H8YurP^RD?K*}yhsocpZAn2}Su-;O z?cl1GDzt4qG%I0CNNcT`%`T5sx?qw}!%thFvjHKYdy4>oYXPf+7Ow_rdF@z@_ww2e zVhOL;2Y(~E2w;8#{Fq|`U55&CCJ9sMi`;9CWxfiYN4ogE8eSr54ht8xpmqR2Z>6iL z8_^%jeMmx@9$T+s+?D0ceUw!5n`QF1kDV6}UgVXO!fAt6ZC#@ce#{oUz?@lnh5>0|Q2;3+H5^+3?uo{bSWDIpZzoB;$FC!Gwp8F8PT zZJvj&VptD$Pa8=2ConWFxhq$O+FQbG))JpzeJmgFH6w1u2-1c?n|BX2;avJZGPol| z!&*T88|{ts^`_a+UWbcOtcg}q{N{oI0=-thil?HNBJusS$MUw})}JG~%v?nx+htjGE!^3drT; zp7op(W}!35=)QAw2OE^8%z0cwp^>3Fr8y(vl@a;FWb%Wf-s+V8(x}RG@cM2Cz6&0m z)*CVEsoh;Skd{#w^hVXAgz#Yxe{){fV{BT}N~spOVNCbNWe* zQ8dZ&v@t`{og~X=;biEwiavN;l?AOfmeM6jGPK%V{nY!pJ2kGLWiOKj_|Y?^ z+n+zdNxivoa`tr6%g)uWmY=Jc5#cPKBCx>L@PS@Em#MKLwqXEt4edl^dVM`fvK|W+ zpyB{3^)Xll1Wu>=4Z)&lKp5#A$3MOZp3QTmC#s(@ zra^Z8elr6hWO7TAXD;Cy8V;8iLRTgyeX-ZqzI|kk+0rGi>}n&^-su+5B*W0nD0x-_ zq>{3@Id_y!6Y9+=lS_>KkB03?Im1L}*)I2X=!P3pSO6XWo7}-H@kZwQTK$&#clX?o zF>aq<(RAeiCrz6RaGW%=t+(#n)wPt6;3;e1u9Oh+X^U3rwLRSfQ3NryN)_0q;m(MC zFfh2cH{5zXt=c`AM$9($KM~aT6j86!hhNn_!%v$FP=u_&DRuA@_f2g^b8|N84x~ft z!=Wrz|9H7an01Q3p;}s%kF1Quhz*gJG>(SX^TC1U;4~4hIeg&5@Y+W;z_5j$fT3LP zQR%x6T_ZC3X5tKrgn+&5Rap)n2|o(}fDSS7)8UC~N<*dzA>^E@f` z&#W7trnH7CjroLy=h2c6s}DB?ryOk7l|8W4xz8d|kzeVfN6R1o=EzRUj(EyL#9AIW zerEY8oYE`Ks&N+TmB4NBn~2LHvOz-Wgsrm|c@CznU9CMH_-o}?|HfV%Uf@K6Rof7%^PuyKn?d&tNmkGHlcp3DIbMG`{XY7Gq866vt&9$`c zpzJ;~zRkwpxfZE^8-Cd95)!*3aVzT2=Xm7TJ^2W`|Cov?vbwbOTuAPO58;hE@zlvX z9LZRTChMe{jUAo7_bMYu(PcX<(F+JLVZ9MJPInAGJeB9;wlR3{^a=f$iIE7~=V9pn z*w#B2Ci*HbaQfXi=qZW9o|pKm!%?DBf-B#z7`7?G7E}Z_irKvVBX~;vkfy+JlqOvP z|CM~2aM4+(^x(NankG!|&FyIMNA1bRlG16{t%#;nRf0!`|3QQQlBN$D;FCVrS0%V( z?Y!)eUP!Pu>F|||!Xp5V8MY~5B0B`f;914zNei(3*+HI}mJz(dv!;%h22E*1PI~R( z7mYP-n>wG?4?r+KtfaFXyW(3kzi>q}@Z~eO9LDmGhBIiTY(u7;`8Y>4?EKjvN|4m6 z&>4s?EGXOPyrV5BfLUwzd9xH(=J-;AvPZK!rf(JL*Y{DL^IojMnm_`%dzsDI@Z^6^ zB46de+vws*!YGXG*L|SRQ*@_O}WkE^53;klT&!uHBfFZ^vYxFsdc>?xDSiyki% znq~RU^KQaPEF}7g(26m;>CuvE^`##w2>q1>(=F+5;ntv20!3slANK%&A>73a4dD9}>6BV)N%SC0HJIFZ2$ z0g&pnD{HVNDQQ|?^2vg-cYdP;EIV>J3B!s1RL(Q3H4ydB&_6zbYS)(sav}cp;miud zv0A$lNsz6*;gIc9?s)Ud){S+_d-=+s4J)xT$sv)<$4BUVU?c2Y+7hp7?RyZ~g$~H>&;R zpyN@4Xir0=SBNz||HQnrwt&0V)!i9|pc0<}S3w?Qck`ur<3Qk2waWFrTT5sIc=nX3 zPXRJJ3OmS4LYubzmNc85{L{6skb0F}0}O~@$}}W%#)wFm4JU5rDl2TK+x7SYmnkl|{#_qDe2o0Oc>pl6kk9Y#5EuzQ{Sswt^ zX3nX!F&7J1H=Bt#P_`wRaJ*e~~J<91k zl6Yr(Rq6-@yrOt0H;>ss+J?dlY`9W>33I7Y(y`K}Yeh%2^4pv-p+yJUJtX;(R|NuH zVsc%A>2bg=A&x(r;8AE;yVpOoHWVJ}q4U(aPF4~X<#x&iOKe?6{W zuQcoRsyyrb#pB!ehsKK#*$<8k{IfA4nP!Qzgy{nm4k#ps*?46| zi1&!<%qop?UnO&a$NcNc7z-XqyHE#{ixlDLuNx%#*h;&nv`xrvLJ7As7kT1=ayc-h zPbc=nNBWkNtEp2~*9v1Bqm*rxC#RlvPq~DE;N$176>c*37pJ?a8vsbH*CA&1Hn<ku>1R>(GHdNFU7wo!k*N zjK%RSyLuCLLabwk{ADk?Q+RO6#$&lZXnxIDiCXD(!Z(&8eIs>^!)C|lR-ZO!huAhV z+nWaU1Sm@BF0)x`rb!HQU3uCS3zfc6{p_GTflrrEjWWZ*LWe0-?j)iXVBsO0Sm|jN z6Q4xCVb_{3kaXQalk33Rz1eWW!1|P7aVB6Ow3N@6QaI__pp8~OwJEOwj$mf*K=_m= z;7E}6w);_!oQ0@dMyUx7+NG=~JEpNV1!n{0bjb!~EJvAF$0n~xp1Ai-0yg5d1MR!6 z^|`SySksSkLPWm@Z4xzJSW`-8bmp zHfp_V$^9*?p^fXFiZu+^QyFY_$}c+-+i6J_$lHn@xuE&0Q9wJJ0_l2_9ay#W@}MuM zA#C13j0yc@h^l?sP2^k}ab%i0iR^C|1d{ACj>eZf5$>-Ifkz*<103Z~OM4y1AhSUhh0({hEkG2| zs3TF@sP_M-f-`YTGF|`p%&BHtEodxrYecM6Br|udIT<8iS}vfVSeB?@Sd_SBoimLo z?umkmOOqlhn+AdkYNyw4ekx}NvB@8|w}z9Ch< z{+PKAj-}(MwNF2^x=GXYkoCunTJqJa+hZH0R#8pNI>Y1G$cjZQe(>?Yhu_0sK-iu$ zr&s|>k^|4fl@GpWYRufkpnbuuhk}5lv1p)MHAL|vT}c`&1Bu~<(e8K2U{OvxU3*F5 zCJGLMiE%C<1jZ#aUP8a}YXK5e^S#k?(GIYupr9o?<_jO1Rzv=Hi+N$eV(}-R+b#_* z4Kx4RT{6*$C=(;17DbUXT`g8G(dq{x!cZRg{ukblqC~1ZC<5lMVcwWwf>i}6g>mAqJTq^HfP)rAw z+V&*TXJ9N8rz}x(v_uA0SHSsf+}y$YvBHqQdvCqs;6@1Nn!09hF?q1I;_wNF7oRB1 z7lPQj>}JQjV6O|-V+t#3!*Vy<)%bmseVc3P^&S)h*x=xRVG>`V35~a?CccqM(bNdt zC&D1pLpFcJ!c0Jo7?UxjyhUcc)&^Hiuc&g$T--=)HeD$}V0v;e$kbysorpg6&au+A z-+fK{dhpp`RojWkH*&@@~zefWR;DqgOoXE;YW~>~J&Fbja`Y+yv zMcQ(wDyy+Cn}BwJFsQNE`^kw&d9AlRNc29e(ZwE&n#1EH>RA7~0icKg5OmD?k^;1B zkr%2QePcUpV)wCR-&OxHa;5i@Xzbj+*(?#EF?oLIQ-K`fu5W|G*!NIKa8vKXL}p;h z?D9Mphc?xShi^(A`G5JeOjmW#M{zA&=KP1X!)Es{gll$})bfgbqGaQir0&pnb#3TC z=W&RVYRf+nuIjJhOxi0PAeH<|r9>IxKJO5^wW0+oPw`a8_!^4t{Q zFiz$wQn*_>V@*WeI~;W$CC~~J$9tA!0YG*|zKaX+^sw1BBuM?x+S?lxKnK_Vl<}K! zsL}x8rMzyA$rO7i3pMq`Xf(WCSxBj*CR$|_dQwc>$3n>Q96HE_3{=7}U@2P?mh9pY zn`)RzVwM|J@kNyQjZ8piEP{o8Lr6?tJs8F*FGXDlvnL{;q&nDD@#49 z(@2AP z&P+sio!}5Tx;Fb^+h%7;xqy2N$jdEt>#O5cEtd-Ek=u5jTuUsPqr9 zk^7`w&-gPS2vHVn?=CwdX%6d9C3KbWp3}x4Xw71gt7!{n@wTF56DP#unp)Cdm>0LR zQx{I$VhG541R&)5;bEm-4?Dw*M}8H2SmS&tInF#oTu3H&7;jOABN(Rn#@%Yvd!O`? z{?6SQYB-&E2+Ky>0YRudi@1(eRi@K~<@Bo5(87pypQwxgn-M* zYfS6u34J^xtkJ?-S0r3P>FVvDFUs^2&=oI9@)L*qx3jNP`PHCce$I4T(i3>5)XaK? zV)C#W8=bwgdM@^6$_g$0^U;pH&$cMCIO@q-LgfCX$i^|5!F*L7>9&}#cpY&E2#1{# z^Mw!f;FtrhrK-hmL4?q2#B6{-3AGFz>aD!e2-` zV8cb9MouQ!0Uz7{DEYaGQuQXTCAVRs_I!NF&R?r6#`BY&2%FJ_V=Cg2RSsvZ8(nH6)p?T0Iwfz1U@ zI64Bk43KC>x0ZG$pAe?6Tp$Ql1nkRLX`r+DD5{_`xkOz&uQ{)S9FJuO`2Nv3obX{y z-^N02a}l`HdC_(K#T-_cHj0jrQsh+YmJM%;Q{zOJc)A*uTiJ@(uDJtfE7q7-r`bYS!Kw!Q1f2p4vsv%u*1JX`2fIFtj8Ofxec$OdXBLoFy!2+d?&XN;-5kOgKO5 z0Iwmds0mh)++{ZtOQA(WP;FiqVl0-y<3DI-BrHkS4v-7hEqOhcxlcfJFgsIHNo8Xz zZ!P?`Em$ zG%uSEA3kij2L;19%tC+zTiSFaA8_u~_X^!GjK=SpO0(hw`9g{sCf$-#P|6Aa*IQ{c zT5GVs2~TZH9Xk@s8!Dd=8W2>Hagw0&E*8#XNphFE6T|xh%_3T)H4AkT>bt*BCe`_6 zU;o3~FL*C%3T(Qok;qa%Y#2I;?AwP6v#-TJaV@sFkd|4XnseEj|KrN+1?a=j=k|QJ zH{lZN8-nWK<2HR>E*qb9E8Cg`+*V?Ub$8rSQa%FJ(h`^%dGs9%JDfa5p{g@k@e*>P zxz=zLQ}4;+&oz==6~tK{WY5O+&U7miJWLtJ-!OCI3k_|Stq0a#7+F{jQ`i6x03li` z66G$x?BhS+#`Y|lFIaoddjxqcd0?+kxK8(!q79kGrsc<#&bv>9rW^~6q?GVeY7(8> zSEy7lY)w*mucD<8F2a$*^@|2Uipld}U*DLz?Mqq?H!nzrDj9&SccnU^`?OYBqQT$? zdFMcYh2$I}XV&xjE8CL-H)6a}Q<4}Gl$vqVtVic$5PGkBZln#gw9w>cku2(zi4Sm6 z*~S$6_Yx087mOc1JpAaw@b9l4yVH|RRICHjMIhjpBa!i;V3v9WQt`UKU=*Dont==4 zrLB%3ULUJ#KE2xgT35OLwP&4p@Ic(!z~DhxVCC7*`-LKchBvM$_Q05=HefI3070o5 zHWL{;lx+Tn6Zr0|E(LP;MSt6VQ(%n|jeyw!fsG~I_`oQWQ{wJkYCT^s5X!&%VG0B+ z3z8`^Cn2>ceQ9JCL=1t&J&!ja3|VwY5X@w~HEA~8dGEZ_lUiAdR%v?H2WN=$v+H9$ zG}_Mq$QX6_3HR=#)li+?IulN?q%I8VQd~z8M^_?H#mFW;Qa`-VmOgRt;8m~)7XZjqdP57#m>;Qo;Ma0iJD_B%|`>ULXUcXk}!^Anl0kCu{}Z1} z>K|jC!*`!#|Mk8!o#he)@4%t~3JOyvTJh?1pCvo@#2Z@_&_Nf>VzOlCW6Vo_PO~TuFZ9tFP>xs@RCh5efI@GsnA<)NhYLmkImO5 z$&aMT^Xkvkk+Wp)yEf1|ScFJ$X#l+Ud05pI&XLy^%&L6OrOT-E1DsuZf2e{roIkZQ z^v)qyruUPJLlps|M@Ew_{hYIR+u4iVM_8dX-;M9xe(BDZ!IGyJm6mqDSeyb>{rXQ= z+VAU2f0b(5lg)10Pwdv1vwwf<7152mO?&70kEg#~tj}NGW~+bq#igW}uE{+E!EfzE zM`MdZ^V?2kC79!h-uC}KtDV_vACa}W3>Lq)|3UrzJtbauKV7_9i2X0?7HFzv^$vE^ z&p&+n1b=rQXrEzml%4Stpf9lLU8k`D`cH*{v4C{MR0QWBy zLp0IUL|$pxWRWU@O)uM8!7w#YBbsZq?kZNC_dJ?WsS(SxF6WMh?omoeN`53w4AIsV z5oAe{OGQF;brb7G4)|V9hkzVsR{tm^Js5QOrZKHTCYOix)W9ft=g~ zp7N{SgDE2U_8gSJdp@z$2Ct3{w4zj8p)o=?!;xp##Jmn=iqb5OrDvq0MB30OB}!f2do%CuL{_{QjV6>WSY~w;pLAz94rBF(VT2y}06677 z;&4>)b=a?xtm~6-@*4&awERfkI+LRzHjI&TU}=?!95p$YI1no>M%St`kK{XXLBi=3 zZvkJXO0mQVeeYJG;?(jZ0>6*{1+ zQ;Q@?bENl?wER%0)>Y`QZ|UvnzOHcF2$}ZPRSZ9rG0wvRN4bwb&%LMK2V%j~(ZPEM z2wD!{G3UiuX&bh}{nI<_wr*60g97< z)DwS##yYG0%f<&oT6smLZyK{483aZJ5Mi&q4!}ZQ^}TP)d)1tbzC3Z_`a7mTI>xx$ znmYq03`Bq;8Vp@mHoU^hR2|j$2YJv7dz-I%&G*7^SU@t->)c2yF+KpN#yU#S_VZ|5 zy9#R+IK1EO{XlEb{c8*5pJ4x*`yL|1Igz2aIBZOQ5N;a-q+g~{K}v%zYfGezKz`_aRR zn!whtsb6Y=4mxae;Q<227W0&rW7;d_h;@YuDsKZR{AKNAuKuX|Wy}&wfa|{7SC|LuihUl}@|zZ7xsxV2J6|5Ko!`>b?#T@*-#yNxV8ap6whz za)`}jtxfs(xDb-hB4Odf%nit=m9HPaevvR~OuA_%Q+jU1hd)#(GBWb;^>I!S0g*MQ z&Gibguw7?=#m)V-CJ53S?gXzr>oDb-x&jh@5&_;9KIVgG$a8 zdVbWTbt#Wbzjp*+jbq$)M(BAs%v4vSs7e)~>MABN#qQmNTbfit;E-f#2neyTZNaTS z8(Xu)JB!v8&Ll?aQ$Qfl1Avo%37Xg$o4S~8>b_;8m~l0*bp(sC%G63S@2sL{o(Q!D z=@OW24?ZJB?t99^#hVgBoh7;62Em|~VupD^9WLFQQu!ZQ$9rto>1r35eheZnjWK14^@&}P}Io6%()Flv@n3oL3&F1?9fX-QhqB#_A0vW$+BNCM?=_u zQP;?(x66yH)&^VCLS=Y+iukIV)_djB!%&TLHowl*?2w(qJ7w^f~hwBjs znz9I}13Jm*+MbGLQu~|Xu*SfQY3Gh!idKw7??u#oxt~4KpNCy@H`Rrccw3v=y8B-7 z${_Ib1{E4k#5adkUR=4ski0wr|5W3uR1IWG0xg8TEo@Y=Rn4Tq~)+-+<-ld9hw8=N}CoDbbZDYQjjoF%UM>>@YmRlp^qFKOk*GX($(?mczK9= z&fmy*Zt9f8I=K{JJiX<} z)2f|zXHj8eQ^wcz*FU`c?Z%e=v;X|h6Cf}<=NXKGDog;Ma(C1;D${vaLTFX9icXpJ znRNbB2@ZF{2iA7f$tEV|ckakc*7P*T1rwCgg3h)eNGD6`DO+0t_EVXp3CO3>W#5qX zE>p}wbv7F-^(9~N(rTi)d?z$W9^ z*68|6J^2Wui*xbG=ID*5g-G70bKgoXt`#s#kWA|K}2uCwbM*OpvO zAZ4V$T@1-2OmW*l5lF`5PT7dYsY^Rn11b1}L<9zdnWmO&+csc?LqSEkP}i(i{lAL@ zYf0FM7MdO#vQ1XtvU zuud}$`n9oMUEXZZ)K`tdh9Al84YlUo)AFTLq<&`;S`TTWMPsl40t*ET3*K8dR$2Gd)h2Hcc=KBE}=tszjRvzMBu>= zJa}{NkJ?^;QDh}imeE(XqOm?2qmx`tsa-bVn+>0jUpgE|7SnS- zFHhPX7$f^CP~x*;tL7D;ueu^gNxLD$8933g=s=PMxEroO((;p-01#{%E}>&L!y-Tc z^KFPla-FW(My84+a2P9o@u-P8T*FX3eyIQ^ono!M8M(b*v=UCeY@Uv9d1M>4yfXE< z4~R4++Qgi!8}nT&uIIZE@D8WVoZG@pQpbjR$tQLh0NByB5;catd_`v1VY83go$eewL@{;Myp4ruvVj-W zp%XNk7p(XiQ~b3sx|*J>Y2%&1e%2f3J}@)W>t5yyt>TQ#26{vz3%$&=tuxBeo%vi&Pdq=?UF)AIQ&WITddwiiiAlMbe?%1u#gm%??2oJMJP zVPHDefeg}4BMD5(lF&`eUHNfcK3|$u+lq~QYu6!!RF4c`)iLlH9<GQZrr3*U=+aF3a4cmZLnd8`Ux#sdgi2?LtuOU$j815J=cSq431I6`dokD)EupDN z?Y#{G1;v8{Fsz7tZphy$r%beYL?1-&|IVuHxLQK+z04?FCR(CW9gt z*avxLvQ(&65VzH#iQW`6jQ`315c4Smu)|fivuAMtDHQ7i)M3tiWi7*;7=qM?_Nq29SsgRE z^&mTI!>$hb^M`uL_2mwn)Wla=hQxL$M!-`Q(?yxy{sfP%UB9W^ z@tw`!mx1Nu6?<=bg?rebn?0Dy!>4Xu@9^JQcXg%z=D!6UPw)M_$80P3;-;i;;_htu z=MUl^Ie%*7?9}~TX8j6jDhX{f>{*j9n>_11$zYS>j(`Eg_}oh&0X1i_CVY% z`@R1<G{w&Hd5^q_Emi7Lf3wT0lMAln@t^? zPy9~(7P0vs|9kfB#qdizV*az)`@`SvlskW3WWU+6>F&L6Ttx#nUkdZ``%H;qxT8=2 z(QWA1FBU_`3PeZVDsOJB-LolpGXh?kx*77UZOg9~yLsLp#Gw#GL+Z*uP4oH(Nt-U7 z{`NQfUqQR`g)`r2;ylnP%x{pJ&mSk>4TYo8QElHQzGA&&S(fzl?LWEa`3vJW$3)LI z!G7Gn**I`&56pUX`xgL!_d_WLoEDy@}*3_VYfV+O_B3cKcm-ew*#{ z?E>J&)b)mE7TNR#k-e{WktIZ0n5n%i>i%=wrf ZyG|wjWAV=a-mD^;w*$>D?TP*M{{ZAu5(fYP literal 0 HcmV?d00001 diff --git a/man/s2_data_example_wkt.Rd b/man/s2_data_example_wkt.Rd new file mode 100644 index 00000000..1dea278c --- /dev/null +++ b/man/s2_data_example_wkt.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{s2_data_example_wkt} +\alias{s2_data_example_wkt} +\title{Example Geometries} +\format{ +An object of class \code{list} of length 29. +} +\usage{ +s2_data_example_wkt +} +\description{ +These geometries are toy examples useful for testing various coordinate +shuffling operations in the s2 package. +} +\keyword{datasets} diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index cea30252..c4432cc7 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -279,14 +279,14 @@ class CollectionConstructor: public Constructor { active_constructor_->ring_end(); } - virtual void geom_end() { + void geom_end() { level_--; if (level_ >= 1) { active_constructor_->geom_end(); } - if (level_ == 0) { + if (level_ == 1) { auto feature = active_constructor_->finish(); features_.push_back(std::move(feature)); active_constructor_ = nullptr; diff --git a/tests/testthat/test-wk-utils.R b/tests/testthat/test-wk-utils.R index 4f3b1531..902df1a8 100644 --- a/tests/testthat/test-wk-utils.R +++ b/tests/testthat/test-wk-utils.R @@ -186,8 +186,8 @@ test_that("mercator projection works", { }) test_that("the s2_geography_writer() works", { - for (name in names(geoarrow::geoarrow_example_wkt)) { - cat(paste0(name, "\n")) + # nc has some rings that get reordered by this operation + for (name in setdiff(names(s2_data_example_wkt), "nc")) { geog <- wk::wk_handle( geoarrow::geoarrow_example_wkt[[name]], s2_geography_writer() From 5daae432f248460b3fc92814b5806bde7bfe179f Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Mon, 11 Apr 2022 22:53:27 -0300 Subject: [PATCH 71/88] using the new constructor for wkt --- R/s2-constructors-formatters.R | 18 ++++++++------- src/s2-constructors-formatters2.cpp | 9 +++++++- src/s2-geography/constructor.hpp | 34 ++++++++++++++--------------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/R/s2-constructors-formatters.R b/R/s2-constructors-formatters.R index 8926f67a..ad0b5b38 100644 --- a/R/s2-constructors-formatters.R +++ b/R/s2-constructors-formatters.R @@ -102,14 +102,16 @@ s2_make_polygon <- function(longitude, latitude, feature_id = 1L, ring_id = 1L, #' @export s2_geog_from_text <- function(wkt_string, oriented = FALSE, check = TRUE) { attributes(wkt_string) <- NULL - wk::validate_wk_wkt(wk::new_wk_wkt(wkt_string)) - new_s2_xptr( - s2_geography_from_wkt( - wkt_string, - oriented = oriented, - check = check - ), - "s2_geography" + + if (check) { + wkt <- wk::wkt(wkt_string, geodesic = TRUE) + } else { + wkt <- wk::new_wk_wkt(wkt_string, geodesic = TRUE) + } + + wk::wk_handle( + wkt, + s2_geography_writer(oriented = oriented, check = check) ) } diff --git a/src/s2-constructors-formatters2.cpp b/src/s2-constructors-formatters2.cpp index f6e6e061..70719b0c 100644 --- a/src/s2-constructors-formatters2.cpp +++ b/src/s2-constructors-formatters2.cpp @@ -236,7 +236,14 @@ void delete_vector_constructor(SEXP xptr) { extern "C" SEXP c_s2_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { CPP_START - auto builder = new s2geography::VectorConstructor(); + int oriented = LOGICAL(oriented_sexp)[0]; + int check = LOGICAL(check_sexp)[0]; + + s2geography::Constructor::Options options; + options.set_oriented(oriented); + options.set_check(check); + + auto builder = new s2geography::VectorConstructor(options); SEXP builder_xptr = PROTECT(R_MakeExternalPtr(builder, R_NilValue, R_NilValue)); R_RegisterCFinalizer(builder_xptr, &delete_vector_constructor); diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index c4432cc7..84216a56 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -36,8 +36,12 @@ class Constructor { bool check_; }; + Constructor(const Options& options): options_(options) {} + virtual ~Constructor() {} + Options* mutable_options() { return &options_; } + virtual void geom_start(util::GeometryType geometry_type, int64_t size) {} virtual void ring_start(int32_t size) {} @@ -54,11 +58,14 @@ class Constructor { protected: std::vector points_; + Options options_; }; class PointConstructor: public Constructor { public: + PointConstructor(): Constructor(Options()) {} + void geom_start(util::GeometryType geometry_type, int64_t size) { if (size != 0 && geometry_type != util::GeometryType::POINT && @@ -104,7 +111,7 @@ class PointConstructor: public Constructor { class PolylineConstructor: public Constructor { public: - PolylineConstructor(const Options& options): options_(options) {} + PolylineConstructor(const Options& options): Constructor(options) {} void geom_start(util::GeometryType geometry_type, int64_t size) { if (size != 0 && @@ -125,11 +132,9 @@ class PolylineConstructor: public Constructor { auto polyline = absl::make_unique(); polyline->Init(std::move(points_)); - if (options_.check()) { + if (options_.check() && !polyline->IsValid()) { polyline->FindValidationError(&error_); - if (!error_.ok()) { - throw S2GeographyException(error_.text()); - } + throw S2GeographyException(error_.text()); } polylines_.push_back(std::move(polyline)); @@ -151,14 +156,13 @@ class PolylineConstructor: public Constructor { } private: - Options options_; std::vector> polylines_; S2Error error_; }; class PolygonConstructor: public Constructor { public: - PolygonConstructor(const Options& options): options_(options) {} + PolygonConstructor(const Options& options): Constructor(options) {} void ring_start(int32_t size) { points_.clear(); @@ -198,18 +202,16 @@ class PolygonConstructor: public Constructor { auto polygon = absl::make_unique(); polygon->set_s2debug_override(S2Debug::DISABLE); if (options_.oriented()) { - polygon->InitNested(std::move(loops_)); + polygon->InitOriented(std::move(loops_)); } else { polygon->InitNested(std::move(loops_)); } loops_.clear(); - if (options_.check()) { + if (options_.check() && !polygon->IsValid()) { polygon->FindValidationError(&error_); - if (!error_.ok()) { - throw S2GeographyException(error_.text()); - } + throw S2GeographyException(error_.text()); } auto result = absl::make_unique(std::move(polygon)); @@ -218,14 +220,13 @@ class PolygonConstructor: public Constructor { private: std::vector> loops_; - Options options_; S2Error error_; }; class CollectionConstructor: public Constructor { public: - CollectionConstructor(const Options& options = Options()): - options_(options), + CollectionConstructor(const Options& options): + Constructor(options), polyline_constructor_(options), polygon_constructor_(options), collection_constructor_(nullptr), @@ -300,7 +301,6 @@ class CollectionConstructor: public Constructor { } private: - Options options_; PointConstructor point_constructor_; PolylineConstructor polyline_constructor_; PolygonConstructor polygon_constructor_; @@ -315,7 +315,7 @@ class CollectionConstructor: public Constructor { class VectorConstructor: public CollectionConstructor { public: - VectorConstructor(const Options& options = Options()): CollectionConstructor(options) {} + VectorConstructor(const Options& options): CollectionConstructor(options) {} void start_feature() { active_constructor_ = nullptr; From 7326d3f6fe2194f5203116e7376fd1f909d1bb2b Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Tue, 12 Apr 2022 21:59:15 -0300 Subject: [PATCH 72/88] use writer for make_polygon --- R/s2-constructors-formatters.R | 38 +++++++++++++--------------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/R/s2-constructors-formatters.R b/R/s2-constructors-formatters.R index ad0b5b38..47e417ff 100644 --- a/R/s2-constructors-formatters.R +++ b/R/s2-constructors-formatters.R @@ -85,16 +85,13 @@ s2_make_line <- function(longitude, latitude, feature_id = 1L) { #' @export s2_make_polygon <- function(longitude, latitude, feature_id = 1L, ring_id = 1L, oriented = FALSE, check = TRUE) { - recycled <- recycle_common(longitude, latitude, feature_id, ring_id) - new_s2_xptr( - cpp_s2_make_polygon( - recycled[[1]], recycled[[2]], - featureId = recycled[[3]], - ringId = recycled[[4]], - oriented = oriented, - check = check - ), - "s2_geography" + wk::wk_handle( + wk::xy(longitude, latitude), + wk::wk_polygon_filter( + s2_geography_writer(oriented = oriented, check = check), + feature_id = as.integer(feature_id), + ring_id = as.integer(ring_id) + ) ) } @@ -102,12 +99,8 @@ s2_make_polygon <- function(longitude, latitude, feature_id = 1L, ring_id = 1L, #' @export s2_geog_from_text <- function(wkt_string, oriented = FALSE, check = TRUE) { attributes(wkt_string) <- NULL - - if (check) { - wkt <- wk::wkt(wkt_string, geodesic = TRUE) - } else { - wkt <- wk::new_wk_wkt(wkt_string, geodesic = TRUE) - } + wkt <- wk::new_wk_wkt(wkt_string, geodesic = TRUE) + wk::validate_wk_wkt(wkt) wk::wk_handle( wkt, @@ -119,14 +112,11 @@ s2_geog_from_text <- function(wkt_string, oriented = FALSE, check = TRUE) { #' @export s2_geog_from_wkb <- function(wkb_bytes, oriented = FALSE, check = TRUE) { attributes(wkb_bytes) <- NULL - wk::validate_wk_wkb(wk::new_wk_wkb(wkb_bytes)) - new_s2_xptr( - s2_geography_from_wkb( - wkb_bytes, - oriented = oriented, - check = check - ), - "s2_geography" + wkb <- wk::new_wk_wkb(wkb_bytes) + wk::validate_wk_wkb(wkb) + wk::wk_handle( + wkb, + s2_geography_writer(oriented = oriented, check = check) ) } From 5301092762ef76ae9b41768ade9c836bd8493129 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Tue, 12 Apr 2022 22:05:08 -0300 Subject: [PATCH 73/88] superceed previous s2-constructors-formatters --- R/RcppExports.R | 12 -- R/s2-constructors-formatters.R | 12 +- R/s2-geography.R | 2 +- src/Makevars.in | 1 - src/RcppExports.cpp | 44 ---- src/s2-constructors-formatters.cpp | 321 +++++++++++++++++++++++----- src/s2-constructors-formatters2.cpp | 293 ------------------------- 7 files changed, 275 insertions(+), 410 deletions(-) delete mode 100644 src/s2-constructors-formatters2.cpp diff --git a/R/RcppExports.R b/R/RcppExports.R index 7ed3ac9b..10375c1b 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -253,18 +253,6 @@ cpp_s2_cell_common_ancestor_level_agg <- function(cellId) { .Call(`_s2_cpp_s2_cell_common_ancestor_level_agg`, cellId) } -cpp_s2_geog_point <- function(x, y) { - .Call(`_s2_cpp_s2_geog_point`, x, y) -} - -cpp_s2_make_line <- function(x, y, featureId) { - .Call(`_s2_cpp_s2_make_line`, x, y, featureId) -} - -cpp_s2_make_polygon <- function(x, y, featureId, ringId, oriented, check) { - .Call(`_s2_cpp_s2_make_polygon`, x, y, featureId, ringId, oriented, check) -} - s2_geography_from_wkb <- function(wkb, oriented, check) { .Call(`_s2_s2_geography_from_wkb`, wkb, oriented, check) } diff --git a/R/s2-constructors-formatters.R b/R/s2-constructors-formatters.R index 47e417ff..52c4151a 100644 --- a/R/s2-constructors-formatters.R +++ b/R/s2-constructors-formatters.R @@ -70,15 +70,19 @@ #' s2_as_binary(geog) #' s2_geog_point <- function(longitude, latitude) { - recycled <- recycle_common(longitude, latitude) - new_s2_xptr(cpp_s2_geog_point(recycled[[1]], recycled[[2]]), "s2_geography") + wk::wk_handle(wk::xy(longitude, latitude), s2_geography_writer()) } #' @rdname s2_geog_point #' @export s2_make_line <- function(longitude, latitude, feature_id = 1L) { - recycled <- recycle_common(longitude, latitude, feature_id) - new_s2_xptr(cpp_s2_make_line(recycled[[1]], recycled[[2]], featureId = recycled[[3]]), "s2_geography") + wk::wk_handle( + wk::xy(longitude, latitude), + wk::wk_linestring_filter( + s2_geography_writer(), + feature_id = as.integer(feature_id) + ) + ) } #' @rdname s2_geog_point diff --git a/R/s2-geography.R b/R/s2-geography.R index c89684c3..ae053c86 100644 --- a/R/s2-geography.R +++ b/R/s2-geography.R @@ -48,7 +48,7 @@ as_s2_geography.s2_geography <- function(x, ...) { #' @export as_s2_geography.s2_lnglat <- function(x, ...) { df <- data_frame_from_s2_lnglat(x) - new_s2_xptr(cpp_s2_geog_point(df[[1]], df[[2]]), "s2_geography") + s2_geog_point(df[[1]], df[[2]]) } #' @rdname as_s2_geography diff --git a/src/Makevars.in b/src/Makevars.in index 0bca88e1..1f95a511 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -92,7 +92,6 @@ OBJECTS = $(ABSL_LIBS) \ s2-cell.o \ s2-cell-union.o \ s2-constructors-formatters.o \ - s2-constructors-formatters2.o \ s2-predicates.o \ s2-transformers.o \ init.o \ diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 3317c623..4025333b 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -737,47 +737,6 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } -// cpp_s2_geog_point -List cpp_s2_geog_point(NumericVector x, NumericVector y); -RcppExport SEXP _s2_cpp_s2_geog_point(SEXP xSEXP, SEXP ySEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP); - Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP); - rcpp_result_gen = Rcpp::wrap(cpp_s2_geog_point(x, y)); - return rcpp_result_gen; -END_RCPP -} -// cpp_s2_make_line -List cpp_s2_make_line(NumericVector x, NumericVector y, IntegerVector featureId); -RcppExport SEXP _s2_cpp_s2_make_line(SEXP xSEXP, SEXP ySEXP, SEXP featureIdSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP); - Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP); - Rcpp::traits::input_parameter< IntegerVector >::type featureId(featureIdSEXP); - rcpp_result_gen = Rcpp::wrap(cpp_s2_make_line(x, y, featureId)); - return rcpp_result_gen; -END_RCPP -} -// cpp_s2_make_polygon -List cpp_s2_make_polygon(NumericVector x, NumericVector y, IntegerVector featureId, IntegerVector ringId, bool oriented, bool check); -RcppExport SEXP _s2_cpp_s2_make_polygon(SEXP xSEXP, SEXP ySEXP, SEXP featureIdSEXP, SEXP ringIdSEXP, SEXP orientedSEXP, SEXP checkSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP); - Rcpp::traits::input_parameter< NumericVector >::type y(ySEXP); - Rcpp::traits::input_parameter< IntegerVector >::type featureId(featureIdSEXP); - Rcpp::traits::input_parameter< IntegerVector >::type ringId(ringIdSEXP); - Rcpp::traits::input_parameter< bool >::type oriented(orientedSEXP); - Rcpp::traits::input_parameter< bool >::type check(checkSEXP); - rcpp_result_gen = Rcpp::wrap(cpp_s2_make_polygon(x, y, featureId, ringId, oriented, check)); - return rcpp_result_gen; -END_RCPP -} // s2_geography_from_wkb List s2_geography_from_wkb(List wkb, bool oriented, bool check); RcppExport SEXP _s2_s2_geography_from_wkb(SEXP wkbSEXP, SEXP orientedSEXP, SEXP checkSEXP) { @@ -1550,9 +1509,6 @@ static const R_CallMethodDef CallEntries[] = { {"_s2_cpp_s2_cell_max_distance", (DL_FUNC) &_s2_cpp_s2_cell_max_distance, 2}, {"_s2_cpp_s2_cell_common_ancestor_level", (DL_FUNC) &_s2_cpp_s2_cell_common_ancestor_level, 2}, {"_s2_cpp_s2_cell_common_ancestor_level_agg", (DL_FUNC) &_s2_cpp_s2_cell_common_ancestor_level_agg, 1}, - {"_s2_cpp_s2_geog_point", (DL_FUNC) &_s2_cpp_s2_geog_point, 2}, - {"_s2_cpp_s2_make_line", (DL_FUNC) &_s2_cpp_s2_make_line, 3}, - {"_s2_cpp_s2_make_polygon", (DL_FUNC) &_s2_cpp_s2_make_polygon, 6}, {"_s2_s2_geography_from_wkb", (DL_FUNC) &_s2_s2_geography_from_wkb, 3}, {"_s2_s2_geography_from_wkt", (DL_FUNC) &_s2_s2_geography_from_wkt, 3}, {"_s2_s2_geography_full", (DL_FUNC) &_s2_s2_geography_full, 1}, diff --git a/src/s2-constructors-formatters.cpp b/src/s2-constructors-formatters.cpp index 7741e4d6..70719b0c 100644 --- a/src/s2-constructors-formatters.cpp +++ b/src/s2-constructors-formatters.cpp @@ -1,82 +1,293 @@ -#include -#include "wk/rcpp-translate.hpp" -#include "wk/rcpp-coord-reader.hpp" +#define R_NO_REMAP +#include +#include -#include "wk-geography.h" +#include "wk-v1.h" +#include "s2-geography/constructor.hpp" +#include "geography-shim.h" -using namespace Rcpp; -// [[Rcpp::export]] -List cpp_s2_geog_point(NumericVector x, NumericVector y) { - NumericVector z(x.size()); - z.fill(NA_REAL); - NumericVector m(x.size()); - m.fill(NA_REAL); +#define CPP_START \ + char cpp_exception_error[8096]; \ + memset(cpp_exception_error, 0, 8096); \ + try { - WKRcppPointCoordProvider provider(x, y, z, m); - WKRcppPointCoordReader reader(provider); +#define CPP_END \ + } catch (std::exception& e) { \ + strncpy(cpp_exception_error, e.what(), 8096 - 1); \ + } \ + Rf_error("%s", cpp_exception_error); \ + return R_NilValue; - WKGeographyWriter writer(provider.nFeatures()); - reader.setHandler(&writer); - while (reader.hasNextFeature()) { - checkUserInterrupt(); - reader.iterateFeature(); +// The other versions of CPP_START and CPP_END stack-allocate the +// error message buffer, which takes a non-trivial amount of time +// when done at this scale (at worst 4 times per coordinate). By +// keeping the buffer in the handler_data struct, we can call C++ +// from every handler method without measurable overhead. +#define WK_METHOD_CPP_START \ + try { + +#define WK_METHOD_CPP_END \ + } catch (std::exception& e) { \ + strncpy(data->cpp_exception_error, e.what(), 8096 - 1); \ + } \ + Rf_error("%s", data->cpp_exception_error); \ + return R_NilValue; + +#define WK_METHOD_CPP_END_INT \ + } catch (std::exception& e) { \ + strncpy(data->cpp_exception_error, e.what(), 8096 - 1); \ + } \ + Rf_error("%s", data->cpp_exception_error); \ + return WK_ABORT; + + +typedef struct { + s2geography::VectorConstructor* builder; + SEXP result; + R_xlen_t feat_id; + int coord_size; + char cpp_exception_error[8096]; +} builder_handler_t; + + +// TODO: Both of these allocate in a way that could longjmp and possibly leak memory +static inline void builder_result_append(builder_handler_t* data, SEXP value) { + R_xlen_t current_size = Rf_xlength(data->result); + if (data->feat_id >= current_size) { + SEXP new_result = PROTECT(Rf_allocVector(VECSXP, current_size * 2 + 1)); + for (R_xlen_t i = 0; i < current_size; i++) { + SET_VECTOR_ELT(new_result, i, VECTOR_ELT(data->result, i)); + } + R_ReleaseObject(data->result); + data->result = new_result; + R_PreserveObject(data->result); + UNPROTECT(1); + } + + SET_VECTOR_ELT(data->result, data->feat_id, value); + data->feat_id++; +} + +static inline void builder_result_finalize(builder_handler_t* data) { + R_xlen_t current_size = Rf_xlength(data->result); + if (data->feat_id != current_size) { + SEXP new_result = PROTECT(Rf_allocVector(VECSXP, data->feat_id)); + for (R_xlen_t i = 0; i < data->feat_id; i++) { + SET_VECTOR_ELT(new_result, i, VECTOR_ELT(data->result, i)); + } + R_ReleaseObject(data->result); + data->result = new_result; + R_PreserveObject(data->result); + UNPROTECT(1); + } +} + +int builder_vector_start(const wk_vector_meta_t* meta, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + + if (data->result != R_NilValue) { + Rf_error("Destination vector was already allocated"); // # nocov + } + + if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { + data->result = PROTECT(Rf_allocVector(VECSXP, 1024)); + } else { + data->result = PROTECT(Rf_allocVector(VECSXP, meta->size)); } + R_PreserveObject(data->result); + UNPROTECT(1); - return writer.output; + data->feat_id = 0; + + return WK_CONTINUE; +} + +SEXP builder_vector_end(const wk_vector_meta_t* meta, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + builder_result_finalize(data); + SEXP cls = PROTECT(Rf_allocVector(STRSXP, 2)); + SET_STRING_ELT(cls, 0, Rf_mkChar("s2_geography")); + SET_STRING_ELT(cls, 1, Rf_mkChar("s2_xptr")); + Rf_setAttrib(data->result, R_ClassSymbol, cls); + UNPROTECT(1); + return data->result; +} + +int builder_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + data->builder->start_feature(); + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} + +int builder_feature_null(void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + builder_result_append(data, R_NilValue); + return WK_ABORT_FEATURE; } -// [[Rcpp::export]] -List cpp_s2_make_line(NumericVector x, NumericVector y, IntegerVector featureId) { - NumericVector z(x.size()); - z.fill(NA_REAL); - NumericVector m(x.size()); - m.fill(NA_REAL); +int builder_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + std::unique_ptr feat = data->builder->finish_feature(); + auto geog = MakeOldGeography(*feat); + builder_result_append(data, Rcpp::XPtr(geog.release())); + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} + +int builder_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + + auto geometry_type = static_cast(meta->geometry_type); + + int32_t size; + if (meta->size == WK_SIZE_UNKNOWN) { + size = -1; + } else { + size = meta->size; + } + + if (meta->flags & WK_FLAG_HAS_Z && meta->flags & WK_FLAG_HAS_M) { + data->coord_size = 4; + } else if (meta->flags & WK_FLAG_HAS_Z) { + data->coord_size = 3; + } else if (meta->flags & WK_FLAG_HAS_M) { + data->coord_size = 3; + } else { + data->coord_size = 2; + } + + data->builder->geom_start(geometry_type, size); + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} - WKRcppLinestringCoordProvider provider(x, y, z, m, featureId); - WKRcppLinestringCoordReader reader(provider); +int builder_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + data->builder->geom_end(); + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} - WKGeographyWriter writer(provider.nFeatures()); - reader.setHandler(&writer); +int builder_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START - while (reader.hasNextFeature()) { - checkUserInterrupt(); - reader.iterateFeature(); + if (size == WK_SIZE_UNKNOWN) { + data->builder->ring_start(-1); + } else { + data->builder->ring_start(size); } - return writer.output; + return WK_CONTINUE; + WK_METHOD_CPP_END_INT } -// [[Rcpp::export]] -List cpp_s2_make_polygon(NumericVector x, NumericVector y, - IntegerVector featureId, IntegerVector ringId, - bool oriented, bool check) { - NumericVector z(x.size()); - z.fill(NA_REAL); - NumericVector m(x.size()); - m.fill(NA_REAL); +int builder_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + data->builder->ring_end(); + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} - WKRcppPolygonCoordProvider provider(x, y, z, m, featureId, ringId); - WKRcppPolygonCoordReader reader(provider); +int builder_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + WK_METHOD_CPP_START + data->builder->coords(coord, 1, data->coord_size); + return WK_CONTINUE; + WK_METHOD_CPP_END_INT +} - WKGeographyWriter writer(provider.nFeatures()); - writer.setOriented(oriented); - writer.setCheck(check); +int builder_error(const char* message, void* handler_data) { + Rf_error("%s", message); + return WK_ABORT; +} - reader.setHandler(&writer); +void builder_deinitialize(void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + if (data->result != R_NilValue) { + R_ReleaseObject(data->result); + data->result = R_NilValue; + } +} - while (reader.hasNextFeature()) { - checkUserInterrupt(); - reader.iterateFeature(); +void builder_finalize(void* handler_data) { + builder_handler_t* data = (builder_handler_t*) handler_data; + if (data != nullptr) { + free(data); } +} + +void delete_vector_constructor(SEXP xptr) { + auto ptr = reinterpret_cast(R_ExternalPtrAddr(xptr)); + if (ptr != nullptr) { + delete ptr; + } +} + +extern "C" SEXP c_s2_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { + CPP_START + + int oriented = LOGICAL(oriented_sexp)[0]; + int check = LOGICAL(check_sexp)[0]; - if (writer.problemId.size() > 0) { - Environment s2NS = Environment::namespace_env("s2"); - Function stopProblems = s2NS["stop_problems_create"]; - stopProblems(writer.problemId, writer.problems); + s2geography::Constructor::Options options; + options.set_oriented(oriented); + options.set_check(check); + + auto builder = new s2geography::VectorConstructor(options); + SEXP builder_xptr = PROTECT(R_MakeExternalPtr(builder, R_NilValue, R_NilValue)); + R_RegisterCFinalizer(builder_xptr, &delete_vector_constructor); + + wk_handler_t* handler = wk_handler_create(); + + handler->vector_start = &builder_vector_start; + handler->vector_end = &builder_vector_end; + + handler->feature_start = &builder_feature_start; + handler->null_feature = &builder_feature_null; + handler->feature_end = &builder_feature_end; + + handler->geometry_start = &builder_geometry_start; + handler->geometry_end = &builder_geometry_end; + + handler->ring_start = &builder_ring_start; + handler->ring_end = &builder_ring_end; + + handler->coord = &builder_coord; + + handler->error = &builder_error; + + handler->deinitialize = &builder_deinitialize; + handler->finalizer = &builder_finalize; + + builder_handler_t* data = (builder_handler_t*) malloc(sizeof(builder_handler_t)); + if (data == NULL) { + wk_handler_destroy(handler); // # nocov + Rf_error("Failed to alloc handler data"); // # nocov } - return writer.output; + data->coord_size = 2; + data->builder = builder; + data->result = R_NilValue; + memset(data->cpp_exception_error, 0, 8096); + + handler->handler_data = data; + + // include the builder pointer as a tag for this external pointer + // which guarnatees that it will not be garbage collected until + // this object is garbage collected + SEXP handler_xptr = wk_handler_create_xptr(handler, builder_xptr, R_NilValue); + UNPROTECT(1); + return handler_xptr; + + CPP_END } diff --git a/src/s2-constructors-formatters2.cpp b/src/s2-constructors-formatters2.cpp deleted file mode 100644 index 70719b0c..00000000 --- a/src/s2-constructors-formatters2.cpp +++ /dev/null @@ -1,293 +0,0 @@ - -#define R_NO_REMAP -#include -#include - -#include "wk-v1.h" -#include "s2-geography/constructor.hpp" -#include "geography-shim.h" - - -#define CPP_START \ - char cpp_exception_error[8096]; \ - memset(cpp_exception_error, 0, 8096); \ - try { - -#define CPP_END \ - } catch (std::exception& e) { \ - strncpy(cpp_exception_error, e.what(), 8096 - 1); \ - } \ - Rf_error("%s", cpp_exception_error); \ - return R_NilValue; - - -// The other versions of CPP_START and CPP_END stack-allocate the -// error message buffer, which takes a non-trivial amount of time -// when done at this scale (at worst 4 times per coordinate). By -// keeping the buffer in the handler_data struct, we can call C++ -// from every handler method without measurable overhead. -#define WK_METHOD_CPP_START \ - try { - -#define WK_METHOD_CPP_END \ - } catch (std::exception& e) { \ - strncpy(data->cpp_exception_error, e.what(), 8096 - 1); \ - } \ - Rf_error("%s", data->cpp_exception_error); \ - return R_NilValue; - -#define WK_METHOD_CPP_END_INT \ - } catch (std::exception& e) { \ - strncpy(data->cpp_exception_error, e.what(), 8096 - 1); \ - } \ - Rf_error("%s", data->cpp_exception_error); \ - return WK_ABORT; - - -typedef struct { - s2geography::VectorConstructor* builder; - SEXP result; - R_xlen_t feat_id; - int coord_size; - char cpp_exception_error[8096]; -} builder_handler_t; - - -// TODO: Both of these allocate in a way that could longjmp and possibly leak memory -static inline void builder_result_append(builder_handler_t* data, SEXP value) { - R_xlen_t current_size = Rf_xlength(data->result); - if (data->feat_id >= current_size) { - SEXP new_result = PROTECT(Rf_allocVector(VECSXP, current_size * 2 + 1)); - for (R_xlen_t i = 0; i < current_size; i++) { - SET_VECTOR_ELT(new_result, i, VECTOR_ELT(data->result, i)); - } - R_ReleaseObject(data->result); - data->result = new_result; - R_PreserveObject(data->result); - UNPROTECT(1); - } - - SET_VECTOR_ELT(data->result, data->feat_id, value); - data->feat_id++; -} - -static inline void builder_result_finalize(builder_handler_t* data) { - R_xlen_t current_size = Rf_xlength(data->result); - if (data->feat_id != current_size) { - SEXP new_result = PROTECT(Rf_allocVector(VECSXP, data->feat_id)); - for (R_xlen_t i = 0; i < data->feat_id; i++) { - SET_VECTOR_ELT(new_result, i, VECTOR_ELT(data->result, i)); - } - R_ReleaseObject(data->result); - data->result = new_result; - R_PreserveObject(data->result); - UNPROTECT(1); - } -} - -int builder_vector_start(const wk_vector_meta_t* meta, void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - - if (data->result != R_NilValue) { - Rf_error("Destination vector was already allocated"); // # nocov - } - - if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { - data->result = PROTECT(Rf_allocVector(VECSXP, 1024)); - } else { - data->result = PROTECT(Rf_allocVector(VECSXP, meta->size)); - } - R_PreserveObject(data->result); - UNPROTECT(1); - - data->feat_id = 0; - - return WK_CONTINUE; -} - -SEXP builder_vector_end(const wk_vector_meta_t* meta, void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - builder_result_finalize(data); - SEXP cls = PROTECT(Rf_allocVector(STRSXP, 2)); - SET_STRING_ELT(cls, 0, Rf_mkChar("s2_geography")); - SET_STRING_ELT(cls, 1, Rf_mkChar("s2_xptr")); - Rf_setAttrib(data->result, R_ClassSymbol, cls); - UNPROTECT(1); - return data->result; -} - -int builder_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - WK_METHOD_CPP_START - data->builder->start_feature(); - return WK_CONTINUE; - WK_METHOD_CPP_END_INT -} - -int builder_feature_null(void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - builder_result_append(data, R_NilValue); - return WK_ABORT_FEATURE; -} - -int builder_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - WK_METHOD_CPP_START - std::unique_ptr feat = data->builder->finish_feature(); - auto geog = MakeOldGeography(*feat); - builder_result_append(data, Rcpp::XPtr(geog.release())); - return WK_CONTINUE; - WK_METHOD_CPP_END_INT -} - -int builder_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - WK_METHOD_CPP_START - - auto geometry_type = static_cast(meta->geometry_type); - - int32_t size; - if (meta->size == WK_SIZE_UNKNOWN) { - size = -1; - } else { - size = meta->size; - } - - if (meta->flags & WK_FLAG_HAS_Z && meta->flags & WK_FLAG_HAS_M) { - data->coord_size = 4; - } else if (meta->flags & WK_FLAG_HAS_Z) { - data->coord_size = 3; - } else if (meta->flags & WK_FLAG_HAS_M) { - data->coord_size = 3; - } else { - data->coord_size = 2; - } - - data->builder->geom_start(geometry_type, size); - return WK_CONTINUE; - WK_METHOD_CPP_END_INT -} - -int builder_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - WK_METHOD_CPP_START - data->builder->geom_end(); - return WK_CONTINUE; - WK_METHOD_CPP_END_INT -} - -int builder_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - WK_METHOD_CPP_START - - if (size == WK_SIZE_UNKNOWN) { - data->builder->ring_start(-1); - } else { - data->builder->ring_start(size); - } - - return WK_CONTINUE; - WK_METHOD_CPP_END_INT -} - -int builder_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - WK_METHOD_CPP_START - data->builder->ring_end(); - return WK_CONTINUE; - WK_METHOD_CPP_END_INT -} - -int builder_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - WK_METHOD_CPP_START - data->builder->coords(coord, 1, data->coord_size); - return WK_CONTINUE; - WK_METHOD_CPP_END_INT -} - -int builder_error(const char* message, void* handler_data) { - Rf_error("%s", message); - return WK_ABORT; -} - -void builder_deinitialize(void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - if (data->result != R_NilValue) { - R_ReleaseObject(data->result); - data->result = R_NilValue; - } -} - -void builder_finalize(void* handler_data) { - builder_handler_t* data = (builder_handler_t*) handler_data; - if (data != nullptr) { - free(data); - } -} - -void delete_vector_constructor(SEXP xptr) { - auto ptr = reinterpret_cast(R_ExternalPtrAddr(xptr)); - if (ptr != nullptr) { - delete ptr; - } -} - -extern "C" SEXP c_s2_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { - CPP_START - - int oriented = LOGICAL(oriented_sexp)[0]; - int check = LOGICAL(check_sexp)[0]; - - s2geography::Constructor::Options options; - options.set_oriented(oriented); - options.set_check(check); - - auto builder = new s2geography::VectorConstructor(options); - SEXP builder_xptr = PROTECT(R_MakeExternalPtr(builder, R_NilValue, R_NilValue)); - R_RegisterCFinalizer(builder_xptr, &delete_vector_constructor); - - wk_handler_t* handler = wk_handler_create(); - - handler->vector_start = &builder_vector_start; - handler->vector_end = &builder_vector_end; - - handler->feature_start = &builder_feature_start; - handler->null_feature = &builder_feature_null; - handler->feature_end = &builder_feature_end; - - handler->geometry_start = &builder_geometry_start; - handler->geometry_end = &builder_geometry_end; - - handler->ring_start = &builder_ring_start; - handler->ring_end = &builder_ring_end; - - handler->coord = &builder_coord; - - handler->error = &builder_error; - - handler->deinitialize = &builder_deinitialize; - handler->finalizer = &builder_finalize; - - builder_handler_t* data = (builder_handler_t*) malloc(sizeof(builder_handler_t)); - if (data == NULL) { - wk_handler_destroy(handler); // # nocov - Rf_error("Failed to alloc handler data"); // # nocov - } - - data->coord_size = 2; - data->builder = builder; - data->result = R_NilValue; - memset(data->cpp_exception_error, 0, 8096); - - handler->handler_data = data; - - // include the builder pointer as a tag for this external pointer - // which guarnatees that it will not be garbage collected until - // this object is garbage collected - SEXP handler_xptr = wk_handler_create_xptr(handler, builder_xptr, R_NilValue); - UNPROTECT(1); - return handler_xptr; - - CPP_END -} From 67c1c2986c045f5912edb4299526814b0701bdff Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Tue, 12 Apr 2022 22:19:05 -0300 Subject: [PATCH 74/88] remove previous builder implementation --- R/RcppExports.R | 8 --- R/s2-geography.R | 27 +++----- src/RcppExports.cpp | 28 -------- src/geography-collection.h | 101 ---------------------------- src/geography.h | 7 -- src/point-geography.h | 18 ----- src/polygon-geography.h | 102 +++++----------------------- src/polyline-geography.h | 27 -------- src/s2-geography.cpp | 54 +-------------- src/wk-geography.h | 133 ------------------------------------- 10 files changed, 28 insertions(+), 477 deletions(-) diff --git a/R/RcppExports.R b/R/RcppExports.R index 10375c1b..9cfdfde6 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -253,14 +253,6 @@ cpp_s2_cell_common_ancestor_level_agg <- function(cellId) { .Call(`_s2_cpp_s2_cell_common_ancestor_level_agg`, cellId) } -s2_geography_from_wkb <- function(wkb, oriented, check) { - .Call(`_s2_s2_geography_from_wkb`, wkb, oriented, check) -} - -s2_geography_from_wkt <- function(wkt, oriented, check) { - .Call(`_s2_s2_geography_from_wkt`, wkt, oriented, check) -} - s2_geography_full <- function(x) { .Call(`_s2_s2_geography_full`, x) } diff --git a/R/s2-geography.R b/R/s2-geography.R index ae053c86..2e90fd2b 100644 --- a/R/s2-geography.R +++ b/R/s2-geography.R @@ -76,28 +76,22 @@ as_s2_geography.wk_wkb <- function(x, ..., oriented = FALSE, check = TRUE) { } } - new_s2_xptr( - s2_geography_from_wkb(x, oriented = oriented, check = check), - "s2_geography" + wk::wk_handle( + x, + s2_geography_writer(oriented = oriented, check = check) ) } #' @rdname as_s2_geography #' @export as_s2_geography.WKB <- function(x, ..., oriented = FALSE, check = TRUE) { - new_s2_xptr( - s2_geography_from_wkb(x, oriented = oriented, check = check), - "s2_geography" - ) + s2_geog_from_wkb(x, oriented = oriented, check = check) } #' @rdname as_s2_geography #' @export as_s2_geography.blob <- function(x, ..., oriented = FALSE, check = TRUE) { - new_s2_xptr( - s2_geography_from_wkb(x, oriented = oriented, check = check), - "s2_geography" - ) + s2_geog_from_wkb(x, oriented = oriented, check = check) } #' @rdname as_s2_geography @@ -119,19 +113,16 @@ as_s2_geography.wk_wkt <- function(x, ..., oriented = FALSE, check = TRUE) { } } - new_s2_xptr( - s2_geography_from_wkt(x, oriented = oriented, check = check), - "s2_geography" + wk::wk_handle( + x, + s2_geography_writer(oriented = oriented, check = check) ) } #' @rdname as_s2_geography #' @export as_s2_geography.character <- function(x, ..., oriented = FALSE, check = TRUE) { - new_s2_xptr( - s2_geography_from_wkt(x, oriented = oriented, check = check), - "s2_geography" - ) + s2_geog_from_text(x, oriented = oriented, check = check) } #' @rdname as_s2_geography diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 4025333b..68bcf808 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -737,32 +737,6 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } -// s2_geography_from_wkb -List s2_geography_from_wkb(List wkb, bool oriented, bool check); -RcppExport SEXP _s2_s2_geography_from_wkb(SEXP wkbSEXP, SEXP orientedSEXP, SEXP checkSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< List >::type wkb(wkbSEXP); - Rcpp::traits::input_parameter< bool >::type oriented(orientedSEXP); - Rcpp::traits::input_parameter< bool >::type check(checkSEXP); - rcpp_result_gen = Rcpp::wrap(s2_geography_from_wkb(wkb, oriented, check)); - return rcpp_result_gen; -END_RCPP -} -// s2_geography_from_wkt -List s2_geography_from_wkt(CharacterVector wkt, bool oriented, bool check); -RcppExport SEXP _s2_s2_geography_from_wkt(SEXP wktSEXP, SEXP orientedSEXP, SEXP checkSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< CharacterVector >::type wkt(wktSEXP); - Rcpp::traits::input_parameter< bool >::type oriented(orientedSEXP); - Rcpp::traits::input_parameter< bool >::type check(checkSEXP); - rcpp_result_gen = Rcpp::wrap(s2_geography_from_wkt(wkt, oriented, check)); - return rcpp_result_gen; -END_RCPP -} // s2_geography_full List s2_geography_full(LogicalVector x); RcppExport SEXP _s2_s2_geography_full(SEXP xSEXP) { @@ -1509,8 +1483,6 @@ static const R_CallMethodDef CallEntries[] = { {"_s2_cpp_s2_cell_max_distance", (DL_FUNC) &_s2_cpp_s2_cell_max_distance, 2}, {"_s2_cpp_s2_cell_common_ancestor_level", (DL_FUNC) &_s2_cpp_s2_cell_common_ancestor_level, 2}, {"_s2_cpp_s2_cell_common_ancestor_level_agg", (DL_FUNC) &_s2_cpp_s2_cell_common_ancestor_level_agg, 1}, - {"_s2_s2_geography_from_wkb", (DL_FUNC) &_s2_s2_geography_from_wkb, 3}, - {"_s2_s2_geography_from_wkt", (DL_FUNC) &_s2_s2_geography_from_wkt, 3}, {"_s2_s2_geography_full", (DL_FUNC) &_s2_s2_geography_full, 1}, {"_s2_s2_geography_to_wkt", (DL_FUNC) &_s2_s2_geography_to_wkt, 3}, {"_s2_s2_geography_to_wkb", (DL_FUNC) &_s2_s2_geography_to_wkb, 2}, diff --git a/src/geography-collection.h b/src/geography-collection.h index 9c8c2f01..c664c7c7 100644 --- a/src/geography-collection.h +++ b/src/geography-collection.h @@ -148,107 +148,6 @@ class GeographyCollection: public Geography { handler->nextGeometryEnd(meta, partId); } - class Builder: public GeographyBuilder { - public: - - Builder(bool oriented, bool check): - metaPtr(nullptr), builderPtr(nullptr), builderMetaPtr(nullptr), - oriented(oriented), check(check) {} - - virtual void nextGeometryStart(const WKGeometryMeta& meta, uint32_t partId) { - // if this is the first call, store the meta reference associated with this geometry - if (this->metaPtr == nullptr) { - this->metaPtr = (WKGeometryMeta*) &meta; - return; - } - - if (!this->builderPtr) { - // store a reference to the meta associated with this - // builder so that we know when the corresponding nextGeometryEnd() - // is called - this->builderMetaPtr = (WKGeometryMeta*) &meta; - - switch (meta.geometryType) { - case WKGeometryType::Point: - case WKGeometryType::MultiPoint: - this->builderPtr = absl::make_unique(); - break; - case WKGeometryType::LineString: - case WKGeometryType::MultiLineString: - this->builderPtr = absl::make_unique(); - break; - case WKGeometryType::Polygon: - case WKGeometryType::MultiPolygon: - this->builderPtr = absl::make_unique( - this->oriented, - this->check - ); - break; - case WKGeometryType::GeometryCollection: - this->builderPtr = absl::make_unique( - this->oriented, - this->check - ); - break; - default: - std::stringstream err; - err << "Unknown geometry type in geography builder: " << meta.geometryType; - Rcpp::stop(err.str()); - } - } - - this->builder()->nextGeometryStart(meta, partId); - } - - virtual void nextLinearRingStart(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) { - this->builder()->nextLinearRingStart(meta, size, ringId); - } - - virtual void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) { - this->builder()->nextCoordinate(meta, coord, coordId); - } - - virtual void nextLinearRingEnd(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) { - this->builder()->nextLinearRingEnd(meta, size, ringId); - } - - virtual void nextGeometryEnd(const WKGeometryMeta& meta, uint32_t partId) { - // the end of this GEOMETRYCOLLECTION - if (&meta == this->metaPtr) { - return; - } - - this->builder()->nextGeometryEnd(meta, partId); - - if (&meta == this->builderMetaPtr) { - std::unique_ptr feature = this->builder()->build(); - features.push_back(std::move(feature)); - this->builderPtr = std::unique_ptr(nullptr); - this->builderMetaPtr = nullptr; - } - } - - std::unique_ptr build() { - return absl::make_unique(std::move(this->features)); - } - - private: - std::vector> features; - WKGeometryMeta* metaPtr; - std::unique_ptr builderPtr; - WKGeometryMeta* builderMetaPtr; - bool oriented; - bool check; - - GeographyBuilder* builder() { - if (this->builderPtr) { - return this->builderPtr.get(); - } else { - Rcpp::stop("Invalid nesting in geometrycollection (can't find nested builder)"); - } - } - }; - private: std::vector> features; }; diff --git a/src/geography.h b/src/geography.h index f1098692..2169b2d7 100644 --- a/src/geography.h +++ b/src/geography.h @@ -115,11 +115,4 @@ class Geography { bool hasIndex; }; - -class GeographyBuilder: public WKGeometryHandler { -public: - virtual std::unique_ptr build() = 0; - virtual ~GeographyBuilder() {} -}; - #endif diff --git a/src/point-geography.h b/src/point-geography.h index b8245442..e1ee28ca 100644 --- a/src/point-geography.h +++ b/src/point-geography.h @@ -160,24 +160,6 @@ class PointGeography: public Geography { } } - class Builder: public GeographyBuilder { - public: - void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) { - // Coordinates with nan in S2 are unpredictable; censor to EMPTY. Empty - // points coming from WKB are always nan, nan. - if (!std::isnan(coord.x) && !std::isnan(coord.y)) { - points.push_back(S2LatLng::FromDegrees(coord.y, coord.x).Normalized().ToPoint()); - } - } - - std::unique_ptr build() { - return absl::make_unique(std::move(this->points)); - } - - private: - std::vector points; - }; - private: std::vector points; }; diff --git a/src/polygon-geography.h b/src/polygon-geography.h index 56b9e042..dc37230c 100644 --- a/src/polygon-geography.h +++ b/src/polygon-geography.h @@ -82,23 +82,24 @@ class PolygonGeography: public Geography { } std::unique_ptr Boundary() { - PolylineGeography::Builder builder; - std::vector> flatIndices = this->flatLoopIndices(); - - // export multilinestring - WKGeometryMeta meta(WKGeometryType::MultiLineString, false, false, false); - meta.hasSize = true; - meta.size = this->polygon->num_loops(); - - builder.nextGeometryStart(meta, WKReader::PART_ID_NONE); - int loopId = 0; - for (size_t i = 0; i < flatIndices.size(); i++) { - this->exportLoops(&builder, meta, flatIndices[i], loopId); - loopId += flatIndices[i].size(); - } - builder.nextGeometryEnd(meta, WKReader::PART_ID_NONE); - - return builder.build(); + // PolylineGeography::Builder builder; + // std::vector> flatIndices = this->flatLoopIndices(); + + // // export multilinestring + // WKGeometryMeta meta(WKGeometryType::MultiLineString, false, false, false); + // meta.hasSize = true; + // meta.size = this->polygon->num_loops(); + + // builder.nextGeometryStart(meta, WKReader::PART_ID_NONE); + // int loopId = 0; + // for (size_t i = 0; i < flatIndices.size(); i++) { + // this->exportLoops(&builder, meta, flatIndices[i], loopId); + // loopId += flatIndices[i].size(); + // } + // builder.nextGeometryEnd(meta, WKReader::PART_ID_NONE); + + // return builder.build(); + throw std::runtime_error("inbetween implementations..."); } std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { @@ -150,73 +151,6 @@ class PolygonGeography: public Geography { } } - class Builder: public GeographyBuilder { - public: - Builder(bool oriented, bool check): - oriented(oriented), check(check) {} - - void nextLinearRingStart(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) { - // skip the last vertex (WKB rings are theoretically closed) - if (size > 0) { - this->vertices = std::vector(size - 1); - } else { - this->vertices = std::vector(); - } - } - - void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) { - if (coordId < this->vertices.size()) { - vertices[coordId] = S2LatLng::FromDegrees(coord.y, coord.x).Normalized().ToPoint(); - } - } - - void nextLinearRingEnd(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) { - std::unique_ptr loop = absl::make_unique(); - loop->set_s2debug_override(S2Debug::DISABLE); - loop->Init(vertices); - - if (!oriented) { - loop->Normalize(); - } - - if (this->check && !loop->IsValid()) { - std::stringstream err; - err << "Loop " << (this->loops.size()) << " is not valid: "; - S2Error error; - loop->FindValidationError(&error); - err << error.text(); - throw WKParseException(err.str()); - } - - this->loops.push_back(std::move(loop)); - } - - std::unique_ptr build() { - std::unique_ptr polygon = absl::make_unique(); - polygon->set_s2debug_override(S2Debug::DISABLE); - if (this->loops.size() > 0 && oriented) { - polygon->InitOriented(std::move(this->loops)); - } else if (this->loops.size() > 0) { - polygon->InitNested(std::move(this->loops)); - } - - // make sure polygon is valid - if (this->check && !polygon->IsValid()) { - S2Error error; - polygon->FindValidationError(&error); - throw WKParseException(error.text()); - } - - return absl::make_unique(std::move(polygon)); - } - - private: - bool oriented; - bool check; - std::vector vertices; - std::vector> loops; - }; - private: std::unique_ptr polygon; diff --git a/src/polyline-geography.h b/src/polyline-geography.h index d9c08942..e66ac064 100644 --- a/src/polyline-geography.h +++ b/src/polyline-geography.h @@ -191,33 +191,6 @@ class PolylineGeography: public Geography { } } - class Builder: public GeographyBuilder { - public: - void nextGeometryStart(const WKGeometryMeta& meta, uint32_t partId) { - if (meta.geometryType == WKGeometryType::LineString) { - points = std::vector(meta.size); - } - } - - void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) { - points[coordId] = S2LatLng::FromDegrees(coord.y, coord.x).Normalized().ToPoint(); - } - - void nextGeometryEnd(const WKGeometryMeta& meta, uint32_t partId) { - if (meta.geometryType == WKGeometryType::LineString) { - polylines.push_back(absl::make_unique(std::move(points))); - } - } - - std::unique_ptr build() { - return absl::make_unique(std::move(this->polylines)); - } - - private: - std::vector points; - std::vector> polylines; - }; - private: std::vector> polylines; }; diff --git a/src/s2-geography.cpp b/src/s2-geography.cpp index 88eb386d..6862a7ee 100644 --- a/src/s2-geography.cpp +++ b/src/s2-geography.cpp @@ -10,64 +10,12 @@ #include "wk/geometry-formatter.hpp" #include "geography.h" -#include "wk-geography.h" -#include "point-geography.h" -#include "polyline-geography.h" #include "polygon-geography.h" -#include "geography-collection.h" +#include "wk-geography.h" #include using namespace Rcpp; - -// [[Rcpp::export]] -List s2_geography_from_wkb(List wkb, bool oriented, bool check) { - WKRawVectorListProvider provider(wkb); - WKGeographyWriter writer(wkb.size()); - writer.setOriented(oriented); - writer.setCheck(check); - - WKBReader reader(provider); - reader.setHandler(&writer); - - while (reader.hasNextFeature()) { - checkUserInterrupt(); - reader.iterateFeature(); - } - - if (writer.problemId.size() > 0) { - Environment s2NS = Environment::namespace_env("s2"); - Function stopProblems = s2NS["stop_problems_create"]; - stopProblems(writer.problemId, writer.problems); - } - - return writer.output; -} - -// [[Rcpp::export]] -List s2_geography_from_wkt(CharacterVector wkt, bool oriented, bool check) { - WKCharacterVectorProvider provider(wkt); - WKGeographyWriter writer(wkt.size()); - writer.setOriented(oriented); - writer.setCheck(check); - - WKTReader reader(provider); - reader.setHandler(&writer); - - while (reader.hasNextFeature()) { - checkUserInterrupt(); - reader.iterateFeature(); - } - - if (writer.problemId.size() > 0) { - Environment s2NS = Environment::namespace_env("s2"); - Function stopProblems = s2NS["stop_problems_create"]; - stopProblems(writer.problemId, writer.problems); - } - - return writer.output; -} - // [[Rcpp::export]] List s2_geography_full(LogicalVector x) { // create single geography with full polygon std::unique_ptr l = absl::make_unique(S2Loop::kFull()); diff --git a/src/wk-geography.h b/src/wk-geography.h index 1ca75ad4..f0c0f55e 100644 --- a/src/wk-geography.h +++ b/src/wk-geography.h @@ -2,145 +2,12 @@ #ifndef WK_GEOGRAPHY_H #define WK_GEOGRAPHY_H -#include - #include "wk/rcpp-io.hpp" #include "wk/reader.hpp" -#include "wk/geometry-handler.hpp" - -#include "point-geography.h" -#include "polyline-geography.h" -#include "polygon-geography.h" -#include "geography-collection.h" #include #include "geography.h" -#define CODE_HAS_BUILD_ERROR 3938829 - - -class WKGeographyWriter: public WKGeometryHandler { -public: - Rcpp::List output; - R_xlen_t featureId; - - Rcpp::IntegerVector problemId; - Rcpp::CharacterVector problems; - - WKGeographyWriter(R_xlen_t size): - output(size), - builder(nullptr), - oriented(false), - check(true) {} - - void setOriented(bool oriented) { - this->oriented = oriented; - } - - void setCheck(bool check) { - this->check = check; - } - - void nextFeatureStart(size_t featureId) { - this->builder = std::unique_ptr(nullptr); - this->featureId = featureId; - } - - void nextNull(size_t featureId) { - this->output[featureId] = R_NilValue; - } - - void nextGeometryStart(const WKGeometryMeta& meta, uint32_t partId) { - if (!this->builder) { - switch (meta.geometryType) { - case WKGeometryType::Point: - case WKGeometryType::MultiPoint: - this->builder = absl::make_unique(); - break; - case WKGeometryType::LineString: - case WKGeometryType::MultiLineString: - this->builder = absl::make_unique(); - break; - case WKGeometryType::Polygon: - case WKGeometryType::MultiPolygon: - this->builder = absl::make_unique( - this->oriented, - this->check - ); - break; - case WKGeometryType::GeometryCollection: - this->builder = absl::make_unique( - this->oriented, - this->check - ); - break; - default: - std::stringstream err; - err << "Unknown geometry type in geography builder: " << meta.geometryType; - this->addProblem(err.str()); - throw WKParseException(CODE_HAS_BUILD_ERROR); - } - } - - this->builder->nextGeometryStart(meta, partId); - } - - void nextLinearRingStart(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) { - this->builder->nextLinearRingStart(meta, size, ringId); - } - - void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) { - this->builder->nextCoordinate(meta, coord, coordId); - } - - void nextLinearRingEnd(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) { - try { - this->builder->nextLinearRingEnd(meta, size, ringId); - } catch (WKParseException& e) { - this->addProblem(e.what()); - throw WKParseException(CODE_HAS_BUILD_ERROR); - } - } - - void nextGeometryEnd(const WKGeometryMeta& meta, uint32_t partId) { - this->builder->nextGeometryEnd(meta, partId); - } - - void nextFeatureEnd(size_t featureId) { - if (this->builder) { - try { - std::unique_ptr feature = builder->build(); - this->output[featureId] = Rcpp::XPtr(feature.release()); - } catch (WKParseException& e) { - this->addProblem(e.what()); - throw WKParseException(CODE_HAS_BUILD_ERROR); - } - } - } - - bool nextError(WKParseException& error, size_t featureId) { - if (error.code() == CODE_HAS_BUILD_ERROR) { - this->output[featureId] = R_NilValue; - return true; - } else { - return false; - } - - this->nextFeatureEnd(featureId); - return true; - } - -private: - std::unique_ptr builder; - bool oriented; - bool check; - - void addProblem(std::string what) { - problemId.push_back(this->featureId); - problems.push_back(what); - } -}; - class WKGeographyReader: public WKReader { public: From 68466525bb11edb20c0674a0a5c81813c415478a Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Tue, 12 Apr 2022 22:27:55 -0300 Subject: [PATCH 75/88] fix C++ errors on gcc --- src/s2-constructors-formatters.cpp | 2 +- src/s2-geography/index.hpp | 2 +- src/s2-geography/s2-geography.hpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/s2-constructors-formatters.cpp b/src/s2-constructors-formatters.cpp index 70719b0c..a6e2576b 100644 --- a/src/s2-constructors-formatters.cpp +++ b/src/s2-constructors-formatters.cpp @@ -4,7 +4,7 @@ #include #include "wk-v1.h" -#include "s2-geography/constructor.hpp" +#include "s2-geography/s2-geography.hpp" #include "geography-shim.h" diff --git a/src/s2-geography/index.hpp b/src/s2-geography/index.hpp index 2f17bce2..948b794e 100644 --- a/src/s2-geography/index.hpp +++ b/src/s2-geography/index.hpp @@ -93,4 +93,4 @@ class S2GeographyIndex { std::vector values_; }; -}; +} diff --git a/src/s2-geography/s2-geography.hpp b/src/s2-geography/s2-geography.hpp index ea3d0df7..39793a20 100644 --- a/src/s2-geography/s2-geography.hpp +++ b/src/s2-geography/s2-geography.hpp @@ -10,3 +10,4 @@ #include "build.hpp" #include "coverings.hpp" #include "predicates.hpp" +#include "constructor.hpp" From 84f4a26612e498f5045ae42a33cde98b999fa045 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 13 Apr 2022 08:36:46 -0300 Subject: [PATCH 76/88] fix constructors --- src/s2-geography/constructor.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index 84216a56..a6142787 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -1,5 +1,5 @@ -# pragma once +#pragma once #include "geography.h" @@ -48,7 +48,7 @@ class Constructor { virtual void coords(const double* coord, int64_t n, int32_t coord_size) { for (int64_t i = 0; i < n; i++) { S2LatLng pt = S2LatLng::FromDegrees(coord[i * coord_size + 1], coord[i * coord_size]); - points_.push_back(pt.ToPoint()); + points_.push_back(pt.Normalized().ToPoint()); } } From 47f37bf4a75e072fae3cf0eb7a228377005d5521 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 13 Apr 2022 08:48:09 -0300 Subject: [PATCH 77/88] remove superceeded methods --- src/geography-collection.h | 101 ----------------------------------- src/geography.h | 56 -------------------- src/point-geography.h | 79 ---------------------------- src/polygon-geography.h | 82 ----------------------------- src/polyline-geography.h | 104 ------------------------------------- src/s2-matrix.cpp | 66 +++++++++++------------ 6 files changed, 29 insertions(+), 459 deletions(-) diff --git a/src/geography-collection.h b/src/geography-collection.h index c664c7c7..1f1fd42c 100644 --- a/src/geography-collection.h +++ b/src/geography-collection.h @@ -24,103 +24,6 @@ class GeographyCollection: public Geography { return absl::make_unique(std::move(features_cpy)); } - Geography::Type GeographyType() { - return Geography::Type::GEOGRAPHY_COLLECTION; - } - - bool FindValidationError(S2Error* error) { - bool result; - error->Clear(); - for (size_t i = 0; i < this->features.size(); i++) { - result = this->features[i]->FindValidationError(error); - if (result) { - return result; - } - } - - return false; - } - - bool IsCollection() { - return this->features.size() > 0; - } - - int Dimension() { - int dimension = -1; - for (size_t i = 0; i < this->features.size(); i++) { - dimension = std::max(this->features[i]->Dimension(), dimension); - } - - return dimension; - } - - int NumPoints() { - int numPoints = 0; - for (size_t i = 0; i < this->features.size(); i++) { - numPoints += this->features[i]->NumPoints(); - } - return numPoints; - } - - bool IsEmpty() { - // could also loop and test all(!IsEmpty()), but - // that is inconsistent with what gets printed - return this->features.size() == 0; - } - - double Area() { - double area = 0; - for (size_t i = 0; i < this->features.size(); i++) { - area += this->features[i]->Area(); - } - return area; - } - - double Length() { - double length = 0; - for (size_t i = 0; i < this->features.size(); i++) { - length += this->features[i]->Length(); - } - return length; - } - - double Perimeter() { - double perimeter = 0; - for (size_t i = 0; i < this->features.size(); i++) { - perimeter += this->features[i]->Perimeter(); - } - return perimeter; - } - - double X() { - Rcpp::stop("Can't compute X value of a non-point geography"); - } - - double Y() { - Rcpp::stop("Can't compute Y value of a non-point geography"); - } - - S2Point Centroid() { - S2Point cumCentroid(0, 0, 0); - for (size_t i = 0; i < this->features.size(); i++) { - S2Point centroid = this->features[i]->Centroid(); - if (centroid.Norm2() > 0) { - cumCentroid += centroid.Normalize(); - } - } - - return cumCentroid; - } - - std::unique_ptr Boundary() { - std::vector> featureBoundaries(this->features.size()); - for (size_t i = 0; i < this->features.size(); i++) { - featureBoundaries[i] = this->features[i]->Boundary(); - } - - return absl::make_unique(std::move(featureBoundaries)); - } - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { std::vector shapeIds; for (size_t i = 0; i < this->features.size(); i++) { @@ -132,10 +35,6 @@ class GeographyCollection: public Geography { return shapeIds; } - const std::vector >* CollectionFeatures() { - return &(this->features); - } - void Export(WKGeometryHandler* handler, uint32_t partId) { WKGeometryMeta meta(WKGeometryType::GeometryCollection, false, false, false); meta.hasSize = true; diff --git a/src/geography.h b/src/geography.h index 2169b2d7..4f3aba6c 100644 --- a/src/geography.h +++ b/src/geography.h @@ -30,30 +30,6 @@ class Geography { Geography(): hasIndex(false) {} - // accessors need to be methods, since their calculation - // depends on the geometry type - virtual Type GeographyType() { - return Type::GEOGRAPHY_EMPTY; - } - - virtual bool FindValidationError(S2Error* error) = 0; - - // returns true for a multi- - // or geometrycollection type - virtual bool IsCollection() = 0; - // Returns 0 for point, 1 for line, 2 for polygon - virtual int Dimension() = 0; - // Returns the number of points in the input - virtual int NumPoints() = 0; - virtual bool IsEmpty() = 0; - virtual double Area() = 0; - virtual double Length() = 0; - virtual double Perimeter() = 0; - virtual double X() = 0; - virtual double Y() = 0; - virtual S2Point Centroid() = 0; - virtual std::unique_ptr Boundary() = 0; - virtual std::unique_ptr NewGeography() = 0; // every type will build the index differently based on @@ -68,25 +44,6 @@ class Geography { virtual ~Geography() {} - // Most calculations will use the ShapeIndex, but sometimes access to the - // underlying point, line, or polygon is useful to keep this class from - // becoming bloated with the entire s2 API. - virtual const std::vector* Point() { - return nullptr; - } - - virtual const std::vector>* Polyline() { - return nullptr; - } - - virtual const S2Polygon* Polygon() { - return nullptr; - } - - virtual const std::vector>* CollectionFeatures() { - return nullptr; - } - // other calculations use ShapeIndex virtual S2ShapeIndex* ShapeIndex() { if (!this->hasIndex) { @@ -97,19 +54,6 @@ class Geography { return &this->shape_index_; } - virtual S2ShapeIndexRegion ShapeIndexRegion() { - S2ShapeIndex *ix = this->ShapeIndex(); - return MakeS2ShapeIndexRegion(ix); - } - - virtual S2Cap GetCapBound() { - return this->ShapeIndexRegion().GetCapBound(); - } - - virtual S2LatLngRect GetRectBound() { - return this->ShapeIndexRegion().GetRectBound(); - } - protected: MutableS2ShapeIndex shape_index_; bool hasIndex; diff --git a/src/point-geography.h b/src/point-geography.h index e1ee28ca..f3bffd4a 100644 --- a/src/point-geography.h +++ b/src/point-geography.h @@ -23,85 +23,6 @@ class PointGeography: public Geography { return absl::make_unique(points); } - Geography::Type GeographyType() { - return Geography::Type::GEOGRAPHY_POINT; - } - - bool FindValidationError(S2Error* error) { - return false; - } - - const std::vector* Point() { - return &(this->points); - } - - bool IsCollection() { - return this->points.size() > 1; - } - - int Dimension() { - return 0; - } - - int NumPoints() { - return this->points.size(); - } - - bool IsEmpty() { - return this->points.size() == 0; - } - - double Area() { - return 0; - } - - double Length() { - return 0; - } - - double Perimeter() { - return 0; - } - - double X() { - if (this->points.size() != 1) { - return NA_REAL; - } else { - S2LatLng latLng(this->points[0]); - return latLng.lng().degrees(); - } - } - - double Y() { - if (this->points.size() != 1) { - return NA_REAL; - } else { - S2LatLng latLng(this->points[0]); - return latLng.lat().degrees(); - } - } - - S2Point Centroid() { - S2Point output(0, 0, 0); - for (size_t i = 0; i < this->points.size(); i++) { - output += this->points[i]; - } - - return output; - } - - S2LatLngRect GetRectBound() { - S2LatLngRect rect; - for (size_t i = 0; i < this->points.size(); i++) { - rect.AddPoint(this->points[i]); // depends on order - } - return rect; - } - - std::unique_ptr Boundary() { - return absl::make_unique(); - } - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { std::vector shapeIds(1); std::vector pointsCopy(this->points); diff --git a/src/polygon-geography.h b/src/polygon-geography.h index dc37230c..eec44efd 100644 --- a/src/polygon-geography.h +++ b/src/polygon-geography.h @@ -20,88 +20,6 @@ class PolygonGeography: public Geography { return absl::make_unique(std::unique_ptr(polygon->Clone())); } - Geography::Type GeographyType() { - return Geography::Type::GEOGRAPHY_POLYGON; - } - - bool FindValidationError(S2Error* error) { - return this->polygon->FindValidationError(error); - } - - const S2Polygon* Polygon() { - return this->polygon.get(); - } - - bool IsCollection() { - return this->outerLoopIndices().size() > 1; - } - - int Dimension() { - return 2; - } - - int NumPoints() { - return this->polygon->num_vertices(); - } - - bool IsEmpty() { - return this->polygon->is_empty(); - } - - double Area() { - return this->polygon->GetArea(); - } - - double Length() { - return 0; - } - - double Perimeter() { - std::unique_ptr boundary = this->Boundary(); - return boundary->Length(); - } - - double X() { - Rcpp::stop("Can't compute X value of a non-point geography"); - } - - double Y() { - Rcpp::stop("Can't compute Y value of a non-point geography"); - } - - S2Point Centroid() { - return this->polygon->GetCentroid(); - } - - S2Cap GetCapBound() { - return this->polygon->GetCapBound(); - } - - S2LatLngRect GetRectBound() { - return this->polygon->GetRectBound(); - } - - std::unique_ptr Boundary() { - // PolylineGeography::Builder builder; - // std::vector> flatIndices = this->flatLoopIndices(); - - // // export multilinestring - // WKGeometryMeta meta(WKGeometryType::MultiLineString, false, false, false); - // meta.hasSize = true; - // meta.size = this->polygon->num_loops(); - - // builder.nextGeometryStart(meta, WKReader::PART_ID_NONE); - // int loopId = 0; - // for (size_t i = 0; i < flatIndices.size(); i++) { - // this->exportLoops(&builder, meta, flatIndices[i], loopId); - // loopId += flatIndices[i].size(); - // } - // builder.nextGeometryEnd(meta, WKReader::PART_ID_NONE); - - // return builder.build(); - throw std::runtime_error("inbetween implementations..."); - } - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { std::vector shapeIds(1); std::unique_ptr shape = absl::make_unique(); diff --git a/src/polyline-geography.h b/src/polyline-geography.h index e66ac064..1f06d7c0 100644 --- a/src/polyline-geography.h +++ b/src/polyline-geography.h @@ -24,110 +24,6 @@ class PolylineGeography: public Geography { return absl::make_unique(std::move(polylines_cpy)); } - Geography::Type GeographyType() { - return Geography::Type::GEOGRAPHY_POLYLINE; - } - - bool FindValidationError(S2Error* error) { - bool result; - error->Clear(); - for (size_t i = 0; i < this->polylines.size(); i++) { - result = this->polylines[i]->FindValidationError(error); - if (result) { - return result; - } - } - - return false; - } - - const std::vector>* Polyline() { - return &(this->polylines); - } - - bool IsCollection() { - return this->polylines.size() > 1; - } - - int Dimension() { - return 1; - } - - int NumPoints() { - int numPoints = 0; - for (size_t i = 0; i < this->polylines.size(); i++) { - numPoints += this->polylines[i]->num_vertices(); - } - - return numPoints; - } - - bool IsEmpty() { - for (size_t i = 0; i < this->polylines.size(); i++) { - if (this->polylines[i]->num_vertices() > 0) { - return false; - } - } - - return true; - } - - double Area() { - return 0; - } - - double Length() { - double length = 0; - for (size_t i = 0; i < this->polylines.size(); i++) { - length += this->polylines[i]->GetLength().radians(); - } - - return length; - } - - double Perimeter() { - return 0; - } - - double X() { - Rcpp::stop("Can't compute X value of a non-point geography"); - } - - double Y() { - Rcpp::stop("Can't compute Y value of a non-point geography"); - } - - S2Point Centroid() { - S2Point output(0, 0, 0); - for (size_t i = 0; i < this->polylines.size(); i++) { - output += this->polylines[i]->GetCentroid(); - } - - return output; - } - - S2LatLngRect GetRectBound() { - S2LatLngRect rect; - if (this->polylines.size()) - rect = this->polylines[0]->GetRectBound(); - for (size_t i = 1; i < this->polylines.size(); i++) { - rect.Union(this->polylines[i]->GetRectBound()); // depends on order - } - return rect; - } - - std::unique_ptr Boundary() { - std::vector endpoints; - for (size_t i = 0; i < this->polylines.size(); i++) { - if (this->polylines[i]->num_vertices() >= 2) { - endpoints.push_back(this->polylines[i]->vertex(0)); - endpoints.push_back(this->polylines[i]->vertex(1)); - } - } - - return absl::make_unique(endpoints); - } - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { std::vector shapeIds(this->polylines.size()); for (size_t i = 0; i < this->polylines.size(); i++) { diff --git a/src/s2-matrix.cpp b/src/s2-matrix.cpp index f576650f..3a158976 100644 --- a/src/s2-matrix.cpp +++ b/src/s2-matrix.cpp @@ -514,17 +514,12 @@ List cpp_s2_contains_matrix_brute_force(List geog1, List geog2, List s2options) Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - // by default Contains() will return true for Contains(x, EMPTY), which is - // not true in BigQuery or GEOS - if (feature2->IsEmpty()) { - return false; - } else { - return S2BooleanOperation::Contains( - *feature1->ShapeIndex(), - *feature2->ShapeIndex(), - this->options - ); - } + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); + + return s2geography::s2_contains(index1, index2, options); }; }; @@ -540,18 +535,12 @@ List cpp_s2_within_matrix_brute_force(List geog1, List geog2, List s2options) { bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { // note reversed index2, index1 + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); - // by default Contains() will return true for Contains(x, EMPTY), which is - // not true in BigQuery or GEOS - if (feature1->IsEmpty()) { - return false; - } else { - return S2BooleanOperation::Contains( - *feature2->ShapeIndex(), - *feature1->ShapeIndex(), - this->options - ); - } + return s2geography::s2_contains(index2, index1, options); }; }; @@ -566,11 +555,12 @@ List cpp_s2_intersects_matrix_brute_force(List geog1, List geog2, List s2options Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - return S2BooleanOperation::Intersects( - *feature1->ShapeIndex(), - *feature2->ShapeIndex(), - this->options - ); + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); + + return s2geography::s2_intersects(index1, index2, options); } }; @@ -585,11 +575,12 @@ List cpp_s2_disjoint_matrix_brute_force(List geog1, List geog2, List s2options) Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - return !S2BooleanOperation::Intersects( - *feature1->ShapeIndex(), - *feature2->ShapeIndex(), - this->options - ); + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); + + return !s2geography::s2_intersects(index1, index2, options); } }; @@ -604,11 +595,12 @@ List cpp_s2_equals_matrix_brute_force(List geog1, List geog2, List s2options) { Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - return S2BooleanOperation::Equals( - *feature1->ShapeIndex(), - *feature2->ShapeIndex(), - this->options - ); + auto geog1 = feature1->NewGeography(); + auto geog2 = feature2->NewGeography(); + s2geography::S2GeographyShapeIndex index1(*geog1); + s2geography::S2GeographyShapeIndex index2(*geog2); + + return s2geography::s2_equals(index1, index2, options); } }; From fe05448ab5ba7984c8b648ceafe3f9cffa286a49 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 13 Apr 2022 21:47:57 -0300 Subject: [PATCH 78/88] fix include --- src/s2-geography/constructor.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index a6142787..ed00db78 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -1,7 +1,9 @@ #pragma once -#include "geography.h" +#include + +#include "geography.hpp" namespace s2geography { From b4f207016b5c804844d09486710b91225d94e5e8 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 14 Apr 2022 08:58:00 -0300 Subject: [PATCH 79/88] include fewer headers, fix compile and test --- src/geography-collection.h | 1 - src/geography.h | 8 -------- src/point-geography.h | 2 -- tests/testthat/test-wk-utils.R | 4 ++-- 4 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/geography-collection.h b/src/geography-collection.h index 1f1fd42c..8e1406ef 100644 --- a/src/geography-collection.h +++ b/src/geography-collection.h @@ -2,7 +2,6 @@ #ifndef GEOGRAPHY_COLLECTION_H #define GEOGRAPHY_COLLECTION_H -#include #include "geography.h" // This class handles collections of other Geography diff --git a/src/geography.h b/src/geography.h index 4f3aba6c..7aa5e143 100644 --- a/src/geography.h +++ b/src/geography.h @@ -3,14 +3,6 @@ #define GEOGRAPHY_H #include -#include "s2/s2latlng.h" -#include "s2/s2polyline.h" -#include "s2/s2polygon.h" -#include "s2/s2shape_index.h" -#include "s2/s2shape_index_region.h" -#include "s2/mutable_s2shape_index.h" -#include "s2/s2point_vector_shape.h" -#include "s2/s2cap.h" #include "wk/geometry-handler.hpp" #include diff --git a/src/point-geography.h b/src/point-geography.h index f3bffd4a..385c0514 100644 --- a/src/point-geography.h +++ b/src/point-geography.h @@ -2,8 +2,6 @@ #ifndef POINT_GEOGRAPHY_H #define POINT_GEOGRAPHY_H -#include - #include "s2/s2latlng_rect.h" #include "geography.h" diff --git a/tests/testthat/test-wk-utils.R b/tests/testthat/test-wk-utils.R index 902df1a8..c265bcf1 100644 --- a/tests/testthat/test-wk-utils.R +++ b/tests/testthat/test-wk-utils.R @@ -189,13 +189,13 @@ test_that("the s2_geography_writer() works", { # nc has some rings that get reordered by this operation for (name in setdiff(names(s2_data_example_wkt), "nc")) { geog <- wk::wk_handle( - geoarrow::geoarrow_example_wkt[[name]], + s2_data_example_wkt[[name]], s2_geography_writer() ) expect_equal( wk::wk_coords(as_wkt(geog))[c("x", "y")], - wk::wk_coords(geoarrow::geoarrow_example_wkt[[name]])[c("x", "y")] + wk::wk_coords(s2_data_example_wkt[[name]])[c("x", "y")] ) } }) From 201c0bb2b4420bd55e728ec3b07893ff88c2185a Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 17 Apr 2022 14:01:12 -0300 Subject: [PATCH 80/88] wk_handle method for geography --- NAMESPACE | 2 + R/wk-utils.R | 6 + src/RcppExports.cpp | 2 + src/s2-constructors-formatters.cpp | 344 +++++++++++++++++++++++++++++ tests/testthat/test-wk-utils.R | 16 ++ 5 files changed, 370 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index 0e8d30da..02390de1 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -73,6 +73,7 @@ S3method(str,s2_cell_union) S3method(str,s2_xptr) S3method(unique,s2_cell) S3method(unlist,s2_cell_union) +S3method(wk_handle,s2_geography) export(as_s2_cell) export(as_s2_cell_union) export(as_s2_geography) @@ -202,4 +203,5 @@ importFrom(Rcpp,sourceCpp) importFrom(utils,str) importFrom(wk,as_wkb) importFrom(wk,as_wkt) +importFrom(wk,wk_handle) useDynLib(s2, .registration = TRUE) diff --git a/R/wk-utils.R b/R/wk-utils.R index 18cc9522..8dda9bf6 100644 --- a/R/wk-utils.R +++ b/R/wk-utils.R @@ -65,6 +65,12 @@ s2_projection_filter <- function(handler, projection = s2_projection_plate_carre ) } +#' @importFrom wk wk_handle +#' @export +wk_handle.s2_geography <- function(geog, handler, ...) { + .Call(c_s2_handle_geography, geog, wk::as_wk_handler(handler)) +} + s2_geography_writer <- function(oriented = FALSE, check = TRUE) { wk::new_wk_handler( .Call( diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 68bcf808..b46a0ee7 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -1416,6 +1416,7 @@ END_RCPP RcppExport SEXP c_s2_coord_filter_new(SEXP, SEXP, SEXP, SEXP); RcppExport SEXP c_s2_geography_writer_new(SEXP, SEXP); +RcppExport SEXP c_s2_handle_geography(SEXP, SEXP); RcppExport SEXP c_s2_projection_mercator(); RcppExport SEXP c_s2_projection_plate_carree(); @@ -1539,6 +1540,7 @@ static const R_CallMethodDef CallEntries[] = { {"_s2_s2_xptr_test_op", (DL_FUNC) &_s2_s2_xptr_test_op, 1}, {"c_s2_coord_filter_new", (DL_FUNC) &c_s2_coord_filter_new, 4}, {"c_s2_geography_writer_new", (DL_FUNC) &c_s2_geography_writer_new, 2}, + {"c_s2_handle_geography", (DL_FUNC) &c_s2_handle_geography, 2}, {"c_s2_projection_mercator", (DL_FUNC) &c_s2_projection_mercator, 0}, {"c_s2_projection_plate_carree", (DL_FUNC) &c_s2_projection_plate_carree, 0}, {NULL, NULL, 0} diff --git a/src/s2-constructors-formatters.cpp b/src/s2-constructors-formatters.cpp index a6e2576b..b44b6936 100644 --- a/src/s2-constructors-formatters.cpp +++ b/src/s2-constructors-formatters.cpp @@ -291,3 +291,347 @@ extern "C" SEXP c_s2_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { CPP_END } + +#define HANDLE_OR_RETURN(expr) \ + result = expr; \ + if (result != WK_CONTINUE) return result + +#define HANDLE_CONTINUE_OR_BREAK(expr) \ + result = expr; \ + if (result == WK_ABORT_FEATURE) continue; else if (result == WK_ABORT) break + + +int handle_points(const s2geography::S2GeographyOwningPoint& geog, wk_handler_t* handler, + uint32_t part_id = WK_PART_ID_NONE) { + int result; + + wk_meta_t meta; + WK_META_RESET(meta, WK_MULTIPOINT); + meta.size = geog.Points().size(); + + wk_meta_t meta_child; + WK_META_RESET(meta_child, WK_POINT); + meta_child.size = 1; + double coord[2]; + + if (meta.size == 0) { + meta_child.size = 0; + HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); + HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); + } else if (meta.size == 1) { + HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); + S2LatLng pt(geog.Points()[0]); + coord[0] = pt.lng().degrees(); + coord[1] = pt.lat().degrees(); + HANDLE_OR_RETURN(handler->coord(&meta_child, coord, 0, handler->handler_data)); + HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); + } else { + HANDLE_OR_RETURN(handler->geometry_start(&meta, part_id, handler->handler_data)); + + for (size_t i = 0; i < geog.Points().size(); i++) { + HANDLE_OR_RETURN(handler->geometry_start(&meta_child, i, handler->handler_data)); + S2LatLng pt(geog.Points()[i]); + coord[0] = pt.lng().degrees(); + coord[1] = pt.lat().degrees(); + HANDLE_OR_RETURN(handler->coord(&meta_child, coord, 0, handler->handler_data)); + HANDLE_OR_RETURN(handler->geometry_end(&meta_child, i, handler->handler_data)); + } + + HANDLE_OR_RETURN(handler->geometry_end(&meta, part_id, handler->handler_data)); + } + + return WK_CONTINUE; +} + +int handle_polylines(const s2geography::S2GeographyOwningPolyline& geog, wk_handler_t* handler, + uint32_t part_id = WK_PART_ID_NONE) { + int result; + + wk_meta_t meta; + WK_META_RESET(meta, WK_MULTILINESTRING); + meta.size = geog.Polylines().size(); + + wk_meta_t meta_child; + WK_META_RESET(meta_child, WK_LINESTRING); + double coord[2]; + + if (meta.size == 0) { + meta_child.size = 0; + HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); + HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); + } else if (meta.size == 1) { + const S2Polyline& poly = *geog.Polylines()[0]; + meta_child.size = poly.num_vertices(); + HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); + + for (int j = 0; j < poly.num_vertices(); j++) { + S2LatLng pt(poly.vertex(j)); + coord[0] = pt.lng().degrees(); + coord[1] = pt.lat().degrees(); + HANDLE_OR_RETURN(handler->coord(&meta_child, coord, j, handler->handler_data)); + } + + HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); + } else { + HANDLE_OR_RETURN(handler->geometry_start(&meta, part_id, handler->handler_data)); + + for (size_t i = 0; i < geog.Polylines().size(); i++) { + const S2Polyline& poly = *geog.Polylines()[i]; + meta_child.size = poly.num_vertices(); + HANDLE_OR_RETURN(handler->geometry_start(&meta_child, i, handler->handler_data)); + + for (int j = 0; j < poly.num_vertices(); j++) { + S2LatLng pt(poly.vertex(j)); + coord[0] = pt.lng().degrees(); + coord[1] = pt.lat().degrees(); + HANDLE_OR_RETURN(handler->coord(&meta_child, coord, j, handler->handler_data)); + } + + HANDLE_OR_RETURN(handler->geometry_end(&meta_child, i, handler->handler_data)); + } + + HANDLE_OR_RETURN(handler->geometry_end(&meta, part_id, handler->handler_data)); + } + + return WK_CONTINUE; +} + +int handle_loop_shell(const S2Loop* loop, const wk_meta_t* meta, uint32_t loop_id, wk_handler_t* handler) { + int result; + double coord[2]; + + if (loop->num_vertices() == 0) { + return handler->error("Unexpected S2Loop with 0 vertices", handler->handler_data); + } + + HANDLE_OR_RETURN(handler->ring_start(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); + + for (int i = 0; i < loop->num_vertices(); i++) { + S2LatLng pt(loop->vertex(i)); + coord[0] = pt.lng().degrees(); + coord[1] = pt.lat().degrees(); + HANDLE_OR_RETURN(handler->coord(meta, coord, i, handler->handler_data)); + } + + // close the loop + S2LatLng pt(loop->vertex(0)); + coord[0] = pt.lng().degrees(); + coord[1] = pt.lat().degrees(); + HANDLE_OR_RETURN(handler->coord(meta, coord, loop->num_vertices(), handler->handler_data)); + + HANDLE_OR_RETURN(handler->ring_end(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); + return WK_CONTINUE; +} + +int handle_loop_hole(const S2Loop* loop, const wk_meta_t* meta, uint32_t loop_id, wk_handler_t* handler) { + int result; + double coord[2]; + + if (loop->num_vertices() == 0) { + return handler->error("Unexpected S2Loop with 0 vertices", handler->handler_data); + } + + HANDLE_OR_RETURN(handler->ring_start(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); + + for (int i = loop->num_vertices() - 1; i >= 0; i--) { + S2LatLng pt(loop->vertex(i)); + coord[0] = pt.lng().degrees(); + coord[1] = pt.lat().degrees(); + HANDLE_OR_RETURN(handler->coord(meta, coord, i, handler->handler_data)); + } + + // close the loop + S2LatLng pt(loop->vertex(0)); + coord[0] = pt.lng().degrees(); + coord[1] = pt.lat().degrees(); + HANDLE_OR_RETURN(handler->coord(meta, coord, loop->num_vertices(), handler->handler_data)); + + HANDLE_OR_RETURN(handler->ring_end(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); + return WK_CONTINUE; +} + +int handle_shell(const S2Polygon& poly, const wk_meta_t* meta, int loop_start, wk_handler_t* handler) { + int result; + const S2Loop* loop0 = poly.loop(loop_start); + HANDLE_OR_RETURN(handle_loop_shell(loop0, meta, 0, handler)); + + uint32_t loop_id = 1; + for (int j = loop_start + 1; j <= poly.GetLastDescendant(loop_start); j++) { + const S2Loop* loop = poly.loop(j); + if (loop->depth() == (loop0->depth() + 1)) { + HANDLE_OR_RETURN(handle_loop_hole(loop, meta, loop_id, handler)); + loop_id++; + } + } + + return WK_CONTINUE; +} + +int handle_polygon(const s2geography::S2GeographyOwningPolygon& geog, wk_handler_t* handler, + uint32_t part_id = WK_PART_ID_NONE) { + const S2Polygon& poly = *geog.Polygon(); + + // find the outer shells (loop depth = 0, 2, 4, etc.) + std::vector outer_shell_loop_ids; + std::vector outer_shell_loop_sizes; + + outer_shell_loop_ids.reserve(poly.num_loops()); + for (int i = 0; i < poly.num_loops(); i++) { + if ((poly.loop(i)->depth() % 2) == 0) { + outer_shell_loop_ids.push_back(i); + } + } + + // count the number of rings in each + outer_shell_loop_sizes.reserve(outer_shell_loop_ids.size()); + for (const auto loop_start : outer_shell_loop_ids) { + const S2Loop* loop0 = poly.loop(loop_start); + int num_loops = 1; + + for (int j = loop_start + 1; j <= poly.GetLastDescendant(loop_start); j++) { + const S2Loop* loop = poly.loop(j); + num_loops += loop->depth() == (loop0->depth() + 1); + } + + outer_shell_loop_sizes.push_back(num_loops); + } + + int result; + + wk_meta_t meta; + WK_META_RESET(meta, WK_MULTIPOLYGON); + meta.size = outer_shell_loop_ids.size(); + + wk_meta_t meta_child; + WK_META_RESET(meta_child, WK_POLYGON); + + if (meta.size == 0) { + meta_child.size = 0; + HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); + HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); + } else if (meta.size == 1) { + meta_child.size = outer_shell_loop_sizes[0]; + HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); + HANDLE_OR_RETURN(handle_shell(poly, &meta_child, outer_shell_loop_ids[0], handler)); + HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); + } else { + HANDLE_OR_RETURN(handler->geometry_start(&meta, part_id, handler->handler_data)); + + for (size_t i = 0; i < outer_shell_loop_sizes.size(); i++) { + meta_child.size = outer_shell_loop_sizes[i]; + HANDLE_OR_RETURN(handler->geometry_start(&meta_child, i, handler->handler_data)); + HANDLE_OR_RETURN(handle_shell(poly, &meta_child, outer_shell_loop_ids[i], handler)); + HANDLE_OR_RETURN(handler->geometry_end(&meta_child, i, handler->handler_data)); + } + + HANDLE_OR_RETURN(handler->geometry_end(&meta, part_id, handler->handler_data)); + } + + return WK_CONTINUE; +} + +int handle_collection(const s2geography::S2GeographyCollection& geog, wk_handler_t* handler, + uint32_t part_id = WK_PART_ID_NONE) { + int result; + + wk_meta_t meta; + WK_META_RESET(meta, WK_GEOMETRYCOLLECTION); + meta.size = geog.Features().size(); + + HANDLE_OR_RETURN(handler->geometry_start(&meta, part_id, handler->handler_data)); + for (size_t i = 0; i < geog.Features().size(); i++) { + const s2geography::S2Geography* child_ptr = geog.Features()[i].get(); + + auto child_point = dynamic_cast(child_ptr); + if (child_point != nullptr) { + HANDLE_OR_RETURN(handle_points(*child_point, handler, i)); + continue; + } + + auto child_polyline = dynamic_cast(child_ptr); + if (child_polyline != nullptr) { + HANDLE_OR_RETURN(handle_polylines(*child_polyline, handler, i)); + continue; + } + + auto child_polygon = dynamic_cast(child_ptr); + if (child_polygon != nullptr) { + HANDLE_OR_RETURN(handle_polygon(*child_polygon, handler, i)); + continue; + } + + auto child_collection = dynamic_cast(child_ptr); + if (child_collection != nullptr) { + HANDLE_OR_RETURN(handle_collection(*child_collection, handler, i)); + continue; + } + + return handler->error("Unsupported S2Geography subclass", handler->handler_data); + } + HANDLE_OR_RETURN(handler->geometry_end(&meta, part_id, handler->handler_data)); + + return WK_CONTINUE; +} + +SEXP handle_geography(SEXP data, wk_handler_t* handler) { + R_xlen_t n_features = Rf_xlength(data); + + wk_vector_meta_t vector_meta; + WK_VECTOR_META_RESET(vector_meta, WK_GEOMETRY); + vector_meta.size = n_features; + vector_meta.flags |= WK_FLAG_DIMS_UNKNOWN; + + if (handler->vector_start(&vector_meta, handler->handler_data) == WK_CONTINUE) { + int result; + SEXP item; + + for (R_xlen_t i = 0; i < n_features; i++) { + item = VECTOR_ELT(data, i); + + HANDLE_CONTINUE_OR_BREAK(handler->feature_start(&vector_meta, i, handler->handler_data)); + + if (item == R_NilValue) { + HANDLE_CONTINUE_OR_BREAK(handler->null_feature(handler->handler_data)); + } else { + auto item_ptr = reinterpret_cast(R_ExternalPtrAddr(item)); + auto geog = item_ptr->NewGeography(); + const s2geography::S2Geography* geog_ptr = geog.get(); + + auto child_point = dynamic_cast(geog_ptr); + if (child_point != nullptr) { + HANDLE_CONTINUE_OR_BREAK(handle_points(*child_point, handler)); + } else { + auto child_polyline = dynamic_cast(geog_ptr); + if (child_polyline != nullptr) { + HANDLE_CONTINUE_OR_BREAK(handle_polylines(*child_polyline, handler)); + } else { + auto child_polygon = dynamic_cast(geog_ptr); + if (child_polygon != nullptr) { + HANDLE_CONTINUE_OR_BREAK(handle_polygon(*child_polygon, handler)); + } else { + auto child_collection = dynamic_cast(geog_ptr); + if (child_collection != nullptr) { + HANDLE_CONTINUE_OR_BREAK(handle_collection(*child_collection, handler)); + } else { + HANDLE_CONTINUE_OR_BREAK( + handler->error("Unsupported S2Geography subclass", handler->handler_data)); + } + } + } + } + } + + if (handler->feature_end(&vector_meta, i, handler->handler_data) == WK_ABORT) { + break; + } + } + } + + SEXP result = PROTECT(handler->vector_end(&vector_meta, handler->handler_data)); + UNPROTECT(1); + return result; +} + +extern "C" SEXP c_s2_handle_geography(SEXP data, SEXP handler_xptr) { + return wk_handler_run_xptr(&handle_geography, data, handler_xptr); +} diff --git a/tests/testthat/test-wk-utils.R b/tests/testthat/test-wk-utils.R index c265bcf1..a2d884e5 100644 --- a/tests/testthat/test-wk-utils.R +++ b/tests/testthat/test-wk-utils.R @@ -185,6 +185,22 @@ test_that("mercator projection works", { ) }) +test_that("wk_handle() for s2_geography works", { + for (name in names(s2_data_example_wkt)) { + geog <- wk::wk_handle( + s2_data_example_wkt[[name]], + s2_geography_writer() + ) + + geog2 <- wk::wk_handle( + geog, + s2_geography_writer(check = TRUE, oriented = TRUE) + ) + + expect_equal(wk::wk_coords(geog), wk::wk_coords(geog2)) + } +}) + test_that("the s2_geography_writer() works", { # nc has some rings that get reordered by this operation for (name in setdiff(names(s2_data_example_wkt), "nc")) { From 731f3133bc83764e04244e7078935f325424411d Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Sun, 17 Apr 2022 14:44:49 -0300 Subject: [PATCH 81/88] actually use new exporters --- R/RcppExports.R | 12 -- R/s2-constructors-formatters.R | 13 +- R/s2-geography.R | 20 ++- R/utils.R | 14 ++- src/RcppExports.cpp | 42 ------- src/geography-collection.h | 53 -------- src/geography-shim.h | 5 +- src/geography.h | 124 ++++++++++++++++--- src/point-geography.h | 86 ------------- src/polygon-geography.h | 190 ----------------------------- src/polyline-geography.h | 94 -------------- src/s2-bounds.cpp | 4 - src/s2-cell-union.cpp | 3 - src/s2-cell.cpp | 4 +- src/s2-constructors-formatters.cpp | 17 +-- src/s2-geography.cpp | 64 ---------- src/wk-geography.h | 35 ------ tests/testthat/test-s2-geography.R | 42 +++---- 18 files changed, 171 insertions(+), 651 deletions(-) delete mode 100644 src/geography-collection.h delete mode 100644 src/point-geography.h delete mode 100644 src/polygon-geography.h delete mode 100644 src/polyline-geography.h delete mode 100644 src/wk-geography.h diff --git a/R/RcppExports.R b/R/RcppExports.R index 9cfdfde6..06d3a941 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -257,18 +257,6 @@ s2_geography_full <- function(x) { .Call(`_s2_s2_geography_full`, x) } -s2_geography_to_wkt <- function(s2_geography, precision, trim) { - .Call(`_s2_s2_geography_to_wkt`, s2_geography, precision, trim) -} - -s2_geography_to_wkb <- function(s2_geography, endian) { - .Call(`_s2_s2_geography_to_wkb`, s2_geography, endian) -} - -s2_geography_format <- function(s2_geography, maxCoords, precision, trim) { - .Call(`_s2_s2_geography_format`, s2_geography, maxCoords, precision, trim) -} - s2_lnglat_from_numeric <- function(lng, lat) { .Call(`_s2_s2_lnglat_from_numeric`, lng, lat) } diff --git a/R/s2-constructors-formatters.R b/R/s2-constructors-formatters.R index 52c4151a..569f13c5 100644 --- a/R/s2-constructors-formatters.R +++ b/R/s2-constructors-formatters.R @@ -127,11 +127,20 @@ s2_geog_from_wkb <- function(wkb_bytes, oriented = FALSE, check = TRUE) { #' @rdname s2_geog_point #' @export s2_as_text <- function(x, precision = 16, trim = TRUE) { - s2_geography_to_wkt(as_s2_geography(x), precision = precision, trim = trim) + wkt <- wk::wk_handle( + as_s2_geography(x), + wk::wkt_writer(precision = precision, trim = trim) + ) + + attributes(wkt) <- NULL + wkt } #' @rdname s2_geog_point #' @export s2_as_binary <- function(x, endian = wk::wk_platform_endian()) { - structure(s2_geography_to_wkb(as_s2_geography(x), endian = endian), class = "blob") + structure( + wk::wk_handle(as_s2_geography(x), wk::wkb_writer(endian = endian)), + class = "blob" + ) } diff --git a/R/s2-geography.R b/R/s2-geography.R index 2e90fd2b..5a2280ba 100644 --- a/R/s2-geography.R +++ b/R/s2-geography.R @@ -136,22 +136,20 @@ as_s2_geography.logical <- function(x, ...) { #' @rdname as_s2_geography #' @export as_wkb.s2_geography <- function(x, ...) { - wk::new_wk_wkb( - s2_geography_to_wkb(x, wk::wk_platform_endian()), - crs = wk::wk_crs_longlat("WGS84"), - geodesic = TRUE - ) + wkb <- wk::wk_handle(x, wk::wkb_writer()) + wk::wk_is_geodesic(wkb) <- TRUE + wk::wk_crs(wkb) <- wk::wk_crs_longlat() + wkb } #' @importFrom wk as_wkt #' @rdname as_s2_geography #' @export as_wkt.s2_geography <- function(x, ...) { - wk::new_wk_wkt( - s2_geography_to_wkt(x, precision = 16, trim = TRUE), - crs = wk::wk_crs_longlat(), - geodesic = TRUE - ) + wkt <- wk::wk_handle(x, wk::wkt_writer()) + wk::wk_is_geodesic(wkt) <- TRUE + wk::wk_crs(wkt) <- wk::wk_crs_longlat() + wkt } @@ -171,7 +169,7 @@ as_wkt.s2_geography <- function(x, ...) { #' @export format.s2_geography <- function(x, ..., max_coords = 5, precision = 9, trim = TRUE) { - paste0("<", s2_geography_format(x, max_coords, precision, trim), ">") + wk::wk_format(x, precision = precision, max_coords = max_coords, trim = trim) } # this is what gets called by the RStudio viewer, for which diff --git a/R/utils.R b/R/utils.R index 8d6d38e6..f12af485 100644 --- a/R/utils.R +++ b/R/utils.R @@ -68,8 +68,18 @@ stop_problems <- function(feature_id, problem, header) { expect_wkt_equal <- function(x, y, precision = 16) { testthat::expect_equal( - s2_geography_to_wkt(as_s2_geography(x), precision = precision, trim = TRUE), - s2_geography_to_wkt(as_s2_geography(y), precision = precision, trim = TRUE) + wk::wk_format( + as_s2_geography(x), + precision = precision, + trim = TRUE, + max_coords = .Machine$integer.max + ), + wk::wk_format( + as_s2_geography(y), + precision = precision, + trim = TRUE, + max_coords = .Machine$integer.max + ) ) } diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index b46a0ee7..cd87e86d 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -748,45 +748,6 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } -// s2_geography_to_wkt -CharacterVector s2_geography_to_wkt(List s2_geography, int precision, bool trim); -RcppExport SEXP _s2_s2_geography_to_wkt(SEXP s2_geographySEXP, SEXP precisionSEXP, SEXP trimSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< List >::type s2_geography(s2_geographySEXP); - Rcpp::traits::input_parameter< int >::type precision(precisionSEXP); - Rcpp::traits::input_parameter< bool >::type trim(trimSEXP); - rcpp_result_gen = Rcpp::wrap(s2_geography_to_wkt(s2_geography, precision, trim)); - return rcpp_result_gen; -END_RCPP -} -// s2_geography_to_wkb -List s2_geography_to_wkb(List s2_geography, int endian); -RcppExport SEXP _s2_s2_geography_to_wkb(SEXP s2_geographySEXP, SEXP endianSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< List >::type s2_geography(s2_geographySEXP); - Rcpp::traits::input_parameter< int >::type endian(endianSEXP); - rcpp_result_gen = Rcpp::wrap(s2_geography_to_wkb(s2_geography, endian)); - return rcpp_result_gen; -END_RCPP -} -// s2_geography_format -CharacterVector s2_geography_format(List s2_geography, int maxCoords, int precision, bool trim); -RcppExport SEXP _s2_s2_geography_format(SEXP s2_geographySEXP, SEXP maxCoordsSEXP, SEXP precisionSEXP, SEXP trimSEXP) { -BEGIN_RCPP - Rcpp::RObject rcpp_result_gen; - Rcpp::RNGScope rcpp_rngScope_gen; - Rcpp::traits::input_parameter< List >::type s2_geography(s2_geographySEXP); - Rcpp::traits::input_parameter< int >::type maxCoords(maxCoordsSEXP); - Rcpp::traits::input_parameter< int >::type precision(precisionSEXP); - Rcpp::traits::input_parameter< bool >::type trim(trimSEXP); - rcpp_result_gen = Rcpp::wrap(s2_geography_format(s2_geography, maxCoords, precision, trim)); - return rcpp_result_gen; -END_RCPP -} // s2_lnglat_from_numeric List s2_lnglat_from_numeric(NumericVector lng, NumericVector lat); RcppExport SEXP _s2_s2_lnglat_from_numeric(SEXP lngSEXP, SEXP latSEXP) { @@ -1485,9 +1446,6 @@ static const R_CallMethodDef CallEntries[] = { {"_s2_cpp_s2_cell_common_ancestor_level", (DL_FUNC) &_s2_cpp_s2_cell_common_ancestor_level, 2}, {"_s2_cpp_s2_cell_common_ancestor_level_agg", (DL_FUNC) &_s2_cpp_s2_cell_common_ancestor_level_agg, 1}, {"_s2_s2_geography_full", (DL_FUNC) &_s2_s2_geography_full, 1}, - {"_s2_s2_geography_to_wkt", (DL_FUNC) &_s2_s2_geography_to_wkt, 3}, - {"_s2_s2_geography_to_wkb", (DL_FUNC) &_s2_s2_geography_to_wkb, 2}, - {"_s2_s2_geography_format", (DL_FUNC) &_s2_s2_geography_format, 4}, {"_s2_s2_lnglat_from_numeric", (DL_FUNC) &_s2_s2_lnglat_from_numeric, 2}, {"_s2_s2_lnglat_from_s2_point", (DL_FUNC) &_s2_s2_lnglat_from_s2_point, 1}, {"_s2_data_frame_from_s2_lnglat", (DL_FUNC) &_s2_data_frame_from_s2_lnglat, 1}, diff --git a/src/geography-collection.h b/src/geography-collection.h deleted file mode 100644 index 8e1406ef..00000000 --- a/src/geography-collection.h +++ /dev/null @@ -1,53 +0,0 @@ - -#ifndef GEOGRAPHY_COLLECTION_H -#define GEOGRAPHY_COLLECTION_H - -#include "geography.h" - -// This class handles collections of other Geography -// objects. -class GeographyCollection: public Geography { -public: - GeographyCollection(): features(0) {} - GeographyCollection(std::vector> features): - features(std::move(features)) {} - - std::unique_ptr NewGeography() { - std::vector> features_cpy; - features_cpy.reserve(features.size()); - - for (const auto& feature : features) { - features_cpy.push_back(feature->NewGeography()); - } - - return absl::make_unique(std::move(features_cpy)); - } - - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { - std::vector shapeIds; - for (size_t i = 0; i < this->features.size(); i++) { - std::vector newShapeIds = this->features[i]->BuildShapeIndex(index); - for (size_t j = 0; j < newShapeIds.size(); j++) { - shapeIds.push_back(newShapeIds[j]); - } - } - return shapeIds; - } - - void Export(WKGeometryHandler* handler, uint32_t partId) { - WKGeometryMeta meta(WKGeometryType::GeometryCollection, false, false, false); - meta.hasSize = true; - meta.size = this->features.size(); - - handler->nextGeometryStart(meta, partId); - for (size_t i = 0; i < this->features.size(); i++) { - this->features[i]->Export(handler, i); - } - handler->nextGeometryEnd(meta, partId); - } - -private: - std::vector> features; -}; - -#endif diff --git a/src/geography-shim.h b/src/geography-shim.h index a7e3bf93..14c4f61f 100644 --- a/src/geography-shim.h +++ b/src/geography-shim.h @@ -2,10 +2,7 @@ #ifndef S2_GEOGRAPHY_SHIM_H_INCLUDED #define S2_GEOGRAPHY_SHIM_H_INCLUDED -#include "point-geography.h" -#include "polyline-geography.h" -#include "polygon-geography.h" -#include "geography-collection.h" +#include "geography.h" #include "s2-geography/s2-geography.hpp" static inline std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog) { diff --git a/src/geography.h b/src/geography.h index 7aa5e143..41078175 100644 --- a/src/geography.h +++ b/src/geography.h @@ -3,7 +3,6 @@ #define GEOGRAPHY_H #include -#include "wk/geometry-handler.hpp" #include #include "s2-geography/s2-geography.hpp" @@ -11,15 +10,6 @@ class Geography { public: - - enum class Type { - GEOGRAPHY_EMPTY, - GEOGRAPHY_POINT, - GEOGRAPHY_POLYLINE, - GEOGRAPHY_POLYGON, - GEOGRAPHY_COLLECTION - }; - Geography(): hasIndex(false) {} virtual std::unique_ptr NewGeography() = 0; @@ -30,10 +20,6 @@ class Geography { // can keep track of which shape came from which feature. virtual std::vector BuildShapeIndex(MutableS2ShapeIndex* index) = 0; - // the factory handler is responsible for building these objects - // but exporting can be done here - virtual void Export(WKGeometryHandler* handler, uint32_t partId) = 0; - virtual ~Geography() {} // other calculations use ShapeIndex @@ -51,4 +37,114 @@ class Geography { bool hasIndex; }; +class PointGeography: public Geography { +public: + PointGeography(): points(0) {} + PointGeography(S2Point point): points(1) { + this->points[0] = point; + } + PointGeography(std::vector points): points(points) {} + + std::unique_ptr NewGeography() { + return absl::make_unique(points); + } + + std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { + std::vector shapeIds(1); + std::vector pointsCopy(this->points); + + shapeIds[0] = index->Add(std::unique_ptr( + new S2PointVectorShape(std::move(pointsCopy))) + ); + return shapeIds; + } + +private: + std::vector points; +}; + +class PolylineGeography: public Geography { +public: + PolylineGeography(): polylines(0) {} + PolylineGeography(std::vector> polylines): + polylines(std::move(polylines)) {} + + std::unique_ptr NewGeography() { + std::vector> polylines_cpy; + + for (const auto& polyline : polylines) { + polylines_cpy.push_back(std::unique_ptr(polyline->Clone())); + } + + return absl::make_unique(std::move(polylines_cpy)); + } + + std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { + std::vector shapeIds(this->polylines.size()); + for (size_t i = 0; i < this->polylines.size(); i++) { + std::unique_ptr shape = absl::make_unique(); + shape->Init(this->polylines[i].get()); + shapeIds[i] = index->Add(std::move(shape)); + } + return shapeIds; + } + +private: + std::vector> polylines; +}; + +class PolygonGeography: public Geography { +public: + PolygonGeography() {} + PolygonGeography(std::unique_ptr polygon): + polygon(std::move(polygon)) {} + + std::unique_ptr NewGeography() { + return absl::make_unique(std::unique_ptr(polygon->Clone())); + } + + std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { + std::vector shapeIds(1); + std::unique_ptr shape = absl::make_unique(); + shape->Init(this->polygon.get()); + shapeIds[0] = index->Add(std::move(shape)); + return shapeIds; + } + +private: + std::unique_ptr polygon; +}; + +class GeographyCollection: public Geography { +public: + GeographyCollection(): features(0) {} + GeographyCollection(std::vector> features): + features(std::move(features)) {} + + std::unique_ptr NewGeography() { + std::vector> features_cpy; + features_cpy.reserve(features.size()); + + for (const auto& feature : features) { + features_cpy.push_back(feature->NewGeography()); + } + + return absl::make_unique(std::move(features_cpy)); + } + + std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { + std::vector shapeIds; + for (size_t i = 0; i < this->features.size(); i++) { + std::vector newShapeIds = this->features[i]->BuildShapeIndex(index); + for (size_t j = 0; j < newShapeIds.size(); j++) { + shapeIds.push_back(newShapeIds[j]); + } + } + return shapeIds; + } + +private: + std::vector> features; +}; + #endif diff --git a/src/point-geography.h b/src/point-geography.h deleted file mode 100644 index 385c0514..00000000 --- a/src/point-geography.h +++ /dev/null @@ -1,86 +0,0 @@ - -#ifndef POINT_GEOGRAPHY_H -#define POINT_GEOGRAPHY_H - -#include "s2/s2latlng_rect.h" - -#include "geography.h" - -// This class handles both points and multipoints, as this is how -// points are generally returned/required in S2 (vector of S2Point) -// This is similar to an S2PointVectorLayer -class PointGeography: public Geography { -public: - PointGeography(): points(0) {} - PointGeography(S2Point point): points(1) { - this->points[0] = point; - } - PointGeography(std::vector points): points(points) {} - - std::unique_ptr NewGeography() { - return absl::make_unique(points); - } - - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { - std::vector shapeIds(1); - std::vector pointsCopy(this->points); - - shapeIds[0] = index->Add(std::unique_ptr( - new S2PointVectorShape(std::move(pointsCopy))) - ); - return shapeIds; - } - - void Export(WKGeometryHandler* handler, uint32_t partId) { - S2LatLng point; - - if (this->points.size() > 1) { - // export multipoint - WKGeometryMeta meta(WKGeometryType::MultiPoint, false, false, false); - meta.hasSize = true; - meta.size = this->points.size(); - - WKGeometryMeta childMeta(WKGeometryType::Point, false, false, false); - childMeta.hasSize = true; - childMeta.size = 1; - - handler->nextGeometryStart(meta, partId); - - for (size_t i = 0; i < this->points.size(); i++) { - point = S2LatLng(this->points[i]); - - handler->nextGeometryStart(childMeta, i); - handler->nextCoordinate(meta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), 0); - handler->nextGeometryEnd(childMeta, i); - } - - handler->nextGeometryEnd(meta, partId); - - } else if (this->points.size() > 0) { - // export point - WKGeometryMeta meta(WKGeometryType::Point, false, false, false); - meta.hasSize = true; - meta.size = this->points.size(); - - handler->nextGeometryStart(meta, partId); - - point = S2LatLng(this->points[0]); - handler->nextCoordinate(meta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), 0); - - handler->nextGeometryEnd(meta, partId); - } else { - // export empty point - // export point - WKGeometryMeta meta(WKGeometryType::Point, false, false, false); - meta.hasSize = true; - meta.size = 0; - handler->nextGeometryStart(meta, partId); - handler->nextGeometryEnd(meta, partId); - } - } - -private: - std::vector points; -}; - -#endif diff --git a/src/polygon-geography.h b/src/polygon-geography.h deleted file mode 100644 index eec44efd..00000000 --- a/src/polygon-geography.h +++ /dev/null @@ -1,190 +0,0 @@ - -#ifndef POLYGON_GEOGRAPHY_H -#define POLYGON_GEOGRAPHY_H - -#include "wk/reader.hpp" - -#include "geography.h" -#include "point-geography.h" -#include "polyline-geography.h" - -// This class handles polygons (POLYGON and MULTIPOLYGON) -// This is similar to an S2PolygonLayer -class PolygonGeography: public Geography { -public: - PolygonGeography() {} - PolygonGeography(std::unique_ptr polygon): - polygon(std::move(polygon)) {} - - std::unique_ptr NewGeography() { - return absl::make_unique(std::unique_ptr(polygon->Clone())); - } - - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { - std::vector shapeIds(1); - std::unique_ptr shape = absl::make_unique(); - shape->Init(this->polygon.get()); - shapeIds[0] = index->Add(std::move(shape)); - return shapeIds; - } - - void Export(WKGeometryHandler* handler, uint32_t partId) { - std::vector> flatIndices = this->flatLoopIndices(); - - if (flatIndices.size() > 1) { - // export multipolygon - WKGeometryMeta meta(WKGeometryType::MultiPolygon, false, false, false); - meta.hasSize = true; - meta.size = flatIndices.size(); - - WKGeometryMeta childMeta(WKGeometryType::Polygon, false, false, false); - childMeta.hasSize = true; - - handler->nextGeometryStart(meta, partId); - for (size_t i = 0; i < flatIndices.size(); i++) { - childMeta.size = flatIndices[i].size(); - handler->nextGeometryStart(childMeta, i); - this->exportLoops(handler, childMeta, flatIndices[i]); - handler->nextGeometryEnd(childMeta, i); - } - - handler->nextGeometryEnd(meta, partId); - - } else if (flatIndices.size() > 0) { - // export polygon - WKGeometryMeta meta(WKGeometryType::Polygon, false, false, false); - meta.hasSize = true; - meta.size = flatIndices[0].size(); - handler->nextGeometryStart(meta, partId); - this->exportLoops(handler, meta, flatIndices[0]); - handler->nextGeometryEnd(meta, partId); - - } else { - // export empty polygon - WKGeometryMeta meta(WKGeometryType::Polygon, false, false, false); - meta.hasSize = true; - meta.size = 0; - handler->nextGeometryStart(meta, partId); - handler->nextGeometryEnd(meta, partId); - } - } - -private: - std::unique_ptr polygon; - - // Calculate which loops in the polygon are outer loops (loop->depth() == 0) - std::vector outerLoopIndices() { - std::vector indices; - for (int i = 0; i < this->polygon->num_loops(); i++) { - if (this->polygon->loop(i)->depth() == 0) { - indices.push_back(i); - } - } - - return indices; - } - - // Calculate the arrangement of loops in the form of a multipolygon - // (list(list(shell, !!! holes))) - std::vector> flatLoopIndices() { - std::vector outerLoops = this->outerLoopIndices(); - - std::vector> flatIndices(outerLoops.size()); - for (size_t i = 0; i < outerLoops.size(); i++) { - int k = outerLoops[i]; - flatIndices[i] = std::vector(); - - // the first loop here is the shell (depth == 0) - flatIndices[i].push_back(k); - - // loops in the S2Polygon are arranged such that child loops are - // directly after the outer loop, so add all loop indices before - // the next parent loop (or end of polygon). This is similar to - // S2Polygon::GetLastDescendant() but is slightly easier to understand. - while (++k < this->polygon->num_loops() && this->polygon->loop(k)->depth() > 0) { - flatIndices[i].push_back(k); - } - } - - return flatIndices; - } - - void exportLoops(WKGeometryHandler* handler, WKGeometryMeta meta, - const std::vector& loopIndices, int loopIdOffset = 0) { - S2LatLng point; - - for (size_t i = 0; i < loopIndices.size(); i++) { - int loopId = loopIndices[i]; - S2Loop* loop = this->polygon->loop(loopId); - if (loop->num_vertices() == 0) { - continue; - } - - // this is a slightly ugly way to make it possible to export either the - // boundaries or the loops using the same code - WKGeometryMeta childMeta(WKGeometryType::LineString, false, false, false); - childMeta.hasSize = true; - childMeta.size = loop->num_vertices() + 1; - - WKGeometryMeta coordMeta; - - if (meta.geometryType == WKGeometryType::Polygon) { - handler->nextLinearRingStart(meta, loop->num_vertices() + 1, i + loopIdOffset); - coordMeta = meta; - } else if (meta.geometryType == WKGeometryType::MultiLineString) { - handler->nextGeometryStart(childMeta, i + loopIdOffset); - coordMeta = childMeta; - } else { - std::stringstream err; - err << "Can't export S2Loop with parent geometry type " << meta.geometryType; - Rcpp::stop(err.str()); - } - - if ((loop->depth() % 2) == 0) { - // if this is the first ring, use the internal vertex order - for (int j = 0; j < loop->num_vertices(); j++) { - point = S2LatLng(loop->vertex(j)); - handler->nextCoordinate( - coordMeta, - WKCoord::xy(point.lng().degrees(), point.lat().degrees()), - j - ); - } - - // close the loop! - point = S2LatLng(loop->vertex(0)); - handler->nextCoordinate( - coordMeta, - WKCoord::xy(point.lng().degrees(), point.lat().degrees()), - loop->num_vertices() - ); - } else { - // if an interior ring, reverse the vertex order - for (int j = 0; j < loop->num_vertices(); j++) { - point = S2LatLng(loop->vertex(loop->num_vertices() - 1 - j)); - handler->nextCoordinate( - coordMeta, - WKCoord::xy(point.lng().degrees(), point.lat().degrees()), - j - ); - } - - // close the loop! - point = S2LatLng(loop->vertex(loop->num_vertices() - 1)); - handler->nextCoordinate( - coordMeta, - WKCoord::xy(point.lng().degrees(), point.lat().degrees()), - loop->num_vertices() - ); - } - - if (meta.geometryType == WKGeometryType::Polygon) { - handler->nextLinearRingEnd(meta, loop->num_vertices() + 1, i + loopIdOffset); - } else if (meta.geometryType == WKGeometryType::MultiLineString) { - handler->nextGeometryEnd(childMeta, i + loopIdOffset); - } - } - } -}; - -#endif diff --git a/src/polyline-geography.h b/src/polyline-geography.h deleted file mode 100644 index 1f06d7c0..00000000 --- a/src/polyline-geography.h +++ /dev/null @@ -1,94 +0,0 @@ - -#ifndef POLYLINE_GEOGRAPHY_H -#define POLYLINE_GEOGRAPHY_H - -#include "s2/s2latlng_rect.h" - -#include "geography.h" - -// This class handles (vectors of) polylines (LINESTRING and MULTILINESTRING) -// This is similar to an S2PolylineVectorLayer -class PolylineGeography: public Geography { -public: - PolylineGeography(): polylines(0) {} - PolylineGeography(std::vector> polylines): - polylines(std::move(polylines)) {} - - std::unique_ptr NewGeography() { - std::vector> polylines_cpy; - - for (const auto& polyline : polylines) { - polylines_cpy.push_back(std::unique_ptr(polyline->Clone())); - } - - return absl::make_unique(std::move(polylines_cpy)); - } - - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { - std::vector shapeIds(this->polylines.size()); - for (size_t i = 0; i < this->polylines.size(); i++) { - std::unique_ptr shape = absl::make_unique(); - shape->Init(this->polylines[i].get()); - shapeIds[i] = index->Add(std::move(shape)); - } - return shapeIds; - } - - void Export(WKGeometryHandler* handler, uint32_t partId) { - S2LatLng point; - - if (this->polylines.size() > 1) { - // export multilinestring - WKGeometryMeta meta(WKGeometryType::MultiLineString, false, false, false); - meta.hasSize = true; - meta.size = this->polylines.size(); - - handler->nextGeometryStart(meta, partId); - - for (size_t i = 0; i < this->polylines.size(); i++) { - WKGeometryMeta childMeta(WKGeometryType::LineString, false, false, false); - childMeta.hasSize = true; - childMeta.size = this->polylines[i]->num_vertices(); - - handler->nextGeometryStart(childMeta, i); - - for (size_t j = 0; j < childMeta.size; j++) { - point = S2LatLng(this->polylines[i]->vertex(j)); - handler->nextCoordinate(meta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), j); - } - - handler->nextGeometryEnd(childMeta, i); - } - - handler->nextGeometryEnd(meta, partId); - - } else if (this->polylines.size() > 0) { - // export linestring - WKGeometryMeta meta(WKGeometryType::LineString, false, false, false); - meta.hasSize = true; - meta.size = this->polylines[0]->num_vertices(); - - handler->nextGeometryStart(meta, partId); - - for (size_t i = 0; i < meta.size; i++) { - point = S2LatLng(this->polylines[0]->vertex(i)); - handler->nextCoordinate(meta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), i); - } - - handler->nextGeometryEnd(meta, partId); - - } else { - // export empty linestring - WKGeometryMeta meta(WKGeometryType::LineString, false, false, false); - meta.hasSize = true; - meta.size = 0; - handler->nextGeometryStart(meta, partId); - handler->nextGeometryEnd(meta, partId); - } - } - -private: - std::vector> polylines; -}; - -#endif diff --git a/src/s2-bounds.cpp b/src/s2-bounds.cpp index 6dd486fa..1a41bca6 100644 --- a/src/s2-bounds.cpp +++ b/src/s2-bounds.cpp @@ -4,10 +4,6 @@ #include "s2-options.h" #include "geography-operator.h" -#include "point-geography.h" -#include "polyline-geography.h" -#include "polygon-geography.h" -#include "geography-collection.h" #include using namespace Rcpp; diff --git a/src/s2-cell-union.cpp b/src/s2-cell-union.cpp index 3710c656..29a62425 100644 --- a/src/s2-cell-union.cpp +++ b/src/s2-cell-union.cpp @@ -8,9 +8,6 @@ #include "s2/s2region_union.h" #include "geography-operator.h" -#include "point-geography.h" -#include "polyline-geography.h" -#include "polygon-geography.h" #include using namespace Rcpp; diff --git a/src/s2-cell.cpp b/src/s2-cell.cpp index 12251166..b4ee52d4 100644 --- a/src/s2-cell.cpp +++ b/src/s2-cell.cpp @@ -9,9 +9,7 @@ #include "s2/s2cell.h" #include "s2/s2latlng.h" -#include "point-geography.h" -#include "polyline-geography.h" -#include "polygon-geography.h" +#include "geography.h" #include using namespace Rcpp; diff --git a/src/s2-constructors-formatters.cpp b/src/s2-constructors-formatters.cpp index b44b6936..5f9d0754 100644 --- a/src/s2-constructors-formatters.cpp +++ b/src/s2-constructors-formatters.cpp @@ -406,19 +406,13 @@ int handle_loop_shell(const S2Loop* loop, const wk_meta_t* meta, uint32_t loop_i HANDLE_OR_RETURN(handler->ring_start(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); - for (int i = 0; i < loop->num_vertices(); i++) { + for (int i = 0; i <= loop->num_vertices(); i++) { S2LatLng pt(loop->vertex(i)); coord[0] = pt.lng().degrees(); coord[1] = pt.lat().degrees(); HANDLE_OR_RETURN(handler->coord(meta, coord, i, handler->handler_data)); } - // close the loop - S2LatLng pt(loop->vertex(0)); - coord[0] = pt.lng().degrees(); - coord[1] = pt.lat().degrees(); - HANDLE_OR_RETURN(handler->coord(meta, coord, loop->num_vertices(), handler->handler_data)); - HANDLE_OR_RETURN(handler->ring_end(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); return WK_CONTINUE; } @@ -433,18 +427,19 @@ int handle_loop_hole(const S2Loop* loop, const wk_meta_t* meta, uint32_t loop_id HANDLE_OR_RETURN(handler->ring_start(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); + uint32_t coord_id = 0; for (int i = loop->num_vertices() - 1; i >= 0; i--) { S2LatLng pt(loop->vertex(i)); coord[0] = pt.lng().degrees(); coord[1] = pt.lat().degrees(); - HANDLE_OR_RETURN(handler->coord(meta, coord, i, handler->handler_data)); + HANDLE_OR_RETURN(handler->coord(meta, coord, coord_id, handler->handler_data)); + coord_id++; } - // close the loop - S2LatLng pt(loop->vertex(0)); + S2LatLng pt(loop->vertex(loop->num_vertices() - 1)); coord[0] = pt.lng().degrees(); coord[1] = pt.lat().degrees(); - HANDLE_OR_RETURN(handler->coord(meta, coord, loop->num_vertices(), handler->handler_data)); + HANDLE_OR_RETURN(handler->coord(meta, coord, coord_id, handler->handler_data)); HANDLE_OR_RETURN(handler->ring_end(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); return WK_CONTINUE; diff --git a/src/s2-geography.cpp b/src/s2-geography.cpp index 6862a7ee..b3f27e7d 100644 --- a/src/s2-geography.cpp +++ b/src/s2-geography.cpp @@ -3,15 +3,7 @@ #include "s2/s2polyline.h" #include "s2/s2polygon.h" -#include "wk/wkb-reader.hpp" -#include "wk/wkt-reader.hpp" -#include "wk/wkb-writer.hpp" -#include "wk/wkt-writer.hpp" -#include "wk/geometry-formatter.hpp" - #include "geography.h" -#include "polygon-geography.h" -#include "wk-geography.h" #include using namespace Rcpp; @@ -25,59 +17,3 @@ List s2_geography_full(LogicalVector x) { // create single geography with full p ret(0) = Rcpp::XPtr(pg); return ret; } - -// [[Rcpp::export]] -CharacterVector s2_geography_to_wkt(List s2_geography, int precision, bool trim) { - WKRcppSEXPProvider provider(s2_geography); - WKGeographyReader reader(provider); - - WKCharacterVectorExporter exporter(reader.nFeatures()); - exporter.setRoundingPrecision(precision); - exporter.setTrim(trim); - WKTWriter writer(exporter); - - reader.setHandler(&writer); - while (reader.hasNextFeature()) { - checkUserInterrupt(); - reader.iterateFeature(); - } - - return exporter.output; -} - -// [[Rcpp::export]] -List s2_geography_to_wkb(List s2_geography, int endian) { - WKRcppSEXPProvider provider(s2_geography); - WKGeographyReader reader(provider); - - WKRawVectorListExporter exporter(reader.nFeatures()); - WKBWriter writer(exporter); - writer.setEndian(endian); - - reader.setHandler(&writer); - while (reader.hasNextFeature()) { - checkUserInterrupt(); - reader.iterateFeature(); - } - - return exporter.output; -} - -// [[Rcpp::export]] -CharacterVector s2_geography_format(List s2_geography, int maxCoords, int precision, bool trim) { - WKRcppSEXPProvider provider(s2_geography); - WKGeographyReader reader(provider); - - WKCharacterVectorExporter exporter(s2_geography.size()); - exporter.setRoundingPrecision(precision); - exporter.setTrim(trim); - WKGeometryFormatter formatter(exporter, maxCoords); - - reader.setHandler(&formatter); - while (reader.hasNextFeature()) { - checkUserInterrupt(); - reader.iterateFeature(); - } - - return exporter.output; -} diff --git a/src/wk-geography.h b/src/wk-geography.h deleted file mode 100644 index f0c0f55e..00000000 --- a/src/wk-geography.h +++ /dev/null @@ -1,35 +0,0 @@ - -#ifndef WK_GEOGRAPHY_H -#define WK_GEOGRAPHY_H - -#include "wk/rcpp-io.hpp" -#include "wk/reader.hpp" - -#include -#include "geography.h" - - -class WKGeographyReader: public WKReader { -public: - - WKGeographyReader(WKRcppSEXPProvider& provider): - WKReader(provider), provider(provider) {} - - void readFeature(size_t featureId) { - this->handler->nextFeatureStart(featureId); - - if (this->provider.featureIsNull()) { - this->handler->nextNull(featureId); - } else { - Rcpp::XPtr geography(this->provider.feature()); - geography->Export(handler, WKReader::PART_ID_NONE); - } - - this->handler->nextFeatureEnd(featureId); - } - -private: - WKRcppSEXPProvider& provider; -}; - -#endif diff --git a/tests/testthat/test-s2-geography.R b/tests/testthat/test-s2-geography.R index 106a672e..80d86af5 100644 --- a/tests/testthat/test-s2-geography.R +++ b/tests/testthat/test-s2-geography.R @@ -38,7 +38,7 @@ test_that("s2_geography vectors can be created from s2_lnglat and s2_point", { test_that("s2_geography vectors can be created from WKB and WKT", { wkb_point <- wk::as_wkb(wk::wkt("POINT (-64 45)", geodesic = TRUE)) - expect_output(print(as_s2_geography(wkb_point)), "") + expect_output(print(as_s2_geography(wkb_point)), "POINT \\(-64 45\\)") expect_error( as_s2_geography(wk::as_wkb("LINESTRING (0 0, 1 1)")), "Cartesian wkb\\(\\)" @@ -51,7 +51,7 @@ test_that("s2_geography vectors can be created from WKB and WKT", { expect_silent(as_s2_geography(wk::as_wkb("MULTIPOINT (0 1)"))) wkt_point <- wk::as_wkt(wk::wkt("POINT (-64 45)", geodesic = TRUE)) - expect_output(print(as_s2_geography(wkt_point)), "") + expect_output(print(as_s2_geography(wkt_point)), "POINT \\(-64 45\\)") expect_error( as_s2_geography(wk::wkt("LINESTRING (0 0, 1 1)")), "Cartesian wkt\\(\\)" @@ -64,8 +64,8 @@ test_that("s2_geography vectors can be created from WKB and WKT", { expect_silent(as_s2_geography(wk::wkt("MULTIPOINT (0 1)"))) # also test other classes commonly used to signify WKB or WKT - expect_output(print(as_s2_geography(structure(wkb_point, class = "WKB")), "")) - expect_output(print(as_s2_geography(structure(wkb_point, class = "blob")), "")) + expect_output(print(as_s2_geography(structure(wkb_point, class = "WKB")), "POINT \\(-64 45\\)")) + expect_output(print(as_s2_geography(structure(wkb_point, class = "blob")), "POINT \\(-64 45\\)")) }) test_that("s2_geography can be exported to WKB/WKT", { @@ -82,34 +82,34 @@ test_that("s2_geography can be exported to WKB/WKT", { }) test_that("s2_geography vectors can be created from wkt", { - expect_output(print(as_s2_geography("POINT (-64 45)")), "") - expect_output(print(as_s2_geography("POINT EMPTY")), "") + expect_output(print(as_s2_geography("POINT (-64 45)")), "POINT \\(-64 45\\)") + expect_output(print(as_s2_geography("POINT EMPTY")), "POINT EMPTY") expect_output( print(as_s2_geography("MULTIPOINT ((-64 45), (30 10))")), - "" + "MULTIPOINT \\(\\(-64 45\\), \\(30 10\\)\\)" ) expect_output( print(as_s2_geography("LINESTRING (-64 45, 0 0)")), - "" + "LINESTRING \\(-64 45, 0 0\\)" ) expect_output( print(as_s2_geography("LINESTRING EMPTY")), - "" + "LINESTRING EMPTY" ) expect_output( print(as_s2_geography("MULTILINESTRING ((-64 45, 0 0), (0 1, 2 3))")), - "" + "MULTILINESTRING \\(\\(-64 45, 0 0), \\(0 1, 2 3\\)\\)" ) - expect_output(print(as_s2_geography("POLYGON EMPTY"), "")) + expect_output(print(as_s2_geography("POLYGON EMPTY"), "POLYGON EMPTY")) expect_output( print(as_s2_geography("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")), - "" + "GEOMETRYCOLLECTION \\(POINT \\(-64 45\\)\\)" ) expect_output( @@ -147,7 +147,7 @@ test_that("s2_geography vectors can be created from wkt", { ) ) - expect_output(print(as_s2_geography("GEOMETRYCOLLECTION EMPTY")), "") + expect_output(print(as_s2_geography("GEOMETRYCOLLECTION EMPTY")), "GEOMETRYCOLLECTION EMPTY") }) test_that("empty points are empty when imported from WKB", { @@ -168,7 +168,7 @@ test_that("nested ring depths are correctly exported", { )"), max_coords = 100 ), - "\\(20 35, 10 30, 10 10, 30 5, 45 20, 20 35\\), \\(30 20, 20 15, 20 25, 30 20" + "\\(20 35, 10 30, 10 10, 30 5, 45 20, 20 35\\), \\(30 20, 20 15, 20 25" ) # polygon with a hole in a hole! @@ -178,13 +178,13 @@ test_that("nested ring depths are correctly exported", { ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), - (30 20, 20 15, 20 25, 30 20), - (27 21, 21 21, 21 16, 27 21) - ) + (30 20, 20 15, 20 25, 30 20) + ), + ((27 21, 21 21, 21 16, 27 21)) )"), max_coords = 100 ), - "30 20, 20 15, 20 25, 30 20\\), \\(27 21, 21 21, 21 16, 27 21" + "\\(\\(27 21, 21 21, 21 16, 27 21\\)\\)\\)" ) }) From 553acc56e4a062402b8647c27e57289dd8ad0cd7 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 20 Apr 2022 21:47:52 -0300 Subject: [PATCH 82/88] simplify Geography class and use it --- src/geography-shim.h | 41 -------- src/geography.h | 163 ++++++++++------------------- src/s2-accessors.cpp | 57 +++------- src/s2-bounds.cpp | 4 +- src/s2-cell-union.cpp | 15 +-- src/s2-cell.cpp | 7 +- src/s2-constructors-formatters.cpp | 8 +- src/s2-geography.cpp | 5 +- src/s2-geography/geography.hpp | 3 + src/s2-lnglat.cpp | 3 - src/s2-matrix.cpp | 69 +++--------- src/s2-predicates.cpp | 37 ++----- src/s2-transformers.cpp | 142 ++++++++----------------- 13 files changed, 155 insertions(+), 399 deletions(-) delete mode 100644 src/geography-shim.h diff --git a/src/geography-shim.h b/src/geography-shim.h deleted file mode 100644 index 14c4f61f..00000000 --- a/src/geography-shim.h +++ /dev/null @@ -1,41 +0,0 @@ - -#ifndef S2_GEOGRAPHY_SHIM_H_INCLUDED -#define S2_GEOGRAPHY_SHIM_H_INCLUDED - -#include "geography.h" -#include "s2-geography/s2-geography.hpp" - -static inline std::unique_ptr MakeOldGeography(const s2geography::S2Geography& geog) { - auto point = dynamic_cast(&geog); - if (point != nullptr) { - return absl::make_unique(point->Points()); - } - - auto polyline = dynamic_cast(&geog); - if (polyline != nullptr) { - std::vector> polylines; - for (auto& poly: polyline->Polylines()) { - polylines.push_back(std::unique_ptr(poly->Clone())); - } - - return absl::make_unique(std::move(polylines)); - } - - auto polygon = dynamic_cast(&geog); - if (polygon != nullptr) { - return absl::make_unique(std::unique_ptr(polygon->Polygon()->Clone())); - } - - auto collection = dynamic_cast(&geog); - if (collection != nullptr) { - std::vector> features; - for (auto& feat: collection->Features()) { - features.push_back(MakeOldGeography(*feat)); - } - return absl::make_unique(std::move(features)); - } - - throw s2geography::S2GeographyException("Unsupported S2Geography subclass"); -} - -#endif diff --git a/src/geography.h b/src/geography.h index 41078175..04a7c2d1 100644 --- a/src/geography.h +++ b/src/geography.h @@ -2,149 +2,94 @@ #ifndef GEOGRAPHY_H #define GEOGRAPHY_H -#include -#include +#define R_NO_REMAP +#include +#include #include "s2-geography/s2-geography.hpp" - class Geography { public: - Geography(): hasIndex(false) {} - - virtual std::unique_ptr NewGeography() = 0; + Geography(std::unique_ptr geog): + geog_(std::move(geog)), index_(nullptr) {} - // every type will build the index differently based on - // the underlying data, and this can (should?) be done - // lazily. Returns a vector of shape IDs so the caller - // can keep track of which shape came from which feature. - virtual std::vector BuildShapeIndex(MutableS2ShapeIndex* index) = 0; - - virtual ~Geography() {} + const s2geography::S2Geography& Geog() const { + return *geog_; + } - // other calculations use ShapeIndex - virtual S2ShapeIndex* ShapeIndex() { - if (!this->hasIndex) { - this->BuildShapeIndex(&this->shape_index_); - this->hasIndex = true; + const s2geography::S2GeographyShapeIndex& Index() { + if (!index_) { + this->index_ = absl::make_unique(*geog_); } - return &this->shape_index_; + return *index_; } -protected: - MutableS2ShapeIndex shape_index_; - bool hasIndex; -}; - -class PointGeography: public Geography { -public: - PointGeography(): points(0) {} - PointGeography(S2Point point): points(1) { - this->points[0] = point; + static SEXP MakeXPtr(std::unique_ptr geog) { + SEXP xptr = PROTECT(R_MakeExternalPtr(new Geography(std::move(geog)), R_NilValue, R_NilValue)); + R_RegisterCFinalizer(xptr, &finalize_xptr); + UNPROTECT(1); + return xptr; } - PointGeography(std::vector points): points(points) {} - std::unique_ptr NewGeography() { - return absl::make_unique(points); + static SEXP MakeXPtr(std::unique_ptr geog) { + std::unique_ptr geog_owning = std::move(geog); + SEXP xptr = PROTECT(R_MakeExternalPtr(geog_owning.release(), R_NilValue, R_NilValue)); + R_RegisterCFinalizer(xptr, &finalize_xptr); + UNPROTECT(1); + return xptr; } - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { - std::vector shapeIds(1); - std::vector pointsCopy(this->points); - - shapeIds[0] = index->Add(std::unique_ptr( - new S2PointVectorShape(std::move(pointsCopy))) - ); - return shapeIds; + static std::unique_ptr MakePoint() { + return absl::make_unique(absl::make_unique()); } -private: - std::vector points; -}; - -class PolylineGeography: public Geography { -public: - PolylineGeography(): polylines(0) {} - PolylineGeography(std::vector> polylines): - polylines(std::move(polylines)) {} + static std::unique_ptr MakePoint(S2Point point) { + return absl::make_unique(absl::make_unique(point)); + } - std::unique_ptr NewGeography() { - std::vector> polylines_cpy; + static std::unique_ptr MakePoint(std::vector points) { + return absl::make_unique(absl::make_unique(std::move(points))); + } - for (const auto& polyline : polylines) { - polylines_cpy.push_back(std::unique_ptr(polyline->Clone())); - } + static std::unique_ptr MakePolyline() { + return absl::make_unique(absl::make_unique()); + } - return absl::make_unique(std::move(polylines_cpy)); + static std::unique_ptr MakePolyline(std::unique_ptr polyline) { + return absl::make_unique(absl::make_unique(std::move(polyline))); } - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { - std::vector shapeIds(this->polylines.size()); - for (size_t i = 0; i < this->polylines.size(); i++) { - std::unique_ptr shape = absl::make_unique(); - shape->Init(this->polylines[i].get()); - shapeIds[i] = index->Add(std::move(shape)); - } - return shapeIds; + static std::unique_ptr MakePolyline(std::vector> polylines) { + return absl::make_unique(absl::make_unique(std::move(polylines))); } -private: - std::vector> polylines; -}; + static std::unique_ptr MakePolygon() { + return absl::make_unique(absl::make_unique()); + } -class PolygonGeography: public Geography { -public: - PolygonGeography() {} - PolygonGeography(std::unique_ptr polygon): - polygon(std::move(polygon)) {} + static std::unique_ptr MakePolygon(std::unique_ptr polygon) { + return absl::make_unique(absl::make_unique(std::move(polygon))); + } - std::unique_ptr NewGeography() { - return absl::make_unique(std::unique_ptr(polygon->Clone())); + static std::unique_ptr MakeCollection() { + return absl::make_unique(absl::make_unique()); } - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { - std::vector shapeIds(1); - std::unique_ptr shape = absl::make_unique(); - shape->Init(this->polygon.get()); - shapeIds[0] = index->Add(std::move(shape)); - return shapeIds; + static std::unique_ptr MakeCollection(std::vector> features) { + return absl::make_unique(absl::make_unique(std::move(features))); } private: - std::unique_ptr polygon; -}; - -class GeographyCollection: public Geography { -public: - GeographyCollection(): features(0) {} - GeographyCollection(std::vector> features): - features(std::move(features)) {} - - std::unique_ptr NewGeography() { - std::vector> features_cpy; - features_cpy.reserve(features.size()); - - for (const auto& feature : features) { - features_cpy.push_back(feature->NewGeography()); - } + std::unique_ptr geog_; + std::unique_ptr index_; - return absl::make_unique(std::move(features_cpy)); - } - - std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { - std::vector shapeIds; - for (size_t i = 0; i < this->features.size(); i++) { - std::vector newShapeIds = this->features[i]->BuildShapeIndex(index); - for (size_t j = 0; j < newShapeIds.size(); j++) { - shapeIds.push_back(newShapeIds[j]); - } + static void finalize_xptr(SEXP xptr) { + Geography* geog = reinterpret_cast(R_ExternalPtrAddr(xptr)); + if (geog != nullptr) { + delete geog; } - return shapeIds; } - -private: - std::vector> features; }; #endif diff --git a/src/s2-accessors.cpp b/src/s2-accessors.cpp index e847f664..c732f94d 100644 --- a/src/s2-accessors.cpp +++ b/src/s2-accessors.cpp @@ -9,8 +9,7 @@ using namespace Rcpp; LogicalVector cpp_s2_is_collection(List geog) { class Op: public UnaryGeographyOperator { int processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - return s2geography::s2_is_collection(*geog); + return s2geography::s2_is_collection(feature->Geog()); } }; @@ -22,8 +21,7 @@ LogicalVector cpp_s2_is_collection(List geog) { LogicalVector cpp_s2_is_valid(List geog) { class Op: public UnaryGeographyOperator { int processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - return !s2geography::s2_find_validation_error(*geog, &error); + return !s2geography::s2_find_validation_error(feature->Geog(), &error); } S2Error error; @@ -37,8 +35,7 @@ LogicalVector cpp_s2_is_valid(List geog) { CharacterVector cpp_s2_is_valid_reason(List geog) { class Op: public UnaryGeographyOperator { String processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - if (s2geography::s2_find_validation_error(*geog, &error)) { + if (s2geography::s2_find_validation_error(feature->Geog(), &error)) { return this->error.text(); } else { return NA_STRING; @@ -56,8 +53,7 @@ CharacterVector cpp_s2_is_valid_reason(List geog) { IntegerVector cpp_s2_dimension(List geog) { class Op: public UnaryGeographyOperator { int processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - return s2geography::s2_dimension(*geog); + return s2geography::s2_dimension(feature->Geog()); } }; @@ -69,8 +65,7 @@ IntegerVector cpp_s2_dimension(List geog) { IntegerVector cpp_s2_num_points(List geog) { class Op: public UnaryGeographyOperator { int processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - return s2geography::s2_num_points(*geog); + return s2geography::s2_num_points(feature->Geog()); } }; @@ -82,8 +77,7 @@ IntegerVector cpp_s2_num_points(List geog) { LogicalVector cpp_s2_is_empty(List geog) { class Op: public UnaryGeographyOperator { int processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - return s2geography::s2_is_empty(*geog); + return s2geography::s2_is_empty(feature->Geog()); } }; @@ -95,8 +89,7 @@ LogicalVector cpp_s2_is_empty(List geog) { NumericVector cpp_s2_area(List geog) { class Op: public UnaryGeographyOperator { double processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - return s2geography::s2_area(*geog); + return s2geography::s2_area(feature->Geog()); } }; @@ -108,8 +101,7 @@ NumericVector cpp_s2_area(List geog) { NumericVector cpp_s2_length(List geog) { class Op: public UnaryGeographyOperator { double processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - return s2geography::s2_length(*geog); + return s2geography::s2_length(feature->Geog()); } }; @@ -121,8 +113,7 @@ NumericVector cpp_s2_length(List geog) { NumericVector cpp_s2_perimeter(List geog) { class Op: public UnaryGeographyOperator { double processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - return s2geography::s2_perimeter(*geog); + return s2geography::s2_perimeter(feature->Geog()); } }; @@ -134,12 +125,11 @@ NumericVector cpp_s2_perimeter(List geog) { NumericVector cpp_s2_x(List geog) { class Op: public UnaryGeographyOperator { double processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - if (s2geography::s2_dimension(*geog) != 0) { + if (s2geography::s2_dimension(feature->Geog()) != 0) { Rcpp::stop("Can't compute X value of a non-point geography"); } - return s2geography::s2_x(*geog); + return s2geography::s2_x(feature->Geog()); } }; @@ -151,12 +141,11 @@ NumericVector cpp_s2_x(List geog) { NumericVector cpp_s2_y(List geog) { class Op: public UnaryGeographyOperator { double processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - if (s2geography::s2_dimension(*geog) != 0) { + if (s2geography::s2_dimension(feature->Geog()) != 0) { Rcpp::stop("Can't compute Y value of a non-point geography"); } - return s2geography::s2_y(*geog); + return s2geography::s2_y(feature->Geog()); } }; @@ -170,9 +159,7 @@ NumericVector cpp_s2_project_normalized(List geog1, List geog2) { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - return s2geography::s2_project_normalized(*geog1, *geog2); + return s2geography::s2_project_normalized(feature1->Geog(), feature2->Geog()); } }; @@ -187,13 +174,7 @@ NumericVector cpp_s2_distance(List geog1, List geog2) { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - double distance = s2geography::s2_distance(index1, index2); + double distance = s2geography::s2_distance(feature1->Index(), feature2->Index()); if (distance == R_PosInf) { return NA_REAL; @@ -214,13 +195,7 @@ NumericVector cpp_s2_max_distance(List geog1, List geog2) { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - double distance = s2geography::s2_max_distance(index1, index2); + double distance = s2geography::s2_max_distance(feature1->Index(), feature2->Index()); // returns -1 if one of the indexes is empty // NA is more consistent with the BigQuery diff --git a/src/s2-bounds.cpp b/src/s2-bounds.cpp index 1a41bca6..e8403ad5 100644 --- a/src/s2-bounds.cpp +++ b/src/s2-bounds.cpp @@ -20,7 +20,7 @@ DataFrame cpp_s2_bounds_cap(List geog) { lat[i] = lng[i] = angle[i] = NA_REAL; } else { Rcpp::XPtr feature(item); - S2Cap cap = feature->NewGeography()->Region()->GetCapBound(); + S2Cap cap = feature->Geog().Region()->GetCapBound(); S2LatLng center(cap.center()); lng[i] = center.lng().degrees(); lat[i] = center.lat().degrees(); @@ -47,7 +47,7 @@ DataFrame cpp_s2_bounds_rect(List geog) { lng_lo[i] = lat_lo[i] = lng_hi[i] = lat_hi[i] = NA_REAL; } else { Rcpp::XPtr feature(item); - S2LatLngRect rect = feature->NewGeography()->Region()->GetRectBound(); + S2LatLngRect rect = feature->Geog().Region()->GetRectBound(); lng_lo[i] = rect.lng_lo().degrees(); lat_lo[i] = rect.lat_lo().degrees(); lng_hi[i] = rect.lng_hi().degrees(); diff --git a/src/s2-cell-union.cpp b/src/s2-cell-union.cpp index 29a62425..a3b4adf5 100644 --- a/src/s2-cell-union.cpp +++ b/src/s2-cell-union.cpp @@ -305,7 +305,7 @@ List cpp_s2_geography_from_cell_union(List cellUnionVector) { SEXP processCell(S2CellUnion& cellUnion, R_xlen_t i) { std::unique_ptr polygon = absl::make_unique(); polygon->InitToCellUnionBorder(cellUnion); - return XPtr(new PolygonGeography(std::move(polygon))); + return Geography::MakeXPtr(Geography::MakePolygon(std::move(polygon))); } }; @@ -327,11 +327,8 @@ List cpp_s2_covering_cell_ids(List geog, int min_level, int max_level, distance(distance), coverer(coverer), interior(interior) {} SEXP processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - s2geography::S2GeographyShapeIndex index(*geog); - S2ShapeIndexBufferedRegion region; - region.Init(&index.ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); + region.Init(&feature->Index().ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); S2CellUnion cellUnion; if (interior) { @@ -366,9 +363,6 @@ List cpp_s2_covering_cell_ids_agg(List geog, int min_level, int max_level, S1ChordAngle bufferAngle = S1ChordAngle::Radians(buffer); S2RegionUnion regionUnion; - std::vector> keep_alive; - std::vector> index_keep_alive; - SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; @@ -380,12 +374,9 @@ List cpp_s2_covering_cell_ids_agg(List geog, int min_level, int max_level, if (item != R_NilValue) { Rcpp::XPtr feature(item); - keep_alive.push_back(feature->NewGeography()); - auto index = absl::make_unique(*keep_alive.back()); auto region = absl::make_unique(); - region->Init(&index->ShapeIndex(), bufferAngle); + region->Init(&feature->Index().ShapeIndex(), bufferAngle); regionUnion.Add(std::move(region)); - index_keep_alive.push_back(std::move(index)); } } diff --git a/src/s2-cell.cpp b/src/s2-cell.cpp index b4ee52d4..f2257485 100644 --- a/src/s2-cell.cpp +++ b/src/s2-cell.cpp @@ -354,7 +354,7 @@ List cpp_s2_cell_center(NumericVector cellIdVector) { class Op: public UnaryS2CellOperator { SEXP processCell(S2CellId cellId, R_xlen_t i) { if (cellId.is_valid()) { - return XPtr(new PointGeography(cellId.ToPoint())); + return Geography::MakeXPtr(Geography::MakePoint(cellId.ToPoint())); } else { return R_NilValue; } @@ -372,7 +372,8 @@ List cpp_s2_cell_polygon(NumericVector cellIdVector) { class Op: public UnaryS2CellOperator { SEXP processCell(S2CellId cellId, R_xlen_t i) { if (cellId.is_valid()) { - return XPtr(new PolygonGeography(absl::make_unique(S2Cell(cellId)))); + auto poly = absl::make_unique(S2Cell(cellId)); + return Geography::MakeXPtr(Geography::MakePolygon(std::move(poly))); } else { return R_NilValue; } @@ -390,7 +391,7 @@ List cpp_s2_cell_vertex(NumericVector cellIdVector, IntegerVector k) { class Op: public UnaryS2CellOperator { SEXP processCell(S2CellId cellId, R_xlen_t i) { if (cellId.is_valid() && (this->k[i] >= 0)) { - return XPtr(new PointGeography(S2Cell(cellId).GetVertex(this->k[i]))); + return Geography::MakeXPtr(Geography::MakePoint(S2Cell(cellId).GetVertex(this->k[i]))); } else { return R_NilValue; } diff --git a/src/s2-constructors-formatters.cpp b/src/s2-constructors-formatters.cpp index 5f9d0754..3e83acaa 100644 --- a/src/s2-constructors-formatters.cpp +++ b/src/s2-constructors-formatters.cpp @@ -5,7 +5,7 @@ #include "wk-v1.h" #include "s2-geography/s2-geography.hpp" -#include "geography-shim.h" +#include "geography.h" #define CPP_START \ @@ -134,8 +134,7 @@ int builder_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* ha builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START std::unique_ptr feat = data->builder->finish_feature(); - auto geog = MakeOldGeography(*feat); - builder_result_append(data, Rcpp::XPtr(geog.release())); + builder_result_append(data, Geography::MakeXPtr(std::move(feat))); return WK_CONTINUE; WK_METHOD_CPP_END_INT } @@ -589,8 +588,7 @@ SEXP handle_geography(SEXP data, wk_handler_t* handler) { HANDLE_CONTINUE_OR_BREAK(handler->null_feature(handler->handler_data)); } else { auto item_ptr = reinterpret_cast(R_ExternalPtrAddr(item)); - auto geog = item_ptr->NewGeography(); - const s2geography::S2Geography* geog_ptr = geog.get(); + const s2geography::S2Geography* geog_ptr = &item_ptr->Geog(); auto child_point = dynamic_cast(geog_ptr); if (child_point != nullptr) { diff --git a/src/s2-geography.cpp b/src/s2-geography.cpp index b3f27e7d..2f062152 100644 --- a/src/s2-geography.cpp +++ b/src/s2-geography.cpp @@ -12,8 +12,5 @@ using namespace Rcpp; List s2_geography_full(LogicalVector x) { // create single geography with full polygon std::unique_ptr l = absl::make_unique(S2Loop::kFull()); std::unique_ptr p = absl::make_unique(std::move(l)); - Geography *pg = new PolygonGeography(std::move(p)); - List ret(1); - ret(0) = Rcpp::XPtr(pg); - return ret; + return List::create(Geography::MakeXPtr(Geography::MakePolygon(std::move(p)))); } diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index 7fa56777..8cb07385 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -96,6 +96,9 @@ class S2GeographyOwningPoint: public S2Geography { class S2GeographyOwningPolyline: public S2Geography { public: S2GeographyOwningPolyline() {} + S2GeographyOwningPolyline(std::unique_ptr polyline) { + polylines_.push_back(std::move(polyline)); + } S2GeographyOwningPolyline(std::vector> polylines): polylines_(std::move(polylines)) {} diff --git a/src/s2-lnglat.cpp b/src/s2-lnglat.cpp index 764e5ccd..b5153a10 100644 --- a/src/s2-lnglat.cpp +++ b/src/s2-lnglat.cpp @@ -1,9 +1,6 @@ #include "s2/s2latlng.h" #include "s2/s2point.h" -#include "wk/rcpp-io.hpp" -#include "wk/wkb-reader.hpp" -#include "wk/wkb-writer.hpp" #include using namespace Rcpp; diff --git a/src/s2-matrix.cpp b/src/s2-matrix.cpp index 3a158976..1706b549 100644 --- a/src/s2-matrix.cpp +++ b/src/s2-matrix.cpp @@ -20,7 +20,6 @@ class IndexedBinaryGeographyOperator: public UnaryGeographyOperator geog2_index; std::unique_ptr iterator; - std::vector> keep_alive_; // max_edges_per_cell should be between 10 and 50, with lower numbers // leading to more memory usage (but potentially faster query times). Benchmarking @@ -45,8 +44,7 @@ class IndexedBinaryGeographyOperator: public UnaryGeographyOperator feature2(item2); - keep_alive_.push_back(std::move(feature2->NewGeography())); - geog2_index->Add(*keep_alive_.back(), j); + geog2_index->Add(feature2->Geog(), j); } } @@ -62,9 +60,8 @@ IntegerVector cpp_s2_closest_feature(List geog1, List geog2) { class Op: public IndexedBinaryGeographyOperator { public: int processFeature(Rcpp::XPtr feature, R_xlen_t i) { - s2geography::S2GeographyShapeIndex index(*feature->NewGeography()); S2ClosestEdgeQuery query(&geog2_index->ShapeIndex()); - S2ClosestEdgeQuery::ShapeIndexTarget target(&index.ShapeIndex()); + S2ClosestEdgeQuery::ShapeIndexTarget target(&feature->Index().ShapeIndex()); const auto& result = query.FindClosestEdge(&target); if (result.is_empty()) { return NA_INTEGER; @@ -86,9 +83,8 @@ IntegerVector cpp_s2_farthest_feature(List geog1, List geog2) { class Op: public IndexedBinaryGeographyOperator { public: int processFeature(Rcpp::XPtr feature, R_xlen_t i) { - s2geography::S2GeographyShapeIndex index(*feature->NewGeography()); S2FurthestEdgeQuery query(&geog2_index->ShapeIndex()); - S2FurthestEdgeQuery::ShapeIndexTarget target(&index.ShapeIndex()); + S2FurthestEdgeQuery::ShapeIndexTarget target(&feature->Index().ShapeIndex()); const auto& result = query.FindFurthestEdge(&target); if (result.is_empty()) { return NA_INTEGER; @@ -111,11 +107,10 @@ List cpp_s2_closest_edges(List geog1, List geog2, int n, double min_distance, class Op: public IndexedBinaryGeographyOperator { public: IntegerVector processFeature(Rcpp::XPtr feature, R_xlen_t i) { - s2geography::S2GeographyShapeIndex index(*feature->NewGeography()); S2ClosestEdgeQuery query(&geog2_index->ShapeIndex()); query.mutable_options()->set_max_results(n); query.mutable_options()->set_max_distance(S1ChordAngle::Radians(max_distance)); - S2ClosestEdgeQuery::ShapeIndexTarget target(&index.ShapeIndex()); + S2ClosestEdgeQuery::ShapeIndexTarget target(&feature->Index().ShapeIndex()); const auto& result = query.FindClosestEdges(&target); // this code searches edges, which may come from the same feature @@ -165,13 +160,10 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperator feature, R_xlen_t i) { - auto geog1 = feature->NewGeography(); - coverer.GetCovering(*geog1->Region(), &cell_ids); + coverer.GetCovering(*feature->Geog().Region(), &cell_ids); indices_unsorted.clear(); iterator->Query(cell_ids, &indices_unsorted); - s2geography::S2GeographyShapeIndex index1(*geog1); - // loop through features from geog2 that might intersect feature // and build a list of indices that actually intersect (based on // this->actuallyIntersects(), which might perform alternative @@ -180,10 +172,8 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperatorgeog2[j]; XPtr feature2(item); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index2(*geog2); - if (this->actuallyIntersects(index1, index2, i, j)) { + if (this->actuallyIntersects(feature->Index(), feature2->Index(), i, j)) { // convert to R index here + 1 indices.push_back(j + 1); } @@ -398,8 +388,8 @@ List cpp_s2_dwithin_matrix(List geog1, List geog2, double distance) { Op(double distance): distance(distance) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - S2ClosestEdgeQuery query(feature2->ShapeIndex()); - S2ClosestEdgeQuery::ShapeIndexTarget target(feature1->ShapeIndex()); + S2ClosestEdgeQuery query(&feature2->Index().ShapeIndex()); + S2ClosestEdgeQuery::ShapeIndexTarget target(&feature1->Index().ShapeIndex()); return query.IsDistanceLessOrEqual(&target, S1ChordAngle::Radians(this->distance)); }; }; @@ -457,8 +447,8 @@ NumericMatrix cpp_s2_distance_matrix(List geog1, List geog2) { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - S2ClosestEdgeQuery query(feature1->ShapeIndex()); - S2ClosestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex()); + S2ClosestEdgeQuery query(&feature1->Index().ShapeIndex()); + S2ClosestEdgeQuery::ShapeIndexTarget target(&feature2->Index().ShapeIndex()); const auto& result = query.FindClosestEdge(&target); S1ChordAngle angle = result.distance(); @@ -482,8 +472,8 @@ NumericMatrix cpp_s2_max_distance_matrix(List geog1, List geog2) { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - S2FurthestEdgeQuery query(feature1->ShapeIndex()); - S2FurthestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex()); + S2FurthestEdgeQuery query(&feature1->Index().ShapeIndex()); + S2FurthestEdgeQuery::ShapeIndexTarget target(&feature2->Index().ShapeIndex()); const auto& result = query.FindFurthestEdge(&target); S1ChordAngle angle = result.distance(); @@ -514,12 +504,7 @@ List cpp_s2_contains_matrix_brute_force(List geog1, List geog2, List s2options) Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - return s2geography::s2_contains(index1, index2, options); + return s2geography::s2_contains(feature1->Index(), feature2->Index(), options); }; }; @@ -535,12 +520,7 @@ List cpp_s2_within_matrix_brute_force(List geog1, List geog2, List s2options) { bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { // note reversed index2, index1 - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - return s2geography::s2_contains(index2, index1, options); + return s2geography::s2_contains(feature2->Index(), feature1->Index(), options); }; }; @@ -555,12 +535,7 @@ List cpp_s2_intersects_matrix_brute_force(List geog1, List geog2, List s2options Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - return s2geography::s2_intersects(index1, index2, options); + return s2geography::s2_intersects(feature1->Index(), feature2->Index(), options); } }; @@ -575,12 +550,7 @@ List cpp_s2_disjoint_matrix_brute_force(List geog1, List geog2, List s2options) Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - return !s2geography::s2_intersects(index1, index2, options); + return !s2geography::s2_intersects(feature1->Index(), feature2->Index(), options); } }; @@ -595,12 +565,7 @@ List cpp_s2_equals_matrix_brute_force(List geog1, List geog2, List s2options) { Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - return s2geography::s2_equals(index1, index2, options); + return s2geography::s2_equals(feature1->Index(), feature2->Index(), options); } }; diff --git a/src/s2-predicates.cpp b/src/s2-predicates.cpp index 2b936a0b..3134b5bb 100644 --- a/src/s2-predicates.cpp +++ b/src/s2-predicates.cpp @@ -28,12 +28,7 @@ LogicalVector cpp_s2_intersects(List geog1, List geog2, List s2options) { public: Op(List s2options): BinaryPredicateOperator(s2options) {} int processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - return s2geography::s2_intersects(index1, index2, options); + return s2geography::s2_intersects(feature1->Index(), feature2->Index(), options); }; }; @@ -48,12 +43,7 @@ LogicalVector cpp_s2_equals(List geog1, List geog2, List s2options) { public: Op(List s2options): BinaryPredicateOperator(s2options) {} int processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - return s2geography::s2_equals(index1, index2, options); + return s2geography::s2_equals(feature1->Index(), feature2->Index(), options); } }; @@ -67,11 +57,7 @@ LogicalVector cpp_s2_contains(List geog1, List geog2, List s2options) { public: Op(List s2options): BinaryPredicateOperator(s2options) {} int processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - return s2geography::s2_contains(index1, index2, options); + return s2geography::s2_contains(feature1->Index(), feature2->Index(), options); } }; @@ -94,13 +80,8 @@ LogicalVector cpp_s2_touches(List geog1, List geog2, List s2options) { } int processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - return s2geography::s2_intersects(index1, index2, this->closedOptions) && - !s2geography::s2_intersects(index1, index2, this->openOptions); + return s2geography::s2_intersects(feature1->Index(), feature2->Index(), this->closedOptions) && + !s2geography::s2_intersects(feature1->Index(), feature2->Index(), this->openOptions); } private: @@ -124,8 +105,8 @@ LogicalVector cpp_s2_dwithin(List geog1, List geog2, NumericVector distance) { Op(NumericVector distance): distance(distance) {} int processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - S2ClosestEdgeQuery query(feature1->ShapeIndex()); - S2ClosestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex()); + S2ClosestEdgeQuery query(&feature1->Index().ShapeIndex()); + S2ClosestEdgeQuery::ShapeIndexTarget target(&feature2->Index().ShapeIndex()); return query.IsDistanceLessOrEqual(&target, S1ChordAngle::Radians(this->distance[i])); } }; @@ -185,9 +166,7 @@ LogicalVector cpp_s2_intersects_box(List geog, S2LatLngRect rect(S2LatLng::FromDegrees(ymin, xmin), S2LatLng::FromDegrees(ymax, xmax)); - auto geog = feature->NewGeography(); - s2geography::S2GeographyShapeIndex index(*geog); - return s2geography::s2_intersects_box(index, rect, options, deltaDegrees); + return s2geography::s2_intersects_box(feature->Index(), rect, options, deltaDegrees); } }; diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index 70da17e4..cedea796 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -4,7 +4,6 @@ #include "s2-options.h" #include "geography-operator.h" -#include "geography-shim.h" #include using namespace Rcpp; @@ -19,18 +18,12 @@ class BooleanOperationOp: public BinaryGeographyOperator { } SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - std::unique_ptr geog_out = s2geography::s2_boolean_operation( - index1, index2, + feature1->Index(), feature2->Index(), this->opType, this->geography_options); - std::unique_ptr geography = MakeOldGeography(*geog_out); - return Rcpp::XPtr(geography.release()); + return Geography::MakeXPtr(std::move(geog_out)); } private: @@ -66,7 +59,6 @@ List cpp_s2_sym_difference(List geog1, List geog2, List s2options) { List cpp_s2_coverage_union_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); s2geography::S2CoverageUnionAggregator agg(options.geographyOptions()); - std::vector> geographies; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { @@ -77,23 +69,18 @@ List cpp_s2_coverage_union_agg(List geog, List s2options, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); - auto geog = feature->NewGeography(); - agg.Add(*geog); - geographies.push_back(std::move(geog)); + agg.Add(feature->Geog()); } } std::unique_ptr geog_out = agg.Finalize(); - - auto geography = MakeOldGeography(*geog_out); - return List::create(Rcpp::XPtr(geography.release())); + return List::create(Geography::MakeXPtr(std::move(geog_out))); } // [[Rcpp::export]] List cpp_s2_union_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); s2geography::S2UnionAggregator agg(options.geographyOptions()); - std::vector> geographies; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { @@ -104,16 +91,12 @@ List cpp_s2_union_agg(List geog, List s2options, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); - auto geog = feature->NewGeography(); - agg.Add(*geog); - geographies.push_back(std::move(geog)); + agg.Add(feature->Geog()); } } std::unique_ptr geog_out = agg.Finalize(); - - auto geography = MakeOldGeography(*geog_out); - return List::create(Rcpp::XPtr(geography.release())); + return List::create(Geography::MakeXPtr(std::move(geog_out))); } // [[Rcpp::export]] @@ -129,7 +112,7 @@ List cpp_s2_centroid_agg(List geog, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); - agg.Add(*feature->NewGeography()); + agg.Add(feature->Geog()); } } @@ -137,9 +120,9 @@ List cpp_s2_centroid_agg(List geog, bool naRm) { List output(1); if (centroid.Norm2() == 0) { - output[0] = Rcpp::XPtr(new PointGeography()); + output[0] = Geography::MakeXPtr(Geography::MakePoint()); } else { - output[0] = Rcpp::XPtr(new PointGeography(centroid)); + output[0] = Geography::MakeXPtr(Geography::MakePoint(centroid)); } return output; @@ -161,16 +144,12 @@ List cpp_s2_rebuild_agg(List geog, List s2options, bool naRm) { if (item != R_NilValue) { Rcpp::XPtr feature(item); - auto geog = feature->NewGeography(); - agg.Add(*geog); - geographies.push_back(std::move(geog)); + agg.Add(feature->Geog()); } } auto geog_out = agg.Finalize(); - - auto geography = MakeOldGeography(*geog_out); - return List::create(Rcpp::XPtr(geography.release())); + return List::create(Geography::MakeXPtr(std::move(geog_out))); } // [[Rcpp::export]] @@ -178,16 +157,11 @@ List cpp_s2_closest_point(List geog1, List geog2) { class Op: public BinaryGeographyOperator { SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - - S2Point pt = s2geography::s2_closest_point(index1, index2); + S2Point pt = s2geography::s2_closest_point(feature1->Index(), feature2->Index()); if (pt.Norm2() == 0) { - return XPtr(new PointGeography()); + return Geography::MakeXPtr(Geography::MakePoint()); } else { - return XPtr(new PointGeography(pt)); + return Geography::MakeXPtr(Geography::MakePoint(pt)); } } }; @@ -201,18 +175,13 @@ List cpp_s2_minimum_clearance_line_between(List geog1, List geog2) { class Op: public BinaryGeographyOperator { SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { - auto geog1 = feature1->NewGeography(); - auto geog2 = feature2->NewGeography(); - s2geography::S2GeographyShapeIndex index1(*geog1); - s2geography::S2GeographyShapeIndex index2(*geog2); - std::pair pts = s2geography::s2_minimum_clearance_line_between( - index1, - index2 + feature1->Index(), + feature2->Index() ); if (pts.first.Norm2() == 0) { - return XPtr(new PolylineGeography()); + return Geography::MakeXPtr(Geography::MakePoint()); } std::vector vertices(2); @@ -220,16 +189,14 @@ List cpp_s2_minimum_clearance_line_between(List geog1, List geog2) { vertices[1] = pts.second; if (pts.first == pts.second) { - return XPtr(new PointGeography(vertices)); + return Geography::MakeXPtr(Geography::MakePoint(std::move(vertices))); } else { std::vector vertices(2); vertices[0] = pts.first; vertices[1] = pts.second; std::unique_ptr polyline = absl::make_unique(); polyline->Init(vertices); - std::vector> polylines(1); - polylines[0] = std::move(polyline); - return XPtr(new PolylineGeography(std::move(polylines))); + return Geography::MakeXPtr(Geography::MakePolyline(std::move(polyline))); } } }; @@ -242,12 +209,11 @@ List cpp_s2_minimum_clearance_line_between(List geog1, List geog2) { List cpp_s2_centroid(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - S2Point centroid = s2geography::s2_centroid(*geog); + S2Point centroid = s2geography::s2_centroid(feature->Geog()); if (centroid.Norm2() == 0) { - return XPtr(new PointGeography()); + return Geography::MakeXPtr(Geography::MakePoint()); } else { - return XPtr(new PointGeography(centroid.Normalize())); + return Geography::MakeXPtr(Geography::MakePoint(centroid.Normalize())); } } }; @@ -263,12 +229,11 @@ List cpp_s2_point_on_surface(List geog) { S2RegionCoverer coverer; SEXP processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - S2Point result = s2geography::s2_point_on_surface(*geog, coverer); + S2Point result = s2geography::s2_point_on_surface(feature->Geog(), coverer); if (result.Norm2() == 0) { - return XPtr(new PointGeography()); + return Geography::MakeXPtr(Geography::MakePoint()); } else { - return XPtr(new PointGeography(result)); + return Geography::MakeXPtr(Geography::MakePoint(result)); } } }; @@ -281,10 +246,8 @@ List cpp_s2_point_on_surface(List geog) { List cpp_s2_boundary(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - std::unique_ptr result = s2geography::s2_boundary(*geog); - std::unique_ptr ptr = MakeOldGeography(*result); - return XPtr(ptr.release()); + std::unique_ptr result = s2geography::s2_boundary(feature->Geog()); + return Geography::MakeXPtr(std::move(result)); } }; @@ -302,15 +265,12 @@ List cpp_s2_rebuild(List geog, List s2options) { } SEXP processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - s2geography::S2GeographyShapeIndex index(*geog); std::unique_ptr ptr = s2geography::s2_rebuild( - index, + feature->Geog(), this->options ); - auto geography = MakeOldGeography(*ptr); - return XPtr(geography.release()); + return Geography::MakeXPtr(std::move(ptr)); } private: @@ -331,12 +291,9 @@ List cpp_s2_unary_union(List geog, List s2options) { } SEXP processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - s2geography::S2GeographyShapeIndex index(*geog); std::unique_ptr geog_out = - s2geography::s2_unary_union(index, this->geographyOptions); - auto geography = MakeOldGeography(*geog_out); - return XPtr(geography.release()); + s2geography::s2_unary_union(feature->Index(), this->geographyOptions); + return Geography::MakeXPtr(std::move(geog_out)); } private: @@ -360,24 +317,22 @@ List cpp_s2_interpolate_normalized(List geog, NumericVector distanceNormalized) return R_NilValue; } - auto geog = feature->NewGeography(); - - if (s2geography::s2_is_empty(*geog)) { - return XPtr(new PointGeography()); + if (s2geography::s2_is_empty(feature->Geog())) { + return Geography::MakeXPtr(Geography::MakePoint()); } - if (s2geography::s2_is_collection(*geog)) { + if (s2geography::s2_is_collection(feature->Geog())) { throw GeographyOperatorException("`x` must be a simple geography"); - } else if (geog->dimension() != 1) { + } else if (feature->Geog().dimension() != 1) { throw GeographyOperatorException("`x` must be a polyline"); } - S2Point point = s2geography::s2_interpolate_normalized(*geog, this->distanceNormalized[i]); + S2Point point = s2geography::s2_interpolate_normalized(feature->Geog(), this->distanceNormalized[i]); if (point.Norm2() == 0) { - return XPtr(new PointGeography()); + return Geography::MakeXPtr(Geography::MakePoint()); } else { - return XPtr(new PointGeography(point)); + return Geography::MakeXPtr(Geography::MakePoint(point)); } } }; @@ -401,11 +356,8 @@ List cpp_s2_buffer_cells(List geog, NumericVector distance, int maxCells, int mi } SEXP processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); - s2geography::S2GeographyShapeIndex index(*geog); - S2ShapeIndexBufferedRegion region; - region.Init(&index.ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); + region.Init(&feature->Index().ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); S2CellUnion cellUnion; cellUnion = coverer.GetCovering(region); @@ -413,7 +365,7 @@ List cpp_s2_buffer_cells(List geog, NumericVector distance, int maxCells, int mi std::unique_ptr polygon = absl::make_unique(); polygon->InitToCellUnionBorder(cellUnion); - return XPtr(new PolygonGeography(std::move(polygon))); + return Geography::MakeXPtr(Geography::MakePolygon(std::move(polygon))); } }; @@ -425,11 +377,9 @@ List cpp_s2_buffer_cells(List geog, NumericVector distance, int maxCells, int mi List cpp_s2_convex_hull(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { - auto geog = feature->NewGeography(); std::unique_ptr geog_out = - s2geography::s2_convex_hull(*geog); - auto geography = MakeOldGeography(*geog_out); - return XPtr(geography.release()); + s2geography::s2_convex_hull(feature->Geog()); + return Geography::MakeXPtr(std::move(geog_out)); } }; @@ -441,7 +391,6 @@ List cpp_s2_convex_hull(List geog) { // [[Rcpp::export]] List cpp_s2_convex_hull_agg(List geog, bool naRm) { s2geography::S2ConvexHullAggregator agg; - std::vector> keep_alive_; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { @@ -452,12 +401,9 @@ List cpp_s2_convex_hull_agg(List geog, bool naRm) { if (item != R_NilValue) { XPtr feature(item); - keep_alive_.push_back(feature->NewGeography()); - agg.Add(*keep_alive_.back()); + agg.Add(feature->Geog()); } } - auto geography = MakeOldGeography(*agg.Finalize()); - XPtr outG(geography.release()); - return List::create(outG); + return List::create(Geography::MakeXPtr(agg.Finalize())); } From b93cc483e9b0f719e12c6bfc424bd11665af9828 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Wed, 20 Apr 2022 21:53:01 -0300 Subject: [PATCH 83/88] fix point constructor --- src/s2-geography/geography.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index 8cb07385..e0d42571 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -73,7 +73,7 @@ class S2Geography { class S2GeographyOwningPoint: public S2Geography { public: S2GeographyOwningPoint() {} - S2GeographyOwningPoint(S2Point point): points_(1) { points_.push_back(point); } + S2GeographyOwningPoint(S2Point point) { points_.push_back(point); } S2GeographyOwningPoint(std::vector points): points_(std::move(points)) {} int dimension() const { return 0; } From e7cb711eddd3cec3a4516c642a34f4aeafbfa989 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 21 Apr 2022 14:33:12 -0300 Subject: [PATCH 84/88] S2GeographyOwningPoint -> PointGeography --- src/geography.h | 6 +++--- src/s2-constructors-formatters.cpp | 6 +++--- src/s2-geography/accessors-geog.cpp | 4 ++-- src/s2-geography/build.cpp | 10 +++++----- src/s2-geography/build.hpp | 2 +- src/s2-geography/constructor.hpp | 2 +- src/s2-geography/geography.cpp | 6 +++--- src/s2-geography/geography.hpp | 8 ++++---- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/geography.h b/src/geography.h index 04a7c2d1..e031b813 100644 --- a/src/geography.h +++ b/src/geography.h @@ -41,15 +41,15 @@ class Geography { } static std::unique_ptr MakePoint() { - return absl::make_unique(absl::make_unique()); + return absl::make_unique(absl::make_unique()); } static std::unique_ptr MakePoint(S2Point point) { - return absl::make_unique(absl::make_unique(point)); + return absl::make_unique(absl::make_unique(point)); } static std::unique_ptr MakePoint(std::vector points) { - return absl::make_unique(absl::make_unique(std::move(points))); + return absl::make_unique(absl::make_unique(std::move(points))); } static std::unique_ptr MakePolyline() { diff --git a/src/s2-constructors-formatters.cpp b/src/s2-constructors-formatters.cpp index 3e83acaa..8bf98942 100644 --- a/src/s2-constructors-formatters.cpp +++ b/src/s2-constructors-formatters.cpp @@ -300,7 +300,7 @@ extern "C" SEXP c_s2_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp) { if (result == WK_ABORT_FEATURE) continue; else if (result == WK_ABORT) break -int handle_points(const s2geography::S2GeographyOwningPoint& geog, wk_handler_t* handler, +int handle_points(const s2geography::PointGeography& geog, wk_handler_t* handler, uint32_t part_id = WK_PART_ID_NONE) { int result; @@ -536,7 +536,7 @@ int handle_collection(const s2geography::S2GeographyCollection& geog, wk_handler for (size_t i = 0; i < geog.Features().size(); i++) { const s2geography::S2Geography* child_ptr = geog.Features()[i].get(); - auto child_point = dynamic_cast(child_ptr); + auto child_point = dynamic_cast(child_ptr); if (child_point != nullptr) { HANDLE_OR_RETURN(handle_points(*child_point, handler, i)); continue; @@ -590,7 +590,7 @@ SEXP handle_geography(SEXP data, wk_handler_t* handler) { auto item_ptr = reinterpret_cast(R_ExternalPtrAddr(item)); const s2geography::S2Geography* geog_ptr = &item_ptr->Geog(); - auto child_point = dynamic_cast(geog_ptr); + auto child_point = dynamic_cast(geog_ptr); if (child_point != nullptr) { HANDLE_CONTINUE_OR_BREAK(handle_points(*child_point, handler)); } else { diff --git a/src/s2-geography/accessors-geog.cpp b/src/s2-geography/accessors-geog.cpp index fe50cf90..4cdd5016 100644 --- a/src/s2-geography/accessors-geog.cpp +++ b/src/s2-geography/accessors-geog.cpp @@ -79,7 +79,7 @@ std::unique_ptr s2_boundary(const S2Geography& geog) { } } - return absl::make_unique(std::move(endpoints)); + return absl::make_unique(std::move(endpoints)); } if (dimension == 2) { @@ -142,7 +142,7 @@ S2Point S2CentroidAggregator::Finalize() { void S2ConvexHullAggregator::Add(const S2Geography& geog) { if (geog.dimension() == 0) { - auto point_ptr = dynamic_cast(&geog); + auto point_ptr = dynamic_cast(&geog); if (point_ptr != nullptr) { for (const auto& point : point_ptr->Points()) { query_.AddPoint(point); diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index 732ccd7e..a39bd454 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -54,7 +54,7 @@ std::unique_ptr s2_geography_from_layers(std::vector point std::vector> features; if (has_points) { - features.push_back(absl::make_unique(std::move(points))); + features.push_back(absl::make_unique(std::move(points))); } if (has_polylines) { @@ -74,7 +74,7 @@ std::unique_ptr s2_geography_from_layers(std::vector point } else if (has_polylines || (included_dimensions == 1 && include_polylines)) { return absl::make_unique(std::move(polylines)); } else if (has_points || (included_dimensions == 1 && include_points)) { - return absl::make_unique(std::move(points)); + return absl::make_unique(std::move(points)); } else { return absl::make_unique(); } @@ -285,7 +285,7 @@ std::unique_ptr s2_rebuild(const S2Geography& geog, options.polygon_layer_action); } -std::unique_ptr s2_build_point(const S2Geography& geog) { +std::unique_ptr s2_build_point(const S2Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, S2GeographyOptions(), @@ -293,8 +293,8 @@ std::unique_ptr s2_build_point(const S2Geography& geog) S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR); - return std::unique_ptr( - dynamic_cast(geog_out.release())); + return std::unique_ptr( + dynamic_cast(geog_out.release())); } diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index 6d1a5aa8..b0a4a202 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -45,7 +45,7 @@ std::unique_ptr s2_unary_union(const S2GeographyShapeIndex& geog, std::unique_ptr s2_rebuild(const S2Geography& geog, const S2GeographyOptions& options); -std::unique_ptr s2_build_point(const S2Geography& geog); +std::unique_ptr s2_build_point(const S2Geography& geog); std::unique_ptr s2_build_polyline(const S2Geography& geog); diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index ed00db78..0f5bf9b2 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -94,7 +94,7 @@ class PointConstructor: public Constructor { } std::unique_ptr finish() { - auto result = absl::make_unique(std::move(points_)); + auto result = absl::make_unique(std::move(points_)); points_.clear(); return std::unique_ptr(result.release()); } diff --git a/src/s2-geography/geography.cpp b/src/s2-geography/geography.cpp index 34c3d494..0e31c0a1 100644 --- a/src/s2-geography/geography.cpp +++ b/src/s2-geography/geography.cpp @@ -72,11 +72,11 @@ void S2Geography::GetCellUnionBound(std::vector* cell_ids) const { MakeS2ShapeIndexRegion(&index).GetCellUnionBound(cell_ids); } -std::unique_ptr S2GeographyOwningPoint::Shape(int id) const { +std::unique_ptr PointGeography::Shape(int id) const { return absl::make_unique(points_); } -std::unique_ptr S2GeographyOwningPoint::Region() const { +std::unique_ptr PointGeography::Region() const { auto region = absl::make_unique(); for (const S2Point& point: points_) { region->Add(absl::make_unique(point)); @@ -87,7 +87,7 @@ std::unique_ptr S2GeographyOwningPoint::Region() const { return std::unique_ptr(region.release()); } -void S2GeographyOwningPoint::GetCellUnionBound(std::vector* cell_ids) const { +void PointGeography::GetCellUnionBound(std::vector* cell_ids) const { if (points_.size() < 10) { for (const S2Point& point: points_) { cell_ids->push_back(S2CellId(point)); diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index e0d42571..8a61ef74 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -70,11 +70,11 @@ class S2Geography { // An S2Geography representing zero or more points using a std::vector // as the underlying representation. -class S2GeographyOwningPoint: public S2Geography { +class PointGeography: public S2Geography { public: - S2GeographyOwningPoint() {} - S2GeographyOwningPoint(S2Point point) { points_.push_back(point); } - S2GeographyOwningPoint(std::vector points): points_(std::move(points)) {} + PointGeography() {} + PointGeography(S2Point point) { points_.push_back(point); } + PointGeography(std::vector points): points_(std::move(points)) {} int dimension() const { return 0; } int num_shapes() const { return 1; } From 81760233436262a86d919bba33046f87378db753 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 21 Apr 2022 14:38:14 -0300 Subject: [PATCH 85/88] more renaming to saner names --- src/geography.h | 16 ++++---- src/s2-constructors-formatters.cpp | 12 +++--- src/s2-geography/accessors-geog.cpp | 18 ++++----- src/s2-geography/accessors-geog.hpp | 4 +- src/s2-geography/accessors.cpp | 28 +++++++------- src/s2-geography/build.cpp | 50 ++++++++++++------------- src/s2-geography/build.hpp | 18 ++++----- src/s2-geography/constructor.hpp | 22 +++++------ src/s2-geography/coverings.cpp | 4 +- src/s2-geography/coverings.hpp | 2 +- src/s2-geography/distance.cpp | 10 ++--- src/s2-geography/distance.hpp | 8 ++-- src/s2-geography/geography.cpp | 22 +++++------ src/s2-geography/geography.hpp | 26 ++++++------- src/s2-geography/index.hpp | 2 +- src/s2-geography/linear-referencing.cpp | 12 +++--- src/s2-geography/predicates.cpp | 14 +++---- src/s2-geography/predicates.hpp | 18 ++++----- src/s2-matrix.cpp | 28 +++++++------- 19 files changed, 157 insertions(+), 157 deletions(-) diff --git a/src/geography.h b/src/geography.h index e031b813..a20f7394 100644 --- a/src/geography.h +++ b/src/geography.h @@ -17,9 +17,9 @@ class Geography { return *geog_; } - const s2geography::S2GeographyShapeIndex& Index() { + const s2geography::ShapeIndexGeography& Index() { if (!index_) { - this->index_ = absl::make_unique(*geog_); + this->index_ = absl::make_unique(*geog_); } return *index_; @@ -53,23 +53,23 @@ class Geography { } static std::unique_ptr MakePolyline() { - return absl::make_unique(absl::make_unique()); + return absl::make_unique(absl::make_unique()); } static std::unique_ptr MakePolyline(std::unique_ptr polyline) { - return absl::make_unique(absl::make_unique(std::move(polyline))); + return absl::make_unique(absl::make_unique(std::move(polyline))); } static std::unique_ptr MakePolyline(std::vector> polylines) { - return absl::make_unique(absl::make_unique(std::move(polylines))); + return absl::make_unique(absl::make_unique(std::move(polylines))); } static std::unique_ptr MakePolygon() { - return absl::make_unique(absl::make_unique()); + return absl::make_unique(absl::make_unique()); } static std::unique_ptr MakePolygon(std::unique_ptr polygon) { - return absl::make_unique(absl::make_unique(std::move(polygon))); + return absl::make_unique(absl::make_unique(std::move(polygon))); } static std::unique_ptr MakeCollection() { @@ -82,7 +82,7 @@ class Geography { private: std::unique_ptr geog_; - std::unique_ptr index_; + std::unique_ptr index_; static void finalize_xptr(SEXP xptr) { Geography* geog = reinterpret_cast(R_ExternalPtrAddr(xptr)); diff --git a/src/s2-constructors-formatters.cpp b/src/s2-constructors-formatters.cpp index 8bf98942..9aea1e1d 100644 --- a/src/s2-constructors-formatters.cpp +++ b/src/s2-constructors-formatters.cpp @@ -342,7 +342,7 @@ int handle_points(const s2geography::PointGeography& geog, wk_handler_t* handler return WK_CONTINUE; } -int handle_polylines(const s2geography::S2GeographyOwningPolyline& geog, wk_handler_t* handler, +int handle_polylines(const s2geography::PolylineGeography& geog, wk_handler_t* handler, uint32_t part_id = WK_PART_ID_NONE) { int result; @@ -461,7 +461,7 @@ int handle_shell(const S2Polygon& poly, const wk_meta_t* meta, int loop_start, w return WK_CONTINUE; } -int handle_polygon(const s2geography::S2GeographyOwningPolygon& geog, wk_handler_t* handler, +int handle_polygon(const s2geography::PolygonGeography& geog, wk_handler_t* handler, uint32_t part_id = WK_PART_ID_NONE) { const S2Polygon& poly = *geog.Polygon(); @@ -542,13 +542,13 @@ int handle_collection(const s2geography::S2GeographyCollection& geog, wk_handler continue; } - auto child_polyline = dynamic_cast(child_ptr); + auto child_polyline = dynamic_cast(child_ptr); if (child_polyline != nullptr) { HANDLE_OR_RETURN(handle_polylines(*child_polyline, handler, i)); continue; } - auto child_polygon = dynamic_cast(child_ptr); + auto child_polygon = dynamic_cast(child_ptr); if (child_polygon != nullptr) { HANDLE_OR_RETURN(handle_polygon(*child_polygon, handler, i)); continue; @@ -594,11 +594,11 @@ SEXP handle_geography(SEXP data, wk_handler_t* handler) { if (child_point != nullptr) { HANDLE_CONTINUE_OR_BREAK(handle_points(*child_point, handler)); } else { - auto child_polyline = dynamic_cast(geog_ptr); + auto child_polyline = dynamic_cast(geog_ptr); if (child_polyline != nullptr) { HANDLE_CONTINUE_OR_BREAK(handle_polylines(*child_polyline, handler)); } else { - auto child_polygon = dynamic_cast(geog_ptr); + auto child_polygon = dynamic_cast(geog_ptr); if (child_polygon != nullptr) { HANDLE_CONTINUE_OR_BREAK(handle_polygon(*child_polygon, handler)); } else { diff --git a/src/s2-geography/accessors-geog.cpp b/src/s2-geography/accessors-geog.cpp index 4cdd5016..e71736ed 100644 --- a/src/s2-geography/accessors-geog.cpp +++ b/src/s2-geography/accessors-geog.cpp @@ -35,11 +35,11 @@ S2Point s2_centroid(const S2Geography& geog) { } if (geog.dimension() == 2) { - auto polygon_ptr = dynamic_cast(&geog); + auto polygon_ptr = dynamic_cast(&geog); if (polygon_ptr != nullptr) { centroid = polygon_ptr->Polygon()->GetCentroid(); } else { - std::unique_ptr built = s2_build_polygon(geog); + std::unique_ptr built = s2_build_polygon(geog); centroid = built->Polygon()->GetCentroid(); } @@ -48,7 +48,7 @@ S2Point s2_centroid(const S2Geography& geog) { auto collection_ptr = dynamic_cast(&geog); if (collection_ptr == nullptr) { - throw S2GeographyException("Can't compute s2_centroid() on custom collection geography"); + throw Exception("Can't compute s2_centroid() on custom collection geography"); } for (auto& feat: collection_ptr->Features()) { @@ -89,7 +89,7 @@ std::unique_ptr s2_boundary(const S2Geography& geog) { for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); if (shape->dimension() != 2) { - throw S2GeographyException("Can't extract boundary from heterogeneous collection"); + throw Exception("Can't extract boundary from heterogeneous collection"); } for (int j = 0; j < shape->num_chains(); j++) { @@ -108,7 +108,7 @@ std::unique_ptr s2_boundary(const S2Geography& geog) { } } - return absl::make_unique(std::move(polylines)); + return absl::make_unique(std::move(polylines)); } return absl::make_unique(); @@ -156,7 +156,7 @@ void S2ConvexHullAggregator::Add(const S2Geography& geog) { } if (geog.dimension() == 1) { - auto poly_ptr = dynamic_cast(&geog); + auto poly_ptr = dynamic_cast(&geog); if (poly_ptr != nullptr) { for (const auto& polyline : poly_ptr->Polylines()) { query_.AddPolyline(*polyline); @@ -170,7 +170,7 @@ void S2ConvexHullAggregator::Add(const S2Geography& geog) { } if (geog.dimension() == 2) { - auto poly_ptr = dynamic_cast(&geog); + auto poly_ptr = dynamic_cast(&geog); if (poly_ptr != nullptr) { query_.AddPolygon(*poly_ptr->Polygon()); } else { @@ -192,10 +192,10 @@ void S2ConvexHullAggregator::Add(const S2Geography& geog) { } } -std::unique_ptr S2ConvexHullAggregator::Finalize() { +std::unique_ptr S2ConvexHullAggregator::Finalize() { auto polygon = absl::make_unique(); polygon->Init(query_.GetConvexHull()); - return absl::make_unique(std::move(polygon)); + return absl::make_unique(std::move(polygon)); } } diff --git a/src/s2-geography/accessors-geog.hpp b/src/s2-geography/accessors-geog.hpp index 42885de4..e115f7fb 100644 --- a/src/s2-geography/accessors-geog.hpp +++ b/src/s2-geography/accessors-geog.hpp @@ -22,10 +22,10 @@ class S2CentroidAggregator: public S2Aggregator { S2Point centroid_; }; -class S2ConvexHullAggregator: public S2Aggregator> { +class S2ConvexHullAggregator: public S2Aggregator> { public: void Add(const S2Geography& geog); - std::unique_ptr Finalize(); + std::unique_ptr Finalize(); private: S2ConvexHullQuery query_; diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index 5499b69f..ce442213 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -6,7 +6,7 @@ namespace s2geography { -bool s2_is_collection(const S2GeographyOwningPolygon& geog) { +bool s2_is_collection(const PolygonGeography& geog) { int num_outer_loops = 0; for (int i = 0; i < geog.Polygon()->num_loops(); i++) { S2Loop* loop = geog.Polygon()->loop(i); @@ -43,11 +43,11 @@ bool s2_is_collection(const S2Geography& geog) { return false; } - auto polygon_geog_ptr = dynamic_cast(&geog); + auto polygon_geog_ptr = dynamic_cast(&geog); if (polygon_geog_ptr != nullptr) { return s2_is_collection(*polygon_geog_ptr); } else { - std::unique_ptr built = s2_build_polygon(geog); + std::unique_ptr built = s2_build_polygon(geog); return s2_is_collection(*built); } } @@ -97,7 +97,7 @@ bool s2_is_empty(const S2Geography& geog) { return true; } -double s2_area(const S2GeographyOwningPolygon& geog) { +double s2_area(const PolygonGeography& geog) { return geog.Polygon()->GetArea(); } @@ -114,7 +114,7 @@ double s2_area(const S2Geography& geog) { return 0; } - auto polygon_geog_ptr = dynamic_cast(&geog); + auto polygon_geog_ptr = dynamic_cast(&geog); if (polygon_geog_ptr != nullptr) { return s2_area(*polygon_geog_ptr); } @@ -124,7 +124,7 @@ double s2_area(const S2Geography& geog) { return s2_area(*collection_geog_ptr); } - std::unique_ptr built = s2_build_polygon(geog); + std::unique_ptr built = s2_build_polygon(geog); return s2_area(*built); } @@ -192,7 +192,7 @@ double s2_y(const S2Geography& geog) { return out; } -bool s2_find_validation_error(const S2GeographyOwningPolyline& geog, S2Error* error) { +bool s2_find_validation_error(const PolylineGeography& geog, S2Error* error) { for (const auto& polyline: geog.Polylines()) { if (polyline->FindValidationError(error)) { return true; @@ -202,7 +202,7 @@ bool s2_find_validation_error(const S2GeographyOwningPolyline& geog, S2Error* er return false; } -bool s2_find_validation_error(const S2GeographyOwningPolygon& geog, S2Error* error) { +bool s2_find_validation_error(const PolygonGeography& geog, S2Error* error) { return geog.Polygon()->FindValidationError(error); } @@ -223,14 +223,14 @@ bool s2_find_validation_error(const S2Geography& geog, S2Error* error) { } if (geog.dimension() == 1) { - auto poly_ptr = dynamic_cast(&geog); + auto poly_ptr = dynamic_cast(&geog); if (poly_ptr != nullptr) { return s2_find_validation_error(*poly_ptr, error); } else { try { auto poly = s2_build_polyline(geog); return s2_find_validation_error(*poly, error); - } catch (S2GeographyException& e) { + } catch (Exception& e) { error->Init(S2Error::INTERNAL, "%s", e.what()); return true; } @@ -238,14 +238,14 @@ bool s2_find_validation_error(const S2Geography& geog, S2Error* error) { } if (geog.dimension() == 2) { - auto poly_ptr = dynamic_cast(&geog); + auto poly_ptr = dynamic_cast(&geog); if (poly_ptr != nullptr) { return s2_find_validation_error(*poly_ptr, error); } else { try { auto poly = s2_build_polygon(geog); return s2_find_validation_error(*poly, error); - } catch (S2GeographyException& e) { + } catch (Exception& e) { error->Init(S2Error::INTERNAL, "%s", e.what()); return true; } @@ -259,13 +259,13 @@ bool s2_find_validation_error(const S2Geography& geog, S2Error* error) { try { auto collection = s2_build_polygon(geog); return s2_find_validation_error(*collection, error); - } catch (S2GeographyException& e) { + } catch (Exception& e) { error->Init(S2Error::INTERNAL, "%s", e.what()); return true; } } - throw S2GeographyException("s2_find_validation() error not implemented for this geography type"); + throw Exception("s2_find_validation() error not implemented for this geography type"); } } diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index a39bd454..b88d3cdf 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -29,19 +29,19 @@ std::unique_ptr s2_geography_from_layers(std::vector point bool include_points = point_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; if (has_polygon && polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { - throw S2GeographyException("Output contained unexpected polygon"); + throw Exception("Output contained unexpected polygon"); } else if (has_polygon && polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { has_polygon = false; } if (has_polylines && polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { - throw S2GeographyException("Output contained unexpected polylines"); + throw Exception("Output contained unexpected polylines"); } else if (has_polylines && polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { has_polylines = false; } if (has_points && point_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { - throw S2GeographyException("Output contained unexpected points"); + throw Exception("Output contained unexpected points"); } else if (has_points && point_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { has_points = false; } @@ -58,11 +58,11 @@ std::unique_ptr s2_geography_from_layers(std::vector point } if (has_polylines) { - features.push_back(absl::make_unique(std::move(polylines))); + features.push_back(absl::make_unique(std::move(polylines))); } if (has_polygon) { - features.push_back(absl::make_unique(std::move(polygon))); + features.push_back(absl::make_unique(std::move(polygon))); } return absl::make_unique(std::move(features)); @@ -70,9 +70,9 @@ std::unique_ptr s2_geography_from_layers(std::vector point // return single dimension output if (has_polygon || (included_dimensions == 1 && include_polygon)) { - return absl::make_unique(std::move(polygon)); + return absl::make_unique(std::move(polygon)); } else if (has_polylines || (included_dimensions == 1 && include_polylines)) { - return absl::make_unique(std::move(polylines)); + return absl::make_unique(std::move(polylines)); } else if (has_points || (included_dimensions == 1 && include_points)) { return absl::make_unique(std::move(points)); } else { @@ -80,8 +80,8 @@ std::unique_ptr s2_geography_from_layers(std::vector point } } -std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, +std::unique_ptr s2_boolean_operation(const ShapeIndexGeography& geog1, + const ShapeIndexGeography& geog2, S2BooleanOperation::OpType op_type, const S2GeographyOptions& options) { @@ -107,7 +107,7 @@ std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& g // do the boolean operation, build layers, and check for errors S2Error error; if (!op.Build(geog1.ShapeIndex(), geog2.ShapeIndex(), &error)) { - throw S2GeographyException(error.text()); + throw Exception(error.text()); } // construct output @@ -121,7 +121,7 @@ std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& g ); } -std::unique_ptr s2_unary_union(const S2GeographyOwningPolygon& geog, +std::unique_ptr s2_unary_union(const PolygonGeography& geog, const S2GeographyOptions& options) { // A geography with invalid loops won't work with the S2BooleanOperation // we will use to accumulate (i.e., union) valid polygons, @@ -147,7 +147,7 @@ std::unique_ptr s2_unary_union(const S2GeographyOwning builder.AddShape(S2Loop::Shape(geog.Polygon()->loop(i))); S2Error error; if (!builder.Build(&error)) { - throw S2GeographyException(error.text()); + throw Exception(error.text()); } // Check if the builder created a polygon whose boundary contained more than @@ -177,10 +177,10 @@ std::unique_ptr s2_unary_union(const S2GeographyOwning accumulated_polygon.swap(polygon_result); } - return absl::make_unique(std::move(accumulated_polygon)); + return absl::make_unique(std::move(accumulated_polygon)); } -std::unique_ptr s2_unary_union(const S2GeographyShapeIndex& geog, +std::unique_ptr s2_unary_union(const ShapeIndexGeography& geog, const S2GeographyOptions& options) { // complex union only needed when a polygon is involved bool simple_union_ok = s2_is_empty(geog) || s2_dimension(geog) < 2; @@ -195,13 +195,13 @@ std::unique_ptr s2_unary_union(const S2GeographyShapeIndex& geog, } if (simple_union_ok) { - S2GeographyShapeIndex empty; + ShapeIndexGeography empty; return s2_boolean_operation(geog, empty, S2BooleanOperation::OpType::UNION, options); } if (geog.dimension() == 2) { // If we've made it here we have an invalid polygon on our hands. - auto poly_ptr = dynamic_cast(&geog); + auto poly_ptr = dynamic_cast(&geog); if (poly_ptr != nullptr) { return s2_unary_union(*poly_ptr, options); } else { @@ -210,7 +210,7 @@ std::unique_ptr s2_unary_union(const S2GeographyShapeIndex& geog, } } - throw S2GeographyException( + throw Exception( "s2_unary_union() for multidimensional collections not implemented"); } @@ -261,7 +261,7 @@ std::unique_ptr s2_rebuild(const S2Geography& geog, // build the output S2Error error; if (!builder.Build(&error)) { - throw S2GeographyException(error.text()); + throw Exception(error.text()); } // construct output @@ -298,7 +298,7 @@ std::unique_ptr s2_build_point(const S2Geography& geog) { } -std::unique_ptr s2_build_polyline(const S2Geography& geog) { +std::unique_ptr s2_build_polyline(const S2Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, S2GeographyOptions(), @@ -306,12 +306,12 @@ std::unique_ptr s2_build_polyline(const S2Geography& S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE, S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR); - return std::unique_ptr( - dynamic_cast(geog_out.release())); + return std::unique_ptr( + dynamic_cast(geog_out.release())); } -std::unique_ptr s2_build_polygon(const S2Geography& geog) { +std::unique_ptr s2_build_polygon(const S2Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, S2GeographyOptions(), @@ -319,8 +319,8 @@ std::unique_ptr s2_build_polygon(const S2Geography& ge S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE); - return std::unique_ptr( - dynamic_cast(geog_out.release())); + return std::unique_ptr( + dynamic_cast(geog_out.release())); } @@ -337,7 +337,7 @@ void S2CoverageUnionAggregator::Add(const S2Geography& geog) { } std::unique_ptr S2CoverageUnionAggregator::Finalize() { - S2GeographyShapeIndex empty_index_; + ShapeIndexGeography empty_index_; return s2_boolean_operation(index_, empty_index_, S2BooleanOperation::OpType::UNION, options_); } diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index b0a4a202..60ed4661 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -34,12 +34,12 @@ class S2GeographyOptions { OutputAction polygon_layer_action; }; -std::unique_ptr s2_boolean_operation(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, +std::unique_ptr s2_boolean_operation(const ShapeIndexGeography& geog1, + const ShapeIndexGeography& geog2, S2BooleanOperation::OpType op_type, const S2GeographyOptions& options); -std::unique_ptr s2_unary_union(const S2GeographyShapeIndex& geog, +std::unique_ptr s2_unary_union(const ShapeIndexGeography& geog, const S2GeographyOptions& options); std::unique_ptr s2_rebuild(const S2Geography& geog, @@ -47,9 +47,9 @@ std::unique_ptr s2_rebuild(const S2Geography& geog, std::unique_ptr s2_build_point(const S2Geography& geog); -std::unique_ptr s2_build_polyline(const S2Geography& geog); +std::unique_ptr s2_build_polyline(const S2Geography& geog); -std::unique_ptr s2_build_polygon(const S2Geography& geog); +std::unique_ptr s2_build_polygon(const S2Geography& geog); class S2RebuildAggregator: public S2Aggregator> { public: @@ -59,7 +59,7 @@ class S2RebuildAggregator: public S2Aggregator> { private: S2GeographyOptions options_; - S2GeographyShapeIndex index_; + ShapeIndexGeography index_; }; class S2CoverageUnionAggregator: public S2Aggregator> { @@ -71,7 +71,7 @@ class S2CoverageUnionAggregator: public S2Aggregator> { @@ -83,8 +83,8 @@ class S2UnionAggregator: public S2Aggregator> { private: class Node { public: - S2GeographyShapeIndex index1; - S2GeographyShapeIndex index2; + ShapeIndexGeography index1; + ShapeIndexGeography index2; std::vector> data; std::unique_ptr Merge(const S2GeographyOptions& options); }; diff --git a/src/s2-geography/constructor.hpp b/src/s2-geography/constructor.hpp index 0f5bf9b2..491d7712 100644 --- a/src/s2-geography/constructor.hpp +++ b/src/s2-geography/constructor.hpp @@ -73,7 +73,7 @@ class PointConstructor: public Constructor { geometry_type != util::GeometryType::POINT && geometry_type != util::GeometryType::MULTIPOINT && geometry_type != util::GeometryType::GEOMETRYCOLLECTION) { - throw S2GeographyException( + throw Exception( "PointConstructor input must be empty, point, multipoint, or collection"); } @@ -120,7 +120,7 @@ class PolylineConstructor: public Constructor { geometry_type != util::GeometryType::LINESTRING && geometry_type != util::GeometryType::MULTILINESTRING && geometry_type != util::GeometryType::GEOMETRYCOLLECTION) { - throw S2GeographyException( + throw Exception( "PolylineConstructor input must be empty, linestring, multilinestring, or collection"); } @@ -136,7 +136,7 @@ class PolylineConstructor: public Constructor { if (options_.check() && !polyline->IsValid()) { polyline->FindValidationError(&error_); - throw S2GeographyException(error_.text()); + throw Exception(error_.text()); } polylines_.push_back(std::move(polyline)); @@ -145,13 +145,13 @@ class PolylineConstructor: public Constructor { } std::unique_ptr finish() { - std::unique_ptr result; + std::unique_ptr result; if (polylines_.size() > 0) { - result = absl::make_unique(std::move(polylines_)); + result = absl::make_unique(std::move(polylines_)); polylines_.clear(); } else { - result = absl::make_unique(); + result = absl::make_unique(); } return std::unique_ptr(result.release()); @@ -193,7 +193,7 @@ class PolygonConstructor: public Constructor { err << "Loop " << (loops_.size()) << " is not valid: "; loop->FindValidationError(&error_); err << error_.text(); - throw S2GeographyException(err.str()); + throw Exception(err.str()); } loops_.push_back(std::move(loop)); @@ -213,10 +213,10 @@ class PolygonConstructor: public Constructor { if (options_.check() && !polygon->IsValid()) { polygon->FindValidationError(&error_); - throw S2GeographyException(error_.text()); + throw Exception(error_.text()); } - auto result = absl::make_unique(std::move(polygon)); + auto result = absl::make_unique(std::move(polygon)); return std::unique_ptr(result.release()); } @@ -264,7 +264,7 @@ class CollectionConstructor: public Constructor { this->active_constructor_ = this->collection_constructor_.get(); break; default: - throw S2GeographyException("CollectionConstructor: unsupported geometry type"); + throw Exception("CollectionConstructor: unsupported geometry type"); } active_constructor_->geom_start(geometry_type, size); @@ -334,7 +334,7 @@ class VectorConstructor: public CollectionConstructor { } else { std::unique_ptr feature = std::move(features_.back()); if (feature.get() == nullptr) { - throw S2GeographyException("finish_feature() generated nullptr"); + throw Exception("finish_feature() generated nullptr"); } features_.pop_back(); diff --git a/src/s2-geography/coverings.cpp b/src/s2-geography/coverings.cpp index 700f908a..eb8dd8d9 100644 --- a/src/s2-geography/coverings.cpp +++ b/src/s2-geography/coverings.cpp @@ -55,7 +55,7 @@ S2Point s2_point_on_surface(const S2Geography& geog, S2RegionCoverer& coverer) { return closest_pt; } - throw S2GeographyException("s2_point_on_surface() not implemented for polyline"); + throw Exception("s2_point_on_surface() not implemented for polyline"); } void s2_covering(const S2Geography& geog, std::vector* covering, @@ -68,7 +68,7 @@ void s2_interior_covering(const S2Geography& geog, std::vector* coveri coverer.GetInteriorCovering(*geog.Region(), covering); } -void s2_covering_buffered(const S2GeographyShapeIndex& geog, double distance_radians, +void s2_covering_buffered(const ShapeIndexGeography& geog, double distance_radians, std::vector* covering, S2RegionCoverer& coverer) { S2ShapeIndexBufferedRegion region(&geog.ShapeIndex(), S1ChordAngle::Radians(distance_radians)); diff --git a/src/s2-geography/coverings.hpp b/src/s2-geography/coverings.hpp index 7b7727ca..d9d6ceb3 100644 --- a/src/s2-geography/coverings.hpp +++ b/src/s2-geography/coverings.hpp @@ -12,7 +12,7 @@ void s2_covering(const S2Geography& geog, std::vector* covering, S2RegionCoverer& coverer); void s2_interior_covering(const S2Geography& geog, std::vector* covering, S2RegionCoverer& coverer); -void s2_covering_buffered(const S2GeographyShapeIndex& geog, double distance_radians, +void s2_covering_buffered(const ShapeIndexGeography& geog, double distance_radians, std::vector* covering, S2RegionCoverer& coverer); diff --git a/src/s2-geography/distance.cpp b/src/s2-geography/distance.cpp index 5a686b18..24fd4509 100644 --- a/src/s2-geography/distance.cpp +++ b/src/s2-geography/distance.cpp @@ -7,7 +7,7 @@ namespace s2geography { -double s2_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2) { +double s2_distance(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2) { S2ClosestEdgeQuery query(&geog1.ShapeIndex()); S2ClosestEdgeQuery::ShapeIndexTarget target(&geog2.ShapeIndex()); @@ -17,7 +17,7 @@ double s2_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeInd return angle.ToAngle().radians(); } -double s2_max_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2) { +double s2_max_distance(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2) { S2FurthestEdgeQuery query(&geog1.ShapeIndex()); S2FurthestEdgeQuery::ShapeIndexTarget target(&geog2.ShapeIndex()); @@ -27,12 +27,12 @@ double s2_max_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShap return angle.ToAngle().radians(); } -S2Point s2_closest_point(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2) { +S2Point s2_closest_point(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2) { return s2_minimum_clearance_line_between(geog1, geog2).first; } std::pair s2_minimum_clearance_line_between( - const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2) { + const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2) { S2ClosestEdgeQuery query1(&geog1.ShapeIndex()); query1.mutable_options()->set_include_interiors(false); S2ClosestEdgeQuery::ShapeIndexTarget target(&geog2.ShapeIndex()); @@ -54,7 +54,7 @@ std::pair s2_minimum_clearance_line_between( // what if result2 has no edges? if (result2.is_interior()) { - throw S2GeographyException("S2ClosestEdgeQuery result is interior!"); + throw Exception("S2ClosestEdgeQuery result is interior!"); } S2Shape::Edge edge2 = query2.GetEdge(result2); diff --git a/src/s2-geography/distance.hpp b/src/s2-geography/distance.hpp index 9e888c4d..cd257298 100644 --- a/src/s2-geography/distance.hpp +++ b/src/s2-geography/distance.hpp @@ -5,10 +5,10 @@ namespace s2geography { -double s2_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2); -double s2_max_distance(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2); -S2Point s2_closest_point(const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2); +double s2_distance(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2); +double s2_max_distance(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2); +S2Point s2_closest_point(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2); std::pair s2_minimum_clearance_line_between( - const S2GeographyShapeIndex& geog1, const S2GeographyShapeIndex& geog2); + const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2); } diff --git a/src/s2-geography/geography.cpp b/src/s2-geography/geography.cpp index 0e31c0a1..04aa4e25 100644 --- a/src/s2-geography/geography.cpp +++ b/src/s2-geography/geography.cpp @@ -98,13 +98,13 @@ void PointGeography::GetCellUnionBound(std::vector* cell_ids) const { } -int S2GeographyOwningPolyline::num_shapes() const { return polylines_.size(); } +int PolylineGeography::num_shapes() const { return polylines_.size(); } -std::unique_ptr S2GeographyOwningPolyline::Shape(int id) const { +std::unique_ptr PolylineGeography::Shape(int id) const { return absl::make_unique(polylines_[id].get()); } -std::unique_ptr S2GeographyOwningPolyline::Region() const { +std::unique_ptr PolylineGeography::Region() const { auto region = absl::make_unique(); for (const auto& polyline: polylines_) { region->Add(absl::make_unique(polyline.get())); @@ -114,21 +114,21 @@ std::unique_ptr S2GeographyOwningPolyline::Region() const { return std::unique_ptr(region.release()); } -void S2GeographyOwningPolyline::GetCellUnionBound(std::vector* cell_ids) const { +void PolylineGeography::GetCellUnionBound(std::vector* cell_ids) const { for (const auto& polyline: polylines_) { polyline->GetCellUnionBound(cell_ids); } } -std::unique_ptr S2GeographyOwningPolygon::Shape(int id) const { +std::unique_ptr PolygonGeography::Shape(int id) const { return absl::make_unique(polygon_.get()); } -std::unique_ptr S2GeographyOwningPolygon::Region() const { +std::unique_ptr PolygonGeography::Region() const { return absl::make_unique(polygon_.get()); } -void S2GeographyOwningPolygon::GetCellUnionBound(std::vector* cell_ids) const { +void PolygonGeography::GetCellUnionBound(std::vector* cell_ids) const { polygon_->GetCellUnionBound(cell_ids); } @@ -144,7 +144,7 @@ std::unique_ptr S2GeographyCollection::Shape(int id) const { } } - throw S2GeographyException("shape id out of bounds"); + throw Exception("shape id out of bounds"); } std::unique_ptr S2GeographyCollection::Region() const { @@ -157,14 +157,14 @@ std::unique_ptr S2GeographyCollection::Region() const { return std::unique_ptr(region.release()); } -int S2GeographyShapeIndex::num_shapes() const { return shape_index_.num_shape_ids(); } +int ShapeIndexGeography::num_shapes() const { return shape_index_.num_shape_ids(); } -std::unique_ptr S2GeographyShapeIndex::Shape(int id) const { +std::unique_ptr ShapeIndexGeography::Shape(int id) const { S2Shape* shape = shape_index_.shape(id); return std::unique_ptr(new S2ShapeWrapper(shape)); } -std::unique_ptr S2GeographyShapeIndex::Region() const { +std::unique_ptr ShapeIndexGeography::Region() const { auto region = absl::make_unique>(&shape_index_); // because Rtools for R 3.6 on Windows complains about a direct // return region diff --git a/src/s2-geography/geography.hpp b/src/s2-geography/geography.hpp index 8a61ef74..db3e040a 100644 --- a/src/s2-geography/geography.hpp +++ b/src/s2-geography/geography.hpp @@ -10,9 +10,9 @@ namespace s2geography { -class S2GeographyException: public std::runtime_error { +class Exception: public std::runtime_error { public: - S2GeographyException(std::string what): std::runtime_error(what.c_str()) {} + Exception(std::string what): std::runtime_error(what.c_str()) {} }; // An S2Geography is an abstraction of S2 types that is designed to closely match @@ -93,13 +93,13 @@ class PointGeography: public S2Geography { // An S2Geography representing zero or more polylines using the S2Polyline class // as the underlying representation. -class S2GeographyOwningPolyline: public S2Geography { +class PolylineGeography: public S2Geography { public: - S2GeographyOwningPolyline() {} - S2GeographyOwningPolyline(std::unique_ptr polyline) { + PolylineGeography() {} + PolylineGeography(std::unique_ptr polyline) { polylines_.push_back(std::move(polyline)); } - S2GeographyOwningPolyline(std::vector> polylines): + PolylineGeography(std::vector> polylines): polylines_(std::move(polylines)) {} int dimension() const { return 1; } @@ -121,10 +121,10 @@ class S2GeographyOwningPolyline: public S2Geography { // as the underlying representation. Note that a single S2Polygon (from the S2 // perspective) can represent zero or more polygons (from the simple features // perspective). -class S2GeographyOwningPolygon: public S2Geography { +class PolygonGeography: public S2Geography { public: - S2GeographyOwningPolygon() {} - S2GeographyOwningPolygon(std::unique_ptr polygon): + PolygonGeography() {} + PolygonGeography(std::unique_ptr polygon): polygon_(std::move(polygon)) {} int dimension() const { return 2; } @@ -176,15 +176,15 @@ class S2GeographyCollection: public S2Geography { // These are used as inputs for operations that are implemented in S2 // using the S2ShapeIndex (e.g., boolean operations). If an S2Geography // instance will be used repeatedly, it will be faster to construct -// one S2GeographyShapeIndex and use it repeatedly. This class does not +// one ShapeIndexGeography and use it repeatedly. This class does not // own any S2Geography objects that are added do it and thus is only // valid for the scope of those objects. -class S2GeographyShapeIndex: public S2Geography { +class ShapeIndexGeography: public S2Geography { public: - S2GeographyShapeIndex(MutableS2ShapeIndex::Options options = MutableS2ShapeIndex::Options()) + ShapeIndexGeography(MutableS2ShapeIndex::Options options = MutableS2ShapeIndex::Options()) : shape_index_(options) {} - explicit S2GeographyShapeIndex(const S2Geography& geog) { + explicit ShapeIndexGeography(const S2Geography& geog) { Add(geog); } diff --git a/src/s2-geography/index.hpp b/src/s2-geography/index.hpp index 948b794e..92aefb2c 100644 --- a/src/s2-geography/index.hpp +++ b/src/s2-geography/index.hpp @@ -7,7 +7,7 @@ namespace s2geography { -// Unlike the S2GeographyShapeIndex, whose function is to index a single S2Geography +// Unlike the ShapeIndexGeography, whose function is to index a single S2Geography // or index multiple S2Geography objects as if they were a single S2Geography, // the S2GeographyIndex exists to index a vector of S2Geography objects (like a // GEOSSTRTree index), providing (hopefully) rapid access to possibly intersecting diff --git a/src/s2-geography/linear-referencing.cpp b/src/s2-geography/linear-referencing.cpp index f8404f57..25afd220 100644 --- a/src/s2-geography/linear-referencing.cpp +++ b/src/s2-geography/linear-referencing.cpp @@ -6,7 +6,7 @@ namespace s2geography { -double s2_project_normalized(const S2GeographyOwningPolyline& geog1, +double s2_project_normalized(const PolylineGeography& geog1, const S2Point& point) { if (geog1.Polylines().size() != 1 || point.Norm2() == 0) { return NAN; @@ -34,7 +34,7 @@ double s2_project_normalized(const S2Geography& geog1, const S2Geography& geog2) } } - auto geog1_poly_ptr = dynamic_cast(&geog1); + auto geog1_poly_ptr = dynamic_cast(&geog1); if (geog1_poly_ptr != nullptr) { return s2_project_normalized(*geog1_poly_ptr, point); } @@ -43,13 +43,13 @@ double s2_project_normalized(const S2Geography& geog1, const S2Geography& geog2) return s2_project_normalized(*geog_poly, geog2); } -S2Point s2_interpolate_normalized(const S2GeographyOwningPolyline& geog, double distance_norm) { +S2Point s2_interpolate_normalized(const PolylineGeography& geog, double distance_norm) { if (s2_is_empty(geog)) { return S2Point(); } else if (geog.Polylines().size() == 1) { return geog.Polylines()[0]->Interpolate(distance_norm); } else { - throw S2GeographyException("`geog` must contain 0 or 1 polyines"); + throw Exception("`geog` must contain 0 or 1 polyines"); } } @@ -59,10 +59,10 @@ S2Point s2_interpolate_normalized(const S2Geography& geog, double distance_norm) } if (geog.dimension() != 1 || geog.num_shapes() > 1) { - throw S2GeographyException("`geog` must be a single polyline"); + throw Exception("`geog` must be a single polyline"); } - auto geog_poly_ptr = dynamic_cast(&geog); + auto geog_poly_ptr = dynamic_cast(&geog); if (geog_poly_ptr != nullptr) { return s2_interpolate_normalized(*geog_poly_ptr, distance_norm); } diff --git a/src/s2-geography/predicates.cpp b/src/s2-geography/predicates.cpp index 6cd47859..f2003f9c 100644 --- a/src/s2-geography/predicates.cpp +++ b/src/s2-geography/predicates.cpp @@ -8,8 +8,8 @@ namespace s2geography { -bool s2_intersects(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, +bool s2_intersects(const ShapeIndexGeography& geog1, + const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options) { return S2BooleanOperation::Intersects( geog1.ShapeIndex(), @@ -17,8 +17,8 @@ bool s2_intersects(const S2GeographyShapeIndex& geog1, options); } -bool s2_equals(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, +bool s2_equals(const ShapeIndexGeography& geog1, + const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options) { return S2BooleanOperation::Equals( geog1.ShapeIndex(), @@ -26,8 +26,8 @@ bool s2_equals(const S2GeographyShapeIndex& geog1, options); } -bool s2_contains(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, +bool s2_contains(const ShapeIndexGeography& geog1, + const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options) { if (s2_is_empty(geog2)) { return false; @@ -53,7 +53,7 @@ bool s2_contains(const S2GeographyShapeIndex& geog1, // ...it isn't implemented here because the options creation should be done // outside of any loop. -bool s2_intersects_box(const S2GeographyShapeIndex& geog1, +bool s2_intersects_box(const ShapeIndexGeography& geog1, const S2LatLngRect& rect, const S2BooleanOperation::Options& options, double tolerance) { diff --git a/src/s2-geography/predicates.hpp b/src/s2-geography/predicates.hpp index a3cf810e..c267d733 100644 --- a/src/s2-geography/predicates.hpp +++ b/src/s2-geography/predicates.hpp @@ -7,23 +7,23 @@ namespace s2geography { -bool s2_intersects(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, +bool s2_intersects(const ShapeIndexGeography& geog1, + const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options); -bool s2_equals(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, +bool s2_equals(const ShapeIndexGeography& geog1, + const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options); -bool s2_contains(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, +bool s2_contains(const ShapeIndexGeography& geog1, + const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options); -bool s2_touches(const S2GeographyShapeIndex& geog1, - const S2GeographyShapeIndex& geog2, +bool s2_touches(const ShapeIndexGeography& geog1, + const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options); -bool s2_intersects_box(const S2GeographyShapeIndex& geog1, +bool s2_intersects_box(const ShapeIndexGeography& geog1, const S2LatLngRect& rect, const S2BooleanOperation::Options& options, double tolerance); diff --git a/src/s2-matrix.cpp b/src/s2-matrix.cpp index 1706b549..82919e95 100644 --- a/src/s2-matrix.cpp +++ b/src/s2-matrix.cpp @@ -184,8 +184,8 @@ class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperatoroptions); }; @@ -240,8 +240,8 @@ List cpp_s2_within_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} - bool actuallyIntersects(const s2geography::S2GeographyShapeIndex& index1, - const s2geography::S2GeographyShapeIndex& index2, + bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, + const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) { // note reversed index2, index1 return s2geography::s2_contains(index2, index1, this->options); @@ -258,8 +258,8 @@ List cpp_s2_intersects_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} - bool actuallyIntersects(const s2geography::S2GeographyShapeIndex& index1, - const s2geography::S2GeographyShapeIndex& index2, + bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, + const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) { return s2geography::s2_intersects(index1, index2, this->options); }; @@ -275,8 +275,8 @@ List cpp_s2_equals_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} - bool actuallyIntersects(const s2geography::S2GeographyShapeIndex& index1, - const s2geography::S2GeographyShapeIndex& index2, + bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, + const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) { return s2geography::s2_equals(index1, index2, this->options); }; @@ -301,8 +301,8 @@ List cpp_s2_touches_matrix(List geog1, List geog2, List s2options) { this->openOptions.set_polyline_model(S2BooleanOperation::PolylineModel::OPEN); } - bool actuallyIntersects(const s2geography::S2GeographyShapeIndex& index1, - const s2geography::S2GeographyShapeIndex& index2, + bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, + const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) { return s2geography::s2_intersects(index1, index2, this->closedOptions) && !s2geography::s2_intersects(index1, index2, this->openOptions); From 1b4137b30c9909416add23e56f8aa92423a86c42 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 21 Apr 2022 14:45:27 -0300 Subject: [PATCH 86/88] more renaming --- src/s2-geography/accessors-geog.cpp | 14 ++--- src/s2-geography/accessors-geog.hpp | 6 +-- src/s2-geography/accessors.cpp | 1 - src/s2-geography/aggregator.hpp | 2 +- src/s2-geography/build.cpp | 70 ++++++++++++------------- src/s2-geography/build.hpp | 30 +++++------ src/s2-geography/linear-referencing.cpp | 4 +- src/s2-options.h | 12 ++--- src/s2-transformers.cpp | 10 ++-- 9 files changed, 74 insertions(+), 75 deletions(-) diff --git a/src/s2-geography/accessors-geog.cpp b/src/s2-geography/accessors-geog.cpp index e71736ed..200a1546 100644 --- a/src/s2-geography/accessors-geog.cpp +++ b/src/s2-geography/accessors-geog.cpp @@ -121,18 +121,18 @@ std::unique_ptr s2_convex_hull(const S2Geography& geog) { } -void S2CentroidAggregator::Add(const S2Geography& geog) { +void CentroidAggregator::Add(const S2Geography& geog) { S2Point centroid = s2_centroid(geog); if (centroid.Norm2() > 0) { centroid_ += centroid.Normalize(); } } -void S2CentroidAggregator::Merge(const S2CentroidAggregator& other) { +void CentroidAggregator::Merge(const CentroidAggregator& other) { centroid_ += other.centroid_; } -S2Point S2CentroidAggregator::Finalize() { +S2Point CentroidAggregator::Finalize() { if (centroid_.Norm2() > 0) { return centroid_.Normalize(); } else { @@ -148,7 +148,7 @@ void S2ConvexHullAggregator::Add(const S2Geography& geog) { query_.AddPoint(point); } } else { - keep_alive_.push_back(s2_rebuild(geog, S2GeographyOptions())); + keep_alive_.push_back(s2_rebuild(geog, GlobalOptions())); Add(*keep_alive_.back()); } @@ -162,7 +162,7 @@ void S2ConvexHullAggregator::Add(const S2Geography& geog) { query_.AddPolyline(*polyline); } } else { - keep_alive_.push_back(s2_rebuild(geog, S2GeographyOptions())); + keep_alive_.push_back(s2_rebuild(geog, GlobalOptions())); Add(*keep_alive_.back()); } @@ -174,7 +174,7 @@ void S2ConvexHullAggregator::Add(const S2Geography& geog) { if (poly_ptr != nullptr) { query_.AddPolygon(*poly_ptr->Polygon()); } else { - keep_alive_.push_back(s2_rebuild(geog, S2GeographyOptions())); + keep_alive_.push_back(s2_rebuild(geog, GlobalOptions())); Add(*keep_alive_.back()); } @@ -187,7 +187,7 @@ void S2ConvexHullAggregator::Add(const S2Geography& geog) { Add(*feature); } } else { - keep_alive_.push_back(s2_rebuild(geog, S2GeographyOptions())); + keep_alive_.push_back(s2_rebuild(geog, GlobalOptions())); Add(*keep_alive_.back()); } } diff --git a/src/s2-geography/accessors-geog.hpp b/src/s2-geography/accessors-geog.hpp index e115f7fb..91dc83ed 100644 --- a/src/s2-geography/accessors-geog.hpp +++ b/src/s2-geography/accessors-geog.hpp @@ -12,17 +12,17 @@ S2Point s2_centroid(const S2Geography& geog); std::unique_ptr s2_boundary(const S2Geography& geog); std::unique_ptr s2_convex_hull(const S2Geography& geog); -class S2CentroidAggregator: public S2Aggregator { +class CentroidAggregator: public Aggregator { public: void Add(const S2Geography& geog); - void Merge(const S2CentroidAggregator& other); + void Merge(const CentroidAggregator& other); S2Point Finalize(); private: S2Point centroid_; }; -class S2ConvexHullAggregator: public S2Aggregator> { +class S2ConvexHullAggregator: public Aggregator> { public: void Add(const S2Geography& geog); std::unique_ptr Finalize(); diff --git a/src/s2-geography/accessors.cpp b/src/s2-geography/accessors.cpp index ce442213..1ea18d31 100644 --- a/src/s2-geography/accessors.cpp +++ b/src/s2-geography/accessors.cpp @@ -1,7 +1,6 @@ #include "geography.hpp" #include "accessors.hpp" -#include "aggregator.hpp" #include "build.hpp" namespace s2geography { diff --git a/src/s2-geography/aggregator.hpp b/src/s2-geography/aggregator.hpp index 8062f9ec..de8e8221 100644 --- a/src/s2-geography/aggregator.hpp +++ b/src/s2-geography/aggregator.hpp @@ -6,7 +6,7 @@ namespace s2geography { template -class S2Aggregator { +class Aggregator { public: virtual void Add(const S2Geography& geog, Params... parameters) = 0; virtual ReturnType Finalize() = 0; diff --git a/src/s2-geography/build.cpp b/src/s2-geography/build.cpp index b88d3cdf..8afcc279 100644 --- a/src/s2-geography/build.cpp +++ b/src/s2-geography/build.cpp @@ -15,34 +15,34 @@ namespace s2geography { std::unique_ptr s2_geography_from_layers(std::vector points, std::vector> polylines, std::unique_ptr polygon, - S2GeographyOptions::OutputAction point_layer_action, - S2GeographyOptions::OutputAction polyline_layer_action, - S2GeographyOptions::OutputAction polygon_layer_action) { + GlobalOptions::OutputAction point_layer_action, + GlobalOptions::OutputAction polyline_layer_action, + GlobalOptions::OutputAction polygon_layer_action) { // count non-empty dimensions bool has_polygon = !polygon->is_empty(); bool has_polylines = polylines.size() > 0; bool has_points = points.size() > 0; // use the requstested dimensions to produce the right kind of EMTPY - bool include_polygon = polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; - bool include_polylines = polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; - bool include_points = point_layer_action == S2GeographyOptions::OUTPUT_ACTION_INCLUDE; + bool include_polygon = polygon_layer_action == GlobalOptions::OUTPUT_ACTION_INCLUDE; + bool include_polylines = polyline_layer_action == GlobalOptions::OUTPUT_ACTION_INCLUDE; + bool include_points = point_layer_action == GlobalOptions::OUTPUT_ACTION_INCLUDE; - if (has_polygon && polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { + if (has_polygon && polygon_layer_action == GlobalOptions::OUTPUT_ACTION_ERROR) { throw Exception("Output contained unexpected polygon"); - } else if (has_polygon && polygon_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { + } else if (has_polygon && polygon_layer_action == GlobalOptions::OUTPUT_ACTION_IGNORE) { has_polygon = false; } - if (has_polylines && polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { + if (has_polylines && polyline_layer_action == GlobalOptions::OUTPUT_ACTION_ERROR) { throw Exception("Output contained unexpected polylines"); - } else if (has_polylines && polyline_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { + } else if (has_polylines && polyline_layer_action == GlobalOptions::OUTPUT_ACTION_IGNORE) { has_polylines = false; } - if (has_points && point_layer_action == S2GeographyOptions::OUTPUT_ACTION_ERROR) { + if (has_points && point_layer_action == GlobalOptions::OUTPUT_ACTION_ERROR) { throw Exception("Output contained unexpected points"); - } else if (has_points && point_layer_action == S2GeographyOptions::OUTPUT_ACTION_IGNORE) { + } else if (has_points && point_layer_action == GlobalOptions::OUTPUT_ACTION_IGNORE) { has_points = false; } @@ -83,7 +83,7 @@ std::unique_ptr s2_geography_from_layers(std::vector point std::unique_ptr s2_boolean_operation(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, S2BooleanOperation::OpType op_type, - const S2GeographyOptions& options) { + const GlobalOptions& options) { // Create the data structures that will contain the output. std::vector points; @@ -122,7 +122,7 @@ std::unique_ptr s2_boolean_operation(const ShapeIndexGeography& geo } std::unique_ptr s2_unary_union(const PolygonGeography& geog, - const S2GeographyOptions& options) { + const GlobalOptions& options) { // A geography with invalid loops won't work with the S2BooleanOperation // we will use to accumulate (i.e., union) valid polygons, // so we need to rebuild each loop as its own polygon, @@ -181,7 +181,7 @@ std::unique_ptr s2_unary_union(const PolygonGeography& geog, } std::unique_ptr s2_unary_union(const ShapeIndexGeography& geog, - const S2GeographyOptions& options) { + const GlobalOptions& options) { // complex union only needed when a polygon is involved bool simple_union_ok = s2_is_empty(geog) || s2_dimension(geog) < 2; @@ -215,10 +215,10 @@ std::unique_ptr s2_unary_union(const ShapeIndexGeography& geog, } std::unique_ptr s2_rebuild(const S2Geography& geog, - const S2GeographyOptions& options, - S2GeographyOptions::OutputAction point_layer_action, - S2GeographyOptions::OutputAction polyline_layer_action, - S2GeographyOptions::OutputAction polygon_layer_action) { + const GlobalOptions& options, + GlobalOptions::OutputAction point_layer_action, + GlobalOptions::OutputAction polyline_layer_action, + GlobalOptions::OutputAction polygon_layer_action) { // create the builder S2Builder builder(options.builder); @@ -276,7 +276,7 @@ std::unique_ptr s2_rebuild(const S2Geography& geog, } std::unique_ptr s2_rebuild(const S2Geography& geog, - const S2GeographyOptions& options) { + const GlobalOptions& options) { return s2_rebuild( geog, options, @@ -288,10 +288,10 @@ std::unique_ptr s2_rebuild(const S2Geography& geog, std::unique_ptr s2_build_point(const S2Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, - S2GeographyOptions(), - S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE, - S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, - S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR); + GlobalOptions(), + GlobalOptions::OutputAction::OUTPUT_ACTION_INCLUDE, + GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR, + GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR); return std::unique_ptr( dynamic_cast(geog_out.release())); @@ -301,10 +301,10 @@ std::unique_ptr s2_build_point(const S2Geography& geog) { std::unique_ptr s2_build_polyline(const S2Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, - S2GeographyOptions(), - S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, - S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE, - S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR); + GlobalOptions(), + GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR, + GlobalOptions::OutputAction::OUTPUT_ACTION_INCLUDE, + GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR); return std::unique_ptr( dynamic_cast(geog_out.release())); @@ -314,21 +314,21 @@ std::unique_ptr s2_build_polyline(const S2Geography& geog) { std::unique_ptr s2_build_polygon(const S2Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, - S2GeographyOptions(), - S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, - S2GeographyOptions::OutputAction::OUTPUT_ACTION_ERROR, - S2GeographyOptions::OutputAction::OUTPUT_ACTION_INCLUDE); + GlobalOptions(), + GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR, + GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR, + GlobalOptions::OutputAction::OUTPUT_ACTION_INCLUDE); return std::unique_ptr( dynamic_cast(geog_out.release())); } -void S2RebuildAggregator::Add(const S2Geography& geog) { +void RebuildAggregator::Add(const S2Geography& geog) { index_.Add(geog); } -std::unique_ptr S2RebuildAggregator::Finalize() { +std::unique_ptr RebuildAggregator::Finalize() { return s2_rebuild(index_, options_); } @@ -364,7 +364,7 @@ void S2UnionAggregator::Add(const S2Geography& geog) { } } -std::unique_ptr S2UnionAggregator::Node::Merge(const S2GeographyOptions& options) { +std::unique_ptr S2UnionAggregator::Node::Merge(const GlobalOptions& options) { return s2_boolean_operation( index1, index2, diff --git a/src/s2-geography/build.hpp b/src/s2-geography/build.hpp index 60ed4661..fde82254 100644 --- a/src/s2-geography/build.hpp +++ b/src/s2-geography/build.hpp @@ -10,7 +10,7 @@ namespace s2geography { -class S2GeographyOptions { +class GlobalOptions { public: enum OutputAction { OUTPUT_ACTION_INCLUDE, @@ -18,7 +18,7 @@ class S2GeographyOptions { OUTPUT_ACTION_ERROR }; - S2GeographyOptions() + GlobalOptions() : point_layer_action(OUTPUT_ACTION_INCLUDE), polyline_layer_action(OUTPUT_ACTION_INCLUDE), polygon_layer_action(OUTPUT_ACTION_INCLUDE) {} @@ -37,13 +37,13 @@ class S2GeographyOptions { std::unique_ptr s2_boolean_operation(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, S2BooleanOperation::OpType op_type, - const S2GeographyOptions& options); + const GlobalOptions& options); std::unique_ptr s2_unary_union(const ShapeIndexGeography& geog, - const S2GeographyOptions& options); + const GlobalOptions& options); std::unique_ptr s2_rebuild(const S2Geography& geog, - const S2GeographyOptions& options); + const GlobalOptions& options); std::unique_ptr s2_build_point(const S2Geography& geog); @@ -51,32 +51,32 @@ std::unique_ptr s2_build_polyline(const S2Geography& geog); std::unique_ptr s2_build_polygon(const S2Geography& geog); -class S2RebuildAggregator: public S2Aggregator> { +class RebuildAggregator: public Aggregator> { public: - S2RebuildAggregator(const S2GeographyOptions& options): options_(options) {} + RebuildAggregator(const GlobalOptions& options): options_(options) {} void Add(const S2Geography& geog); std::unique_ptr Finalize(); private: - S2GeographyOptions options_; + GlobalOptions options_; ShapeIndexGeography index_; }; -class S2CoverageUnionAggregator: public S2Aggregator> { +class S2CoverageUnionAggregator: public Aggregator> { public: - S2CoverageUnionAggregator(const S2GeographyOptions& options): options_(options) {} + S2CoverageUnionAggregator(const GlobalOptions& options): options_(options) {} void Add(const S2Geography& geog); std::unique_ptr Finalize(); private: - S2GeographyOptions options_; + GlobalOptions options_; ShapeIndexGeography index_; }; -class S2UnionAggregator: public S2Aggregator> { +class S2UnionAggregator: public Aggregator> { public: - S2UnionAggregator(const S2GeographyOptions& options): options_(options) {} + S2UnionAggregator(const GlobalOptions& options): options_(options) {} void Add(const S2Geography& geog); std::unique_ptr Finalize(); @@ -86,10 +86,10 @@ class S2UnionAggregator: public S2Aggregator> { ShapeIndexGeography index1; ShapeIndexGeography index2; std::vector> data; - std::unique_ptr Merge(const S2GeographyOptions& options); + std::unique_ptr Merge(const GlobalOptions& options); }; - S2GeographyOptions options_; + GlobalOptions options_; Node root_; std::vector> other_; }; diff --git a/src/s2-geography/linear-referencing.cpp b/src/s2-geography/linear-referencing.cpp index 25afd220..09f70bac 100644 --- a/src/s2-geography/linear-referencing.cpp +++ b/src/s2-geography/linear-referencing.cpp @@ -39,7 +39,7 @@ double s2_project_normalized(const S2Geography& geog1, const S2Geography& geog2) return s2_project_normalized(*geog1_poly_ptr, point); } - std::unique_ptr geog_poly = s2_rebuild(geog1, S2GeographyOptions()); + std::unique_ptr geog_poly = s2_rebuild(geog1, GlobalOptions()); return s2_project_normalized(*geog_poly, geog2); } @@ -67,7 +67,7 @@ S2Point s2_interpolate_normalized(const S2Geography& geog, double distance_norm) return s2_interpolate_normalized(*geog_poly_ptr, distance_norm); } - std::unique_ptr geog_poly = s2_rebuild(geog, S2GeographyOptions()); + std::unique_ptr geog_poly = s2_rebuild(geog, GlobalOptions()); return s2_interpolate_normalized(*geog_poly, distance_norm); } diff --git a/src/s2-options.h b/src/s2-options.h index abd282a6..86ec1c18 100644 --- a/src/s2-options.h +++ b/src/s2-options.h @@ -196,9 +196,9 @@ class GeographyOperationOptions { return options; } - // options for new S2GeographyOptions API - s2geography::S2GeographyOptions geographyOptions() { - s2geography::S2GeographyOptions options; + // options for new GlobalOptions API + s2geography::GlobalOptions geographyOptions() { + s2geography::GlobalOptions options; options.boolean_operation = booleanOperationOptions(); options.builder = builderOptions(); @@ -208,15 +208,15 @@ class GeographyOperationOptions { options.polygon_layer = layer_options.polygonLayerOptions; if (!(layer_options.dimensions & Dimension::POINT)) { - options.point_layer_action = s2geography::S2GeographyOptions::OUTPUT_ACTION_IGNORE; + options.point_layer_action = s2geography::GlobalOptions::OUTPUT_ACTION_IGNORE; } if (!(layer_options.dimensions & Dimension::POLYLINE)) { - options.polyline_layer_action = s2geography::S2GeographyOptions::OUTPUT_ACTION_IGNORE; + options.polyline_layer_action = s2geography::GlobalOptions::OUTPUT_ACTION_IGNORE; } if (!(layer_options.dimensions & Dimension::POLYGON)) { - options.polygon_layer_action = s2geography::S2GeographyOptions::OUTPUT_ACTION_IGNORE; + options.polygon_layer_action = s2geography::GlobalOptions::OUTPUT_ACTION_IGNORE; } return options; diff --git a/src/s2-transformers.cpp b/src/s2-transformers.cpp index cedea796..408d0b87 100644 --- a/src/s2-transformers.cpp +++ b/src/s2-transformers.cpp @@ -28,7 +28,7 @@ class BooleanOperationOp: public BinaryGeographyOperator { private: S2BooleanOperation::OpType opType; - s2geography::S2GeographyOptions geography_options; + s2geography::GlobalOptions geography_options; }; // [[Rcpp::export]] @@ -101,7 +101,7 @@ List cpp_s2_union_agg(List geog, List s2options, bool naRm) { // [[Rcpp::export]] List cpp_s2_centroid_agg(List geog, bool naRm) { - s2geography::S2CentroidAggregator agg; + s2geography::CentroidAggregator agg; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { @@ -132,7 +132,7 @@ List cpp_s2_centroid_agg(List geog, bool naRm) { List cpp_s2_rebuild_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); - s2geography::S2RebuildAggregator agg(options.geographyOptions()); + s2geography::RebuildAggregator agg(options.geographyOptions()); std::vector> geographies; SEXP item; @@ -274,7 +274,7 @@ List cpp_s2_rebuild(List geog, List s2options) { } private: - s2geography::S2GeographyOptions options; + s2geography::GlobalOptions options; }; Op op(s2options); @@ -299,7 +299,7 @@ List cpp_s2_unary_union(List geog, List s2options) { private: S2BooleanOperation::Options options; GeographyOperationOptions::LayerOptions layerOptions; - s2geography::S2GeographyOptions geographyOptions; + s2geography::GlobalOptions geographyOptions; }; Op op(s2options); From 7c8e602564d3222b88001f297ade1df84bcb6b59 Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 21 Apr 2022 14:47:11 -0300 Subject: [PATCH 87/88] rename index --- src/s2-geography/index.hpp | 10 +++++----- src/s2-matrix.cpp | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/s2-geography/index.hpp b/src/s2-geography/index.hpp index 92aefb2c..6715d8d0 100644 --- a/src/s2-geography/index.hpp +++ b/src/s2-geography/index.hpp @@ -9,12 +9,12 @@ namespace s2geography { // Unlike the ShapeIndexGeography, whose function is to index a single S2Geography // or index multiple S2Geography objects as if they were a single S2Geography, -// the S2GeographyIndex exists to index a vector of S2Geography objects (like a +// the GeographyIndex exists to index a vector of S2Geography objects (like a // GEOSSTRTree index), providing (hopefully) rapid access to possibly intersecting // features. -class S2GeographyIndex { +class GeographyIndex { public: - S2GeographyIndex(MutableS2ShapeIndex::Options options = MutableS2ShapeIndex::Options()) + GeographyIndex(MutableS2ShapeIndex::Options options = MutableS2ShapeIndex::Options()) : index_(options) {} void Add(const S2Geography& geog, int value) { @@ -40,7 +40,7 @@ class S2GeographyIndex { class Iterator { public: - Iterator(const S2GeographyIndex* index): + Iterator(const GeographyIndex* index): index_(index), iterator_(&index_->ShapeIndex()) {} void Query(const std::vector& covering, std::unordered_set* indices) { @@ -84,7 +84,7 @@ class S2GeographyIndex { } private: - const S2GeographyIndex* index_; + const GeographyIndex* index_; MutableS2ShapeIndex::Iterator iterator_; }; diff --git a/src/s2-matrix.cpp b/src/s2-matrix.cpp index 82919e95..84c13dad 100644 --- a/src/s2-matrix.cpp +++ b/src/s2-matrix.cpp @@ -18,8 +18,8 @@ using namespace Rcpp; template class IndexedBinaryGeographyOperator: public UnaryGeographyOperator { public: - std::unique_ptr geog2_index; - std::unique_ptr iterator; + std::unique_ptr geog2_index; + std::unique_ptr iterator; // max_edges_per_cell should be between 10 and 50, with lower numbers // leading to more memory usage (but potentially faster query times). Benchmarking @@ -30,7 +30,7 @@ class IndexedBinaryGeographyOperator: public UnaryGeographyOperator(index_options); + geog2_index = absl::make_unique(index_options); } virtual void buildIndex(List geog2) { @@ -48,7 +48,7 @@ class IndexedBinaryGeographyOperator: public UnaryGeographyOperator(geog2_index.get()); + iterator = absl::make_unique(geog2_index.get()); } }; From 3d33c13aface6cc34b49521427fe88ccaf575d6e Mon Sep 17 00:00:00 2001 From: Dewey Dunnington Date: Thu, 21 Apr 2022 16:18:49 -0300 Subject: [PATCH 88/88] stop pretending that there will be a C API --- src/Makevars.in | 3 +- src/Makevars.win | 2 -- src/s2-geography/capi/status.cpp | 30 ------------------- src/{s2-geography/capi => }/tessellator.cpp | 0 .../s2-geography_c.h => tessellator.h} | 0 src/wk-c-utils.c | 2 +- 6 files changed, 2 insertions(+), 35 deletions(-) delete mode 100644 src/s2-geography/capi/status.cpp rename src/{s2-geography/capi => }/tessellator.cpp (100%) rename src/{s2-geography/s2-geography_c.h => tessellator.h} (100%) diff --git a/src/Makevars.in b/src/Makevars.in index 1f95a511..672304cb 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -103,8 +103,7 @@ OBJECTS = $(ABSL_LIBS) \ s2-xptr.o \ wk-impl.o \ wk-c-utils.o \ - s2-geography/capi/tessellator.o \ - s2-geography/capi/status.o \ + tessellator.o \ s2-geography/accessors.o \ s2-geography/accessors-geog.o \ s2-geography/linear-referencing.o \ diff --git a/src/Makevars.win b/src/Makevars.win index 0ca43b61..15a8af20 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -88,8 +88,6 @@ ABSL_LIBS = absl/base/internal/cycleclock.o \ absl/types/bad_variant_access.o S2LIBS = $(ABSL_LIBS) \ - s2-geography/capi/tessellator.o \ - s2-geography/capi/status.o \ s2-geography/linear-referencing.o \ s2-geography/distance.o \ s2-geography/accessors.o \ diff --git a/src/s2-geography/capi/status.cpp b/src/s2-geography/capi/status.cpp deleted file mode 100644 index 1d47a9bc..00000000 --- a/src/s2-geography/capi/status.cpp +++ /dev/null @@ -1,30 +0,0 @@ - -#include -#include -#include - - -// capi typedef start -typedef struct { - int code; - char message[8096]; - void* private_data; -} s2_status_t; -// capi typedef end - - -void s2_status_reset(s2_status_t* status) { - memset(status->message, 0, sizeof(status->message)); - status->code = 0; -} - - -void s2_status_set_error(s2_status_t* status, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - int chars_written = vsnprintf(status->message, sizeof(status->message) - 1, fmt, args); - va_end(args); - status->message[chars_written] = '\0'; -} - - diff --git a/src/s2-geography/capi/tessellator.cpp b/src/tessellator.cpp similarity index 100% rename from src/s2-geography/capi/tessellator.cpp rename to src/tessellator.cpp diff --git a/src/s2-geography/s2-geography_c.h b/src/tessellator.h similarity index 100% rename from src/s2-geography/s2-geography_c.h rename to src/tessellator.h diff --git a/src/wk-c-utils.c b/src/wk-c-utils.c index d37b915c..8e6f27ec 100644 --- a/src/wk-c-utils.c +++ b/src/wk-c-utils.c @@ -3,7 +3,7 @@ #include #include #include "wk-v1.h" -#include "s2-geography/s2-geography_c.h" +#include "tessellator.h" // Expose projections as external pointers so that they can theoretically be generated // by other packages.