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