1 
2 #ifndef S2_OPTIONS_H
3 #define S2_OPTIONS_H
4 
5 #include <sstream>
6 #include <Rcpp.h>
7 #include "s2/s2boolean_operation.h"
8 #include "s2/s2builderutil_snap_functions.h"
9 #include "s2/s2builderutil_s2polygon_layer.h"
10 #include "s2/s2builderutil_s2polyline_vector_layer.h"
11 #include "s2/s2builderutil_s2point_vector_layer.h"
12 
13 // This class wraps several concepts in the S2BooleanOperation,
14 // and S2Layer, parameterized such that these can be specified from R
15 class GeographyOperationOptions {
16 public:
17   int polygonModel;
18   int polylineModel;
19   Rcpp::List snap;
20   double snapRadius;
21   int duplicatePointEdges;
22   int duplicatePolylineEdges;
23   int duplicatePolygonEdges;
24   int polylineEdgeType;
25   int polygonEdgeType;
26   int validatePolyline;
27   int validatePolygon;
28   int polylineType;
29   int polylineSiblingPairs;
30   int simplifyEdgeChains;
31   int splitCrossingEdges;
32   int idempotent;
33   int dimensions;
34 
35   enum Dimension {
36     POINT = 1,
37     POLYLINE = 2,
38     POLYGON = 4
39   };
40 
41   // Wraps options for the three layer types
42   class LayerOptions {
43     public:
44     s2builderutil::S2PointVectorLayer::Options pointLayerOptions;
45     s2builderutil::S2PolylineVectorLayer::Options polylineLayerOptions;
46     s2builderutil::S2PolygonLayer::Options polygonLayerOptions;
47     int dimensions;
48   };
49 
50   // deaults: use S2 defaults
GeographyOperationOptions()51   GeographyOperationOptions(): polygonModel(-1), polylineModel(-1), snapRadius(-1) {
52     this->snap.attr("class") = "snap_identity";
53   }
54 
55   // create from s2_options() object
GeographyOperationOptions(Rcpp::List s2options)56   GeographyOperationOptions(Rcpp::List s2options): GeographyOperationOptions() {
57     if (!Rf_inherits(s2options, "s2_options")) {
58       Rcpp::stop("`options` must be created using s2_options()");
59     }
60 
61     // if these items are of an incorrect type (e.g., list() instead of int)
62     // the default errors are very difficult to diagnose.
63     try {
64       int model = s2options["model"];
65       this->polylineModel = model;
66       this->polygonModel = model;
67     } catch (std::exception& e) {
68       std::stringstream err;
69       err << "Error setting s2_options() `model`: " << e.what();
70       Rcpp::stop(err.str());
71     }
72 
73     try {
74       this->snap = s2options["snap"];
75     } catch (std::exception& e) {
76       std::stringstream err;
77       err << "Error setting s2_options() `snap`: " << e.what();
78       Rcpp::stop(err.str());
79     }
80 
81     try {
82       this->snapRadius = s2options["snap_radius"];
83     } catch (std::exception& e) {
84       std::stringstream err;
85       err << "Error setting s2_options() `snap_radius`: " << e.what();
86       Rcpp::stop(err.str());
87     }
88 
89     try {
90       int duplicateEdges = s2options["duplicate_edges"];
91       this->duplicatePointEdges = duplicateEdges;
92       this->duplicatePolylineEdges = duplicateEdges;
93       this->duplicatePolygonEdges = duplicateEdges;
94     } catch (std::exception& e) {
95       std::stringstream err;
96       err << "Error setting s2_options() `duplicate_edges`: " << e.what();
97       Rcpp::stop(err.str());
98     }
99 
100     try {
101       int edgeType = s2options["edge_type"];
102       this->polylineEdgeType = edgeType;
103       this->polygonEdgeType = edgeType;
104     } catch (std::exception& e) {
105       std::stringstream err;
106       err << "Error setting s2_options() `edge_type`: " << e.what();
107       Rcpp::stop(err.str());
108     }
109 
110     try {
111       int validate = s2options["validate"];
112       this->validatePolyline = validate;
113       this->validatePolygon = validate;
114     } catch (std::exception& e) {
115       std::stringstream err;
116       err << "Error setting s2_options() `duplicate_edges`: " << e.what();
117       Rcpp::stop(err.str());
118     }
119 
120     try {
121       this->polylineType = s2options["polyline_type"];
122     } catch (std::exception& e) {
123       std::stringstream err;
124       err << "Error setting s2_options() `polyline_type`: " << e.what();
125       Rcpp::stop(err.str());
126     }
127 
128     try {
129       this->polylineSiblingPairs = s2options["polyline_sibling_pairs"];
130     } catch (std::exception& e) {
131       std::stringstream err;
132       err << "Error setting s2_options() `polyline_sibling_pairs`: " << e.what();
133       Rcpp::stop(err.str());
134     }
135 
136     try {
137       this->simplifyEdgeChains = s2options["simplify_edge_chains"];
138     } catch (std::exception& e) {
139       std::stringstream err;
140       err << "Error setting s2_options() `simplify_edge_chains`: " << e.what();
141       Rcpp::stop(err.str());
142     }
143 
144     try {
145       this->splitCrossingEdges = s2options["split_crossing_edges"];
146     } catch (std::exception& e) {
147       std::stringstream err;
148       err << "Error setting s2_options() `split_crossing_edges`: " << e.what();
149       Rcpp::stop(err.str());
150     }
151 
152     try {
153       this->idempotent = s2options["idempotent"];
154     } catch (std::exception& e) {
155       std::stringstream err;
156       err << "Error setting s2_options() `idempotent`: " << e.what();
157       Rcpp::stop(err.str());
158     }
159 
160     try {
161       this->dimensions = 0;
162       Rcpp::IntegerVector dim = s2options["dimensions"];
163       for (int i = 0; i < dim.size(); i++) {
164         switch (dim[i]) {
165         case 1:
166           this->dimensions |= Dimension::POINT;
167           break;
168         case 2:
169           this->dimensions |= Dimension::POLYLINE;
170           break;
171         case 3:
172           this->dimensions |= Dimension::POLYGON;
173           break;
174         }
175       }
176     } catch (std::exception& e) {
177       std::stringstream err;
178       err << "Error setting s2_options() `dimensions`: " << e.what();
179       Rcpp::stop(err.str());
180     }
181   }
182 
183   // build options for passing this to the S2BooleanOperation
booleanOperationOptions()184   S2BooleanOperation::Options booleanOperationOptions() {
185     S2BooleanOperation::Options options;
186     if (this->polygonModel >= 0) {
187       options.set_polygon_model(getPolygonModel(this->polygonModel));
188     }
189     if (this->polylineModel >= 0) {
190       options.set_polyline_model(getPolylineModel(this->polylineModel));
191     }
192     this->setSnapFunction<S2BooleanOperation::Options>(options);
193 
194     return options;
195   }
196 
197   // build options for S2Builder
builderOptions()198   S2Builder::Options builderOptions() {
199     S2Builder::Options options;
200     options.set_simplify_edge_chains(this->simplifyEdgeChains);
201     options.set_split_crossing_edges(this->splitCrossingEdges);
202     options.set_idempotent(this->idempotent);
203     this->setSnapFunction<S2Builder::Options>(options);
204     return options;
205   }
206 
207   // build options for point, polyline, and polygon layers
layerOptions()208   LayerOptions layerOptions() {
209     LayerOptions out;
210 
211     // point layer
212     out.pointLayerOptions.set_duplicate_edges(getDuplicateEdges(this->duplicatePointEdges));
213 
214     // polyline layer
215     out.polylineLayerOptions.set_duplicate_edges(getDuplicateEdges(this->duplicatePolylineEdges));
216     out.polylineLayerOptions.set_edge_type(getEdgeType(this->polylineEdgeType));
217     out.polylineLayerOptions.set_polyline_type(getPolylineType(this->polylineType));
218     out.polylineLayerOptions.set_sibling_pairs(getSiblingPairs(this->polylineSiblingPairs));
219     out.polylineLayerOptions.set_validate(this->validatePolyline);
220 
221     // always disable debugging where possible
222     out.polylineLayerOptions.set_s2debug_override(S2Debug::DISABLE);
223 
224     // polygon layer
225     out.polygonLayerOptions.set_edge_type(getEdgeType(this->polygonEdgeType));
226     out.polygonLayerOptions.set_validate(this->validatePolygon);
227 
228     // dimensions
229     out.dimensions = this->dimensions;
230 
231     return out;
232   }
233 
234   template <class OptionsType>
setSnapFunction(OptionsType & options)235   void setSnapFunction(OptionsType& options) {
236     // S2Builder::SnapFunction is abstract and can't be returned
237     // hence the templated function
238 
239     if (Rf_inherits(this->snap, "snap_identity")) {
240       s2builderutil::IdentitySnapFunction snapFunction;
241       if (this->snapRadius > 0) {
242         snapFunction.set_snap_radius(S1Angle::Radians(this->snapRadius));
243       }
244       options.set_snap_function(snapFunction);
245 
246     } else if (Rf_inherits(this->snap, "snap_level")) {
247       int snapLevel = this->snap["level"];
248       s2builderutil::S2CellIdSnapFunction snapFunction(snapLevel);
249       if (this->snapRadius > 0) {
250         snapFunction.set_snap_radius(S1Angle::Radians(this->snapRadius));
251       }
252       options.set_snap_function(snapFunction);
253 
254     } else if (Rf_inherits(this->snap, "snap_precision")) {
255       int exponent = snap["exponent"];
256       s2builderutil::IntLatLngSnapFunction snapFunction(exponent);
257       if (this->snapRadius > 0) {
258         snapFunction.set_snap_radius(S1Angle::Radians(this->snapRadius));
259       }
260       options.set_snap_function(snapFunction);
261 
262     } else if (Rf_inherits(this->snap, "snap_distance")) {
263       double distance = snap["distance"];
264       double snapLevel = s2builderutil::S2CellIdSnapFunction::LevelForMaxSnapRadius(
265         S1Angle::Radians(distance)
266       );
267       s2builderutil::S2CellIdSnapFunction snapFunction(snapLevel);
268       if (this->snapRadius > 0) {
269         snapFunction.set_snap_radius(S1Angle::Radians(this->snapRadius));
270       }
271       options.set_snap_function(snapFunction);
272 
273     } else {
274       Rcpp::stop("`snap` must be specified using s2_snap_*()");
275     }
276   }
277 
getPolygonModel(int model)278   static S2BooleanOperation::PolygonModel getPolygonModel(int model) {
279     switch (model) {
280       case 1: return S2BooleanOperation::PolygonModel::OPEN;
281       case 2: return S2BooleanOperation::PolygonModel::SEMI_OPEN;
282       case 3: return S2BooleanOperation::PolygonModel::CLOSED;
283       default:
284         std::stringstream err;
285         err << "Invalid value for polygon model: " << model;
286         Rcpp::stop(err.str());
287     }
288   }
289 
getPolylineModel(int model)290   static S2BooleanOperation::PolylineModel getPolylineModel(int model) {
291     switch (model) {
292       case 1: return S2BooleanOperation::PolylineModel::OPEN;
293       case 2: return S2BooleanOperation::PolylineModel::SEMI_OPEN;
294       case 3: return S2BooleanOperation::PolylineModel::CLOSED;
295       default:
296         std::stringstream err;
297         err << "Invalid value for polyline model: " << model;
298         Rcpp::stop(err.str());
299     }
300   }
301 
getDuplicateEdges(int value)302   static S2Builder::GraphOptions::DuplicateEdges getDuplicateEdges(int value) {
303     switch (value) {
304       case 0: return S2Builder::GraphOptions::DuplicateEdges::MERGE;
305       case 1: return S2Builder::GraphOptions::DuplicateEdges::KEEP;
306       default:
307         std::stringstream err;
308         err << "Invalid value for duplicate edges: " << value;
309         Rcpp::stop(err.str());
310     }
311   }
312 
getEdgeType(int value)313   static S2Builder::GraphOptions::EdgeType getEdgeType(int value) {
314     switch (value) {
315       case 1: return S2Builder::GraphOptions::EdgeType::DIRECTED;
316       case 2: return S2Builder::GraphOptions::EdgeType::UNDIRECTED;
317       default:
318         std::stringstream err;
319         err << "Invalid value for edge type: " << value;
320         Rcpp::stop(err.str());
321     }
322   }
323 
getSiblingPairs(int value)324   static S2Builder::GraphOptions::SiblingPairs getSiblingPairs(int value) {
325     switch (value) {
326       case 1: return S2Builder::GraphOptions::SiblingPairs::DISCARD;
327       case 2: return S2Builder::GraphOptions::SiblingPairs::KEEP;
328       default:
329         std::stringstream err;
330         err << "Invalid value for sibling pairs: " << value;
331         Rcpp::stop(err.str());
332     }
333   }
334 
getPolylineType(int value)335   static S2Builder::Graph::PolylineType getPolylineType(int value) {
336     switch (value) {
337       case 1: return S2Builder::Graph::PolylineType::PATH;
338       case 2: return S2Builder::Graph::PolylineType::WALK;
339       default:
340         std::stringstream err;
341         err << "Invalid value for polylie type: " << value;
342         Rcpp::stop(err.str());
343     }
344   }
345 };
346 
347 #endif
348