1 
2 #ifndef WK_GEOGRAPHY_H
3 #define WK_GEOGRAPHY_H
4 
5 #include <vector>
6 
7 #include "wk/rcpp-io.hpp"
8 #include "wk/reader.hpp"
9 #include "wk/geometry-handler.hpp"
10 
11 #include "point-geography.h"
12 #include "polyline-geography.h"
13 #include "polygon-geography.h"
14 #include "geography-collection.h"
15 
16 #include <Rcpp.h>
17 #include "geography.h"
18 
19 #define CODE_HAS_BUILD_ERROR 3938829
20 
21 
22 class WKGeographyWriter: public WKGeometryHandler {
23 public:
24   Rcpp::List output;
25   R_xlen_t featureId;
26 
27   Rcpp::IntegerVector problemId;
28   Rcpp::CharacterVector problems;
29 
WKGeographyWriter(R_xlen_t size)30   WKGeographyWriter(R_xlen_t size):
31     output(size),
32     builder(nullptr),
33     oriented(false),
34     check(true) {}
35 
setOriented(bool oriented)36   void setOriented(bool oriented) {
37     this->oriented = oriented;
38   }
39 
setCheck(bool check)40   void setCheck(bool check) {
41     this->check = check;
42   }
43 
nextFeatureStart(size_t featureId)44   void nextFeatureStart(size_t featureId) {
45     this->builder = std::unique_ptr<GeographyBuilder>(nullptr);
46     this->featureId = featureId;
47   }
48 
nextNull(size_t featureId)49   void nextNull(size_t featureId) {
50     this->output[featureId] = R_NilValue;
51   }
52 
nextGeometryStart(const WKGeometryMeta & meta,uint32_t partId)53   void nextGeometryStart(const WKGeometryMeta& meta, uint32_t partId) {
54     if (!this->builder) {
55       switch (meta.geometryType) {
56       case WKGeometryType::Point:
57       case WKGeometryType::MultiPoint:
58         this->builder = absl::make_unique<PointGeography::Builder>();
59         break;
60       case WKGeometryType::LineString:
61       case WKGeometryType::MultiLineString:
62         this->builder = absl::make_unique<PolylineGeography::Builder>();
63         break;
64       case WKGeometryType::Polygon:
65       case WKGeometryType::MultiPolygon:
66         this->builder = absl::make_unique<PolygonGeography::Builder>(
67           this->oriented,
68           this->check
69         );
70         break;
71       case WKGeometryType::GeometryCollection:
72         this->builder = absl::make_unique<GeographyCollection::Builder>(
73           this->oriented,
74           this->check
75         );
76         break;
77       default:
78         std::stringstream err;
79         err << "Unknown geometry type in geography builder: " << meta.geometryType;
80         this->addProblem(err.str());
81         throw WKParseException(CODE_HAS_BUILD_ERROR);
82       }
83     }
84 
85     this->builder->nextGeometryStart(meta, partId);
86   }
87 
nextLinearRingStart(const WKGeometryMeta & meta,uint32_t size,uint32_t ringId)88   void nextLinearRingStart(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) {
89     this->builder->nextLinearRingStart(meta, size, ringId);
90   }
91 
nextCoordinate(const WKGeometryMeta & meta,const WKCoord & coord,uint32_t coordId)92   void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) {
93     this->builder->nextCoordinate(meta, coord, coordId);
94   }
95 
nextLinearRingEnd(const WKGeometryMeta & meta,uint32_t size,uint32_t ringId)96   void nextLinearRingEnd(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) {
97     try {
98       this->builder->nextLinearRingEnd(meta, size, ringId);
99     } catch (WKParseException& e) {
100       this->addProblem(e.what());
101       throw WKParseException(CODE_HAS_BUILD_ERROR);
102     }
103   }
104 
nextGeometryEnd(const WKGeometryMeta & meta,uint32_t partId)105   void nextGeometryEnd(const WKGeometryMeta& meta, uint32_t partId) {
106     this->builder->nextGeometryEnd(meta, partId);
107   }
108 
nextFeatureEnd(size_t featureId)109   void nextFeatureEnd(size_t featureId) {
110     if (this->builder) {
111       try {
112         std::unique_ptr<Geography> feature = builder->build();
113         this->output[featureId] = Rcpp::XPtr<Geography>(feature.release());
114       } catch (WKParseException& e) {
115         this->addProblem(e.what());
116         throw WKParseException(CODE_HAS_BUILD_ERROR);
117       }
118     }
119   }
120 
nextError(WKParseException & error,size_t featureId)121   bool nextError(WKParseException& error, size_t featureId) {
122     if (error.code() == CODE_HAS_BUILD_ERROR) {
123       this->output[featureId] = R_NilValue;
124       return true;
125     } else {
126       return false;
127     }
128 
129     this->nextFeatureEnd(featureId);
130     return true;
131   }
132 
133 private:
134   std::unique_ptr<GeographyBuilder> builder;
135   bool oriented;
136   bool check;
137 
addProblem(std::string what)138   void addProblem(std::string what) {
139     problemId.push_back(this->featureId);
140     problems.push_back(what);
141   }
142 };
143 
144 
145 class WKGeographyReader: public WKReader {
146 public:
147 
WKGeographyReader(WKRcppSEXPProvider & provider)148   WKGeographyReader(WKRcppSEXPProvider& provider):
149   WKReader(provider), provider(provider) {}
150 
readFeature(size_t featureId)151   void readFeature(size_t featureId) {
152     this->handler->nextFeatureStart(featureId);
153 
154     if (this->provider.featureIsNull()) {
155       this->handler->nextNull(featureId);
156     } else {
157       Rcpp::XPtr<Geography> geography(this->provider.feature());
158       geography->Export(handler, WKReader::PART_ID_NONE);
159     }
160 
161     this->handler->nextFeatureEnd(featureId);
162   }
163 
164 private:
165   WKRcppSEXPProvider& provider;
166 };
167 
168 #endif
169