OmniSciDB  a667adc9c8
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Types.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 MapD Technologies, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "Geospatial/Types.h"
18 
19 #include <limits>
20 #include <mutex>
21 
22 #include <gdal.h>
23 #include <ogr_geometry.h>
24 #include <ogrsf_frmts.h>
25 
26 #include "Geospatial/GDAL.h"
27 #include "Logger/Logger.h"
28 #include "Shared/sqltypes.h"
29 
39 namespace {
40 constexpr auto DOUBLE_MAX = std::numeric_limits<double>::max();
41 constexpr auto DOUBLE_MIN = std::numeric_limits<double>::lowest();
42 
43 struct Coords {
44  double x;
45  double y;
46  Coords(double x, double y) : x(x), y(y) {}
47 };
48 
49 struct BoundingBox {
52  BoundingBox() : min(DOUBLE_MAX, DOUBLE_MAX), max(DOUBLE_MIN, DOUBLE_MIN) {}
53 
54  void update(double x, double y) {
55  if (x < min.x) {
56  min.x = x;
57  }
58  if (y < min.y) {
59  min.y = y;
60  }
61  if (x > max.x) {
62  max.x = x;
63  }
64  if (y > max.y) {
65  max.y = y;
66  }
67  }
68 };
69 
70 int process_poly_ring(OGRLinearRing* ring,
71  std::vector<double>& coords,
72  BoundingBox* bbox) {
73  double last_x = DOUBLE_MAX, last_y = DOUBLE_MAX;
74  size_t first_index = coords.size();
75  int num_points_added = 0;
76  int num_points_in_ring = ring->getNumPoints();
77  if (num_points_in_ring < 3) {
79  "PolyRing",
80  "All poly rings must have more than 3 points. Found ring with " +
81  std::to_string(num_points_in_ring) + " points.");
82  }
83  for (auto i = 0; i < num_points_in_ring; i++) {
84  OGRPoint point;
85  ring->getPoint(i, &point);
86  last_x = point.getX();
87  last_y = point.getY();
88  coords.push_back(last_x);
89  coords.push_back(last_y);
90  if (bbox) {
91  bbox->update(last_x, last_y);
92  }
93  num_points_added++;
94  }
95  // Store all rings as open rings (implicitly assumes all rings are closed)
96  if ((coords[first_index] == last_x) && (coords[first_index + 1] == last_y)) {
97  coords.pop_back();
98  coords.pop_back();
99  num_points_added--;
100  if (num_points_added < 3) {
102  "PolyRing",
103  "All exterior rings must have more than 3 points. Found ring with " +
104  std::to_string(num_points_added) + " points.");
105  }
106  }
107  return num_points_added;
108 }
109 
110 } // namespace
111 
112 namespace Geospatial {
113 
115 std::map<std::tuple<int32_t, int32_t>, std::shared_ptr<OGRCoordinateTransformation>>
117 
118 std::string GeoTypesError::OGRErrorToStr(const int ogr_err) {
119  switch (ogr_err) {
120  case OGRERR_NOT_ENOUGH_DATA:
121  return std::string("not enough input data");
122  case OGRERR_NOT_ENOUGH_MEMORY:
123  return std::string("not enough memory");
124  case OGRERR_UNSUPPORTED_GEOMETRY_TYPE:
125  return std::string("unsupported geometry type");
126  case OGRERR_UNSUPPORTED_OPERATION:
127  return std::string("unsupported operation");
128  case OGRERR_CORRUPT_DATA:
129  return std::string("corrupt input data");
130  case OGRERR_FAILURE:
131  return std::string("ogr failure");
132  case OGRERR_UNSUPPORTED_SRS:
133  return std::string("unsupported spatial reference system");
134  case OGRERR_INVALID_HANDLE:
135  return std::string("invalid file handle");
136 #if (GDAL_VERSION_MAJOR > 1)
137  case OGRERR_NON_EXISTING_FEATURE:
138  return std::string("feature does not exist in input geometry");
139 #endif
140  default:
141  return std::string("Unknown OGOR error encountered: ") + std::to_string(ogr_err);
142  }
143 }
144 
146  // Note: Removing the geometry object that was pulled from an OGRFeature results in a
147  // segfault. If we are wrapping around a pre-existing OGRGeometry object, we let the
148  // caller manage the memory.
149  if (geom_ && owns_geom_obj_) {
150  OGRGeometryFactory::destroyGeometry(geom_);
151  }
152 }
153 
154 OGRErr GeoBase::createFromWktString(const std::string& wkt, OGRGeometry** geom) {
155 #if (GDAL_VERSION_MAJOR > 2) || (GDAL_VERSION_MAJOR == 2 && GDAL_VERSION_MINOR >= 3)
156  OGRErr ogr_status = OGRGeometryFactory::createFromWkt(wkt.c_str(), nullptr, geom);
157 #else
158  auto data = (char*)wkt.c_str();
159  OGRErr ogr_status = OGRGeometryFactory::createFromWkt(&data, NULL, geom);
160 #endif
161  return ogr_status;
162 }
163 
164 std::string GeoBase::getWktString() const {
165  char* wkt = nullptr;
166  geom_->exportToWkt(&wkt);
167  CHECK(wkt);
168  std::string wkt_str(wkt);
169  CPLFree(wkt);
170  return wkt_str;
171 }
172 
173 OGRErr GeoBase::createFromWkb(const std::vector<uint8_t>& wkb, OGRGeometry** geom) {
174 #if (GDAL_VERSION_MAJOR > 2) || (GDAL_VERSION_MAJOR == 2 && GDAL_VERSION_MINOR >= 3)
175  OGRErr ogr_status =
176  OGRGeometryFactory::createFromWkb(wkb.data(), nullptr, geom, wkb.size());
177  return ogr_status;
178 #else
179  CHECK(false);
180 #endif
181 }
182 
183 // GeoBase could also generate GEOS geometries through geom_->exportToGEOS(),
184 // conversion to WKB and subsequent load of WKB into GEOS could be eliminated.
185 
186 bool GeoBase::getWkb(std::vector<uint8_t>& wkb) const {
187  auto size = geom_->WkbSize();
188  if (size > 0) {
189  wkb.resize(size);
190  geom_->exportToWkb(wkbNDR, wkb.data());
191  return true;
192  }
193  return false;
194 }
195 
196 bool GeoBase::isEmpty() const {
197  return geom_ && geom_->IsEmpty();
198 }
199 
200 bool GeoBase::operator==(const GeoBase& other) const {
201  if (!this->geom_ || !other.geom_) {
202  return false;
203  }
204  return this->geom_->Equals(other.geom_);
205 }
206 
208 #define SRID_WORLD_MERCATOR 999000
209 
210 #define SRID_NORTH_UTM_START 999001
211 
212 #define SRID_NORTH_UTM_END 999060
213 
214 #define SRID_NORTH_LAMBERT 999061
215 
216 #define SRID_SOUTH_UTM_START 999101
217 
218 #define SRID_SOUTH_UTM_END 999160
219 
220 #define SRID_SOUTH_LAMBERT 999161
221 
222 #define SRID_LAEA_START 999163
223 
224 #define SRID_LAEA_END 999283
225 
226 int32_t GeoBase::getBestPlanarSRID() const {
227  if (!this->geom_) {
228  return 0;
229  }
230  double cx, cy, xwidth, ywidth;
231  OGREnvelope envelope;
232  geom_->getEnvelope(&envelope);
233  // Can't use GDAL's Centroid geom_->Centroid(OGRPoint*): requires GEOS
234  // Use center of the bounding box for now.
235  // TODO: hook up our own Centroid implementation
236  cx = (envelope.MaxX + envelope.MinX) / 2.0;
237  cy = (envelope.MaxY + envelope.MinY) / 2.0;
238  xwidth = envelope.MaxX - envelope.MinX;
239  ywidth = envelope.MaxY - envelope.MinY;
240 
241  // Arctic coords: Lambert Azimuthal Equal Area North
242  if (cy > 70.0 && ywidth < 45.0) {
243  return SRID_NORTH_LAMBERT;
244  }
245  // Antarctic coords: Lambert Azimuthal Equal Area South
246  if (cy < -70.0 && ywidth < 45.0) {
247  return SRID_SOUTH_LAMBERT;
248  }
249 
250  // Can geometry fit into a single UTM zone?
251  if (xwidth < 6.0) {
252  int zone = floor((cx + 180.0) / 6.0);
253  if (zone > 59) {
254  zone = 59;
255  }
256  // Below the equator: UTM South
257  // Above the equator: UTM North
258  if (cy < 0.0) {
259  return SRID_SOUTH_UTM_START + zone;
260  } else {
261  return SRID_NORTH_UTM_START + zone;
262  }
263  }
264 
265  // TODO: to be removed once we add custom LAEA zone transforms
266  // Can geometry still fit into 5 consecutive UTM zones?
267  // Then go for the mid-UTM zone, tolerating some limited distortion
268  // in the left and right corners. That's still better than Mercator.
269  if (xwidth < 30.0) {
270  int zone = floor((cx + 180.0) / 6.0);
271  if (zone > 59) {
272  zone = 59;
273  }
274  // Below the equator: UTM South
275  // Above the equator: UTM North
276  if (cy < 0.0) {
277  return SRID_SOUTH_UTM_START + zone;
278  } else {
279  return SRID_NORTH_UTM_START + zone;
280  }
281  }
282 
283  // Can geometry fit into a custom LAEA area 30 degrees high? Allow some overlap.
284  if (ywidth < 25.0) {
285  int xzone = -1;
286  int yzone = 3 + floor(cy / 30.0); // range of 0-5
287  if ((yzone == 2 || yzone == 3) && xwidth < 30.0) {
288  // Equatorial band, 12 zones, 30 degrees wide
289  xzone = 6 + floor(cx / 30.0);
290  } else if ((yzone == 1 || yzone == 4) && xwidth < 45.0) {
291  // Temperate band, 8 zones, 45 degrees wide
292  xzone = 4 + floor(cx / 45.0);
293  } else if ((yzone == 0 || yzone == 5) && xwidth < 90.0) {
294  // Arctic band, 4 zones, 90 degrees wide
295  xzone = 2 + floor(cx / 90.0);
296  }
297  // Found an appropriate xzone to fit in?
298  if (xzone != -1) {
299  return SRID_LAEA_START + 20 * yzone + xzone;
300  }
301  }
302 
303  // Fall-back to Mercator
304  return SRID_WORLD_MERCATOR;
305 }
306 
307 std::shared_ptr<OGRCoordinateTransformation> GeoBase::getTransformation(int32_t srid0,
308  int32_t srid1) {
309  std::lock_guard<std::mutex> guard(transformation_map_mutex_);
310  std::tuple<int32_t, int32_t> key{srid0, srid1};
311  auto it = transformation_map_.find(key);
312  if (it != transformation_map_.end()) {
313  return it->second;
314  }
315  auto setSpatialReference = [&](OGRSpatialReference* sr, int32_t srid) -> bool {
316  OGRErr status = OGRERR_NONE;
317  if (srid == 4326) {
318  status = sr->importFromEPSG(4326);
319  } else if (srid == SRID_NORTH_LAMBERT) {
320  // +proj=laea +lat_0=90 +lon_0=-40 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m
321  // +no_defs
322  status = sr->importFromEPSG(3574);
323  } else if (srid == SRID_SOUTH_LAMBERT) {
324  // +proj=laea +lat_0=-90 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m
325  // +no_defs
326  status = sr->importFromEPSG(3409);
327  } else if (SRID_SOUTH_UTM_START <= srid && srid <= SRID_SOUTH_UTM_END) {
328  // +proj=utm +zone=%d +south +ellps=WGS84 +datum=WGS84 +units=m +no_defs
329  int32_t zone = srid - SRID_SOUTH_UTM_START;
330  status = sr->importFromEPSG(32701 + zone);
331  } else if (SRID_NORTH_UTM_START <= srid && srid <= SRID_NORTH_UTM_END) {
332  // +proj=utm +zone=%d +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
333  int32_t zone = srid - SRID_NORTH_UTM_START;
334  status = sr->importFromEPSG(32601 + zone);
335  } else if (SRID_LAEA_START <= srid && srid <= SRID_LAEA_END) {
336  // TODO: add support and coordinate operations for custom Lambert zones,
337  // need to calculate lat/lon for the zone, SetCoordinateOperation in options.
338  // +proj=laea +ellps=WGS84 +datum=WGS84 +lat_0=%g +lon_0=%g +units=m +no_defs
339  // Go with Mercator for now
340  status = sr->importFromEPSG(3395);
341  } else if (srid == SRID_WORLD_MERCATOR) {
342  // +proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m
343  // +no_defs
344  status = sr->importFromEPSG(3395);
345  } else if (srid > 0) {
346  // Attempt to import from srid directly
347  status = sr->importFromEPSG(srid);
348  } else {
349  return false;
350  }
351 #if GDAL_VERSION_MAJOR >= 3
352  // GDAL 3.x (really Proj.4 6.x) now enforces lat, lon order
353  // this results in X and Y being transposed for angle-based
354  // coordinate systems. This restores the previous behavior.
355  if (status == OGRERR_NONE) {
356  sr->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
357  }
358 #endif
359  return (status == OGRERR_NONE);
360  };
361 
362  // lazy init GDAL
363  GDAL::init();
364 
365  OGRSpatialReference sr0;
366  if (!setSpatialReference(&sr0, srid0)) {
367  return nullptr;
368  }
369  OGRSpatialReference sr1;
370  if (!setSpatialReference(&sr1, srid1)) {
371  return nullptr;
372  }
373  // GDAL 3 allows specification of advanced transformations in
374  // OGRCoordinateTransformationOptions, including multi-step pipelines.
375  // GDAL 3 would be required to handle Lambert zone proj4 strings.
376  // Using a simple transform for now.
377  std::shared_ptr<OGRCoordinateTransformation> new_transformation;
378  new_transformation.reset(OGRCreateCoordinateTransformation(&sr0, &sr1));
379  transformation_map_[key] = new_transformation;
380  return new_transformation;
381 }
382 
383 bool GeoBase::transform(int32_t srid0, int32_t srid1) {
384  auto coordinate_transformation = getTransformation(srid0, srid1);
385  if (!coordinate_transformation) {
386  return false;
387  }
388  auto ogr_status = geom_->transform(coordinate_transformation.get());
389  return (ogr_status == OGRERR_NONE);
390 }
391 
393  auto srid1 = ti.get_output_srid();
394  if (srid1 == 4326) {
395  auto srid0 = ti.get_input_srid();
396  if (srid0 > 0 && srid0 != 4326) {
397  if (!transform(srid0, srid1)) {
398  return false;
399  }
400  }
401  }
402  return true;
403 }
404 
405 // Run a specific geo operation on this and other geometries
406 std::unique_ptr<GeoBase> GeoBase::run(GeoBase::GeoOp op, const GeoBase& other) const {
407  OGRGeometry* result = nullptr;
408  // Invalid geometries are derailing geo op performance,
409  // Checking validity before running an operation doesn't have lesser penalty.
410  // DOES NOT HELP: if (geom_->IsValid() && other.geom_->IsValid()) {
411  switch (op) {
413  result = geom_->Intersection(other.geom_);
414  break;
416  result = geom_->Difference(other.geom_);
417  break;
419  result = geom_->Union(other.geom_);
420  break;
421  default:
422  break;
423  }
424  // TODO: Need to handle empty/non-POLYGON result
425  if (!result || result->IsEmpty() ||
426  !(result->getGeometryType() == wkbPolygon ||
427  result->getGeometryType() == wkbMultiPolygon)) {
428  // throw GeoTypesError(std::string(OGRGeometryTypeToName(geom_->getGeometryType())),
429  // "Currenly don't support invalid or empty result");
430  // return GeoTypesFactory::createGeoType("POLYGON EMPTY");
431  // Not supporting EMPTY polygons, return a dot polygon
433  "MULTIPOLYGON(((0 0,0.0000001 0,0 0.0000001)))");
434  }
435  return GeoTypesFactory::createGeoType(result);
436 }
437 
438 // Run a specific geo operation on this and other geometries
439 std::unique_ptr<GeoBase> GeoBase::optimized_run(GeoBase::GeoOp op,
440  const GeoBase& other) const {
441  OGRGeometry* result = nullptr;
442  // Loop through polys combinations, check validity, do intersections
443  // where needed, return a union of all intersections
444  auto gc1 = geom_->toGeometryCollection();
445  auto gc2 = other.geom_->toGeometryCollection();
446  if (!gc1 || !gc2 || gc1->IsEmpty() || gc2->IsEmpty()) {
447  return nullptr;
448  }
449  for (int i1 = 0; i1 < gc1->getNumGeometries(); i1++) {
450  auto g1 = gc1->getGeometryRef(i1);
451  // Validity check is very slow
452  if (!g1 || g1->IsEmpty() /*|| !g1->IsValid()*/) {
453  continue;
454  }
455  OGREnvelope ge1;
456  g1->getEnvelope(&ge1);
457  for (int i2 = 0; i2 < gc2->getNumGeometries(); i2++) {
458  auto g2 = gc2->getGeometryRef(i2);
459  // Validity check is very slow
460  if (!g2 || g2->IsEmpty() /*|| !g2->IsValid()*/) {
461  continue;
462  }
463  // Check for bounding box overlap
464  OGREnvelope ge2;
465  g2->getEnvelope(&ge2);
466  if (!ge1.Intersects(ge2)) {
467  continue;
468  }
469  // Do intersection
470  auto intermediate_result = g1->Intersection(g2);
471  if (!intermediate_result || intermediate_result->IsEmpty()) {
472  continue;
473  }
474  if (!result) {
475  result = intermediate_result;
476  } else {
477  result = result->Union(intermediate_result);
478  }
479  }
480  }
481 
482  // TODO: Need to handle empty/non-POLYGON result
483  if (!result || result->IsEmpty() ||
484  !(result->getGeometryType() == wkbPolygon ||
485  result->getGeometryType() == wkbMultiPolygon)) {
486  // throw GeoTypesError(std::string(OGRGeometryTypeToName(geom_->getGeometryType())),
487  // "Currenly don't support invalid or empty result");
488  // return GeoTypesFactory::createGeoType("POLYGON EMPTY");
489  // Not supporting EMPTY polygons, return a dot polygon
491  "MULTIPOLYGON(((0 0,0.0000001 0,0 0.0000001)))");
492  }
493  return GeoTypesFactory::createGeoType(result);
494 }
495 
496 // Run a specific geo operation on this geometry and a double param
497 std::unique_ptr<GeoBase> GeoBase::run(GeoBase::GeoOp op, double param) const {
498  OGRGeometry* result = nullptr;
499  switch (op) {
501  result = geom_->Buffer(param);
502  break;
503  default:
504  break;
505  }
506  // TODO: Need to handle empty/non-POLYGON result
507  if (!result || result->IsEmpty() ||
508  !(result->getGeometryType() == wkbPolygon ||
509  result->getGeometryType() == wkbMultiPolygon)) {
510  // throw GeoTypesError(std::string(OGRGeometryTypeToName(geom_->getGeometryType())),
511  // "Currenly don't support invalid or empty result");
512  // return GeoTypesFactory::createGeoType("POLYGON EMPTY");
513  // Not supporting EMPTY polygons, return a dot polygon
515  "MULTIPOLYGON(((0 0,0.0000001 0,0 0.0000001)))");
516  }
517  return GeoTypesFactory::createGeoType(result);
518 }
519 
520 // Run a specific predicate operation on this geometry
521 bool GeoBase::run(GeoBase::GeoOp op) const {
522  auto result = false;
523  switch (op) {
525  result = geom_->IsValid();
526  break;
528  result = isEmpty();
529  break;
530  default:
531  break;
532  }
533  return result;
534 }
535 
536 GeoPoint::GeoPoint(const std::vector<double>& coords) {
537  if (coords.size() != 2) {
538  throw GeoTypesError("Point",
539  "Incorrect coord size of " + std::to_string(coords.size()) +
540  " supplied. Expected 2.");
541  }
542  geom_ = OGRGeometryFactory::createGeometry(OGRwkbGeometryType::wkbPoint);
543  OGRPoint* point = dynamic_cast<OGRPoint*>(geom_);
544  CHECK(point);
545  point->setX(coords[0]);
546  point->setY(coords[1]);
547 }
548 
549 GeoPoint::GeoPoint(const std::string& wkt) {
550  const auto err = GeoBase::createFromWktString(wkt, &geom_);
551  if (err != OGRERR_NONE) {
552  throw GeoTypesError("Point", err);
553  }
554  CHECK(geom_);
555  if (wkbFlatten(geom_->getGeometryType()) != OGRwkbGeometryType::wkbPoint) {
556  throw GeoTypesError("Point",
557  "Unexpected geometry type from WKT string: " +
558  std::string(OGRGeometryTypeToName(geom_->getGeometryType())));
559  }
560 }
561 
562 void GeoPoint::getColumns(std::vector<double>& coords) const {
563  const auto point_geom = dynamic_cast<OGRPoint*>(geom_);
564  CHECK(point_geom);
565 
566  if (point_geom->IsEmpty()) {
567  // until the run-time can handle empties
568  throw GeoTypesError("Point", "'EMPTY' not supported");
569  // we cannot yet handle NULL fixed-length array
570  // so we have to store sentinel values instead
571  coords.push_back(NULL_DOUBLE);
572  coords.push_back(NULL_DOUBLE);
573  return;
574  }
575 
576  coords.push_back(point_geom->getX());
577  coords.push_back(point_geom->getY());
578 }
579 
580 GeoLineString::GeoLineString(const std::vector<double>& coords) {
581  geom_ = OGRGeometryFactory::createGeometry(OGRwkbGeometryType::wkbLineString);
582  OGRLineString* line = dynamic_cast<OGRLineString*>(geom_);
583  CHECK(line);
584  for (size_t i = 0; i < coords.size(); i += 2) {
585  line->addPoint(coords[i], coords[i + 1]);
586  }
587 }
588 
589 GeoLineString::GeoLineString(const std::string& wkt) {
590  const auto err = GeoBase::createFromWktString(wkt, &geom_);
591  if (err != OGRERR_NONE) {
592  throw GeoTypesError("LineString", err);
593  }
594  CHECK(geom_);
595  if (wkbFlatten(geom_->getGeometryType()) != OGRwkbGeometryType::wkbLineString) {
596  throw GeoTypesError("LineString",
597  "Unexpected geometry type from WKT string: " +
598  std::string(OGRGeometryTypeToName(geom_->getGeometryType())));
599  }
600 }
601 
602 void GeoLineString::getColumns(std::vector<double>& coords,
603  std::vector<double>& bounds) const {
604  auto linestring_geom = dynamic_cast<OGRLineString*>(geom_);
605  CHECK(linestring_geom);
606 
607  if (linestring_geom->IsEmpty()) {
608  // until the run-time can handle empties
609  throw GeoTypesError("LineString", "'EMPTY' not supported");
610  // return null bounds
611  bounds.push_back(NULL_DOUBLE);
612  bounds.push_back(NULL_DOUBLE);
613  bounds.push_back(NULL_DOUBLE);
614  bounds.push_back(NULL_DOUBLE);
615  return;
616  }
617 
618  BoundingBox bbox;
619  for (auto i = 0; i < linestring_geom->getNumPoints(); i++) {
620  OGRPoint point;
621  linestring_geom->getPoint(i, &point);
622  double x = point.getX();
623  double y = point.getY();
624  coords.push_back(x);
625  coords.push_back(y);
626  bbox.update(x, y);
627  }
628  bounds.push_back(bbox.min.x);
629  bounds.push_back(bbox.min.y);
630  bounds.push_back(bbox.max.x);
631  bounds.push_back(bbox.max.y);
632 }
633 
634 GeoPolygon::GeoPolygon(const std::vector<double>& coords,
635  const std::vector<int32_t>& ring_sizes) {
636  geom_ = OGRGeometryFactory::createGeometry(OGRwkbGeometryType::wkbPolygon);
637  OGRPolygon* poly = dynamic_cast<OGRPolygon*>(geom_);
638  CHECK(poly);
639 
640  size_t coords_ctr = 0;
641  for (size_t r = 0; r < ring_sizes.size(); r++) {
642  OGRLinearRing ring;
643  const auto ring_sz = ring_sizes[r];
644  for (auto i = 0; i < 2 * ring_sz; i += 2) {
645  ring.addPoint(coords[coords_ctr + i], coords[coords_ctr + i + 1]);
646  }
647  ring.addPoint(coords[coords_ctr], coords[coords_ctr + 1]);
648  coords_ctr += 2 * ring_sz;
649  poly->addRing(&ring);
650  }
651 }
652 
653 GeoPolygon::GeoPolygon(const std::string& wkt) {
654  const auto err = GeoBase::createFromWktString(wkt, &geom_);
655  if (err != OGRERR_NONE) {
656  throw GeoTypesError("Polygon", err);
657  }
658  CHECK(geom_);
659  if (wkbFlatten(geom_->getGeometryType()) != OGRwkbGeometryType::wkbPolygon) {
660  throw GeoTypesError("Polygon",
661  "Unexpected geometry type from WKT string: " +
662  std::string(OGRGeometryTypeToName(geom_->getGeometryType())));
663  }
664 }
665 
666 void GeoPolygon::getColumns(std::vector<double>& coords,
667  std::vector<int32_t>& ring_sizes,
668  std::vector<double>& bounds) const {
669  const auto poly_geom = dynamic_cast<OGRPolygon*>(geom_);
670  CHECK(poly_geom);
671 
672  if (poly_geom->IsEmpty()) {
673  // until the run-time can handle empties
674  throw GeoTypesError("Polygon", "'EMPTY' not supported");
675  // return null bounds
676  bounds.push_back(NULL_DOUBLE);
677  bounds.push_back(NULL_DOUBLE);
678  bounds.push_back(NULL_DOUBLE);
679  bounds.push_back(NULL_DOUBLE);
680  return;
681  }
682 
683  BoundingBox bbox;
684  const auto exterior_ring = poly_geom->getExteriorRing();
685  CHECK(exterior_ring);
686  // All exterior rings are imported CCW
687  if (exterior_ring->isClockwise()) {
688  exterior_ring->reverseWindingOrder();
689  }
690  const auto num_points_added = process_poly_ring(exterior_ring, coords, &bbox);
691  ring_sizes.push_back(num_points_added);
692  for (auto r = 0; r < poly_geom->getNumInteriorRings(); r++) {
693  auto interior_ring = poly_geom->getInteriorRing(r);
694  CHECK(interior_ring);
695  // All interior rings are imported CW
696  if (!interior_ring->isClockwise()) {
697  interior_ring->reverseWindingOrder();
698  }
699  const auto num_points_added = process_poly_ring(interior_ring, coords, nullptr);
700  ring_sizes.push_back(num_points_added);
701  }
702  bounds.push_back(bbox.min.x);
703  bounds.push_back(bbox.min.y);
704  bounds.push_back(bbox.max.x);
705  bounds.push_back(bbox.max.y);
706 }
707 
709  const auto poly_geom = dynamic_cast<OGRPolygon*>(geom_);
710  CHECK(poly_geom);
711  return poly_geom->getNumInteriorRings();
712 }
713 
714 GeoMultiPolygon::GeoMultiPolygon(const std::vector<double>& coords,
715  const std::vector<int32_t>& ring_sizes,
716  const std::vector<int32_t>& poly_rings) {
717  geom_ = OGRGeometryFactory::createGeometry(OGRwkbGeometryType::wkbMultiPolygon);
718  OGRMultiPolygon* multipoly = dynamic_cast<OGRMultiPolygon*>(geom_);
719  CHECK(multipoly);
720 
721  size_t ring_ctr = 0;
722  size_t coords_ctr = 0;
723  for (const auto& rings_in_poly : poly_rings) {
724  OGRPolygon poly;
725  for (auto r = 0; r < rings_in_poly; r++) {
726  OGRLinearRing ring;
727  const auto ring_sz = ring_sizes[ring_ctr];
728  for (auto i = 0; i < 2 * ring_sz; i += 2) {
729  ring.addPoint(coords[coords_ctr + i], coords[coords_ctr + i + 1]);
730  }
731  ring.addPoint(coords[coords_ctr], coords[coords_ctr + 1]);
732  coords_ctr += 2 * ring_sz;
733  poly.addRing(&ring);
734  ring_ctr++;
735  }
736  multipoly->addGeometry(&poly);
737  }
738 }
739 
740 GeoMultiPolygon::GeoMultiPolygon(const std::string& wkt) {
741  const auto err = GeoBase::createFromWktString(wkt, &geom_);
742  if (err != OGRERR_NONE) {
743  throw GeoTypesError("MultiPolygon", err);
744  }
745  CHECK(geom_);
746  if (wkbFlatten(geom_->getGeometryType()) != OGRwkbGeometryType::wkbMultiPolygon) {
747  throw GeoTypesError("MultiPolygon",
748  "Unexpected geometry type from WKT string: " +
749  std::string(OGRGeometryTypeToName(geom_->getGeometryType())));
750  }
751 }
752 
753 void GeoMultiPolygon::getColumns(std::vector<double>& coords,
754  std::vector<int32_t>& ring_sizes,
755  std::vector<int32_t>& poly_rings,
756  std::vector<double>& bounds) const {
757  const auto mpoly = dynamic_cast<OGRMultiPolygon*>(geom_);
758  CHECK(mpoly);
759 
760  if (mpoly->IsEmpty()) {
761  // until the run-time can handle empties
762  throw GeoTypesError("MultiPolygon", "'EMPTY' not supported");
763  // return null bounds
764  bounds.push_back(NULL_DOUBLE);
765  bounds.push_back(NULL_DOUBLE);
766  bounds.push_back(NULL_DOUBLE);
767  bounds.push_back(NULL_DOUBLE);
768  return;
769  }
770 
771  BoundingBox bbox;
772  for (auto p = 0; p < mpoly->getNumGeometries(); p++) {
773  const auto mpoly_geom = mpoly->getGeometryRef(p);
774  CHECK(mpoly_geom);
775  const auto poly_geom = dynamic_cast<OGRPolygon*>(mpoly_geom);
776  if (!poly_geom) {
777  throw GeoTypesError("MultiPolygon",
778  "Failed to read polygon geometry from multipolygon");
779  }
780  const auto exterior_ring = poly_geom->getExteriorRing();
781  CHECK(exterior_ring);
782  // All exterior rings are imported CCW
783  if (exterior_ring->isClockwise()) {
784  exterior_ring->reverseWindingOrder();
785  }
786  const auto num_points_added = process_poly_ring(exterior_ring, coords, &bbox);
787  ring_sizes.push_back(num_points_added);
788 
789  for (auto r = 0; r < poly_geom->getNumInteriorRings(); r++) {
790  auto interior_ring = poly_geom->getInteriorRing(r);
791  CHECK(interior_ring);
792  // All interior rings are imported CW
793  if (!interior_ring->isClockwise()) {
794  interior_ring->reverseWindingOrder();
795  }
796  const auto num_points_added = process_poly_ring(interior_ring, coords, nullptr);
797  ring_sizes.push_back(num_points_added);
798  }
799  poly_rings.push_back(poly_geom->getNumInteriorRings() + 1);
800  }
801  bounds.push_back(bbox.min.x);
802  bounds.push_back(bbox.min.y);
803  bounds.push_back(bbox.max.x);
804  bounds.push_back(bbox.max.y);
805 }
806 
808  const auto err = GeoBase::createFromWktString(wkt, &geom_);
809  if (err != OGRERR_NONE) {
810  throw GeoTypesError("GeometryCollection", err);
811  }
812  CHECK(geom_);
813  if (wkbFlatten(geom_->getGeometryType()) != OGRwkbGeometryType::wkbGeometryCollection) {
814  throw GeoTypesError("GeometryCollection",
815  "Unexpected geometry type from WKT string: " +
816  std::string(OGRGeometryTypeToName(geom_->getGeometryType())));
817  }
818 }
819 
820 namespace {
821 
823  uint8_t table_[128];
824  constexpr HexDigitToDecimalTable() : table_{} {
825  table_['1'] = 1;
826  table_['2'] = 2;
827  table_['3'] = 3;
828  table_['4'] = 4;
829  table_['5'] = 5;
830  table_['6'] = 6;
831  table_['7'] = 7;
832  table_['8'] = 8;
833  table_['9'] = 9;
834  table_['a'] = 10;
835  table_['A'] = 10;
836  table_['b'] = 11;
837  table_['B'] = 11;
838  table_['c'] = 12;
839  table_['C'] = 12;
840  table_['d'] = 13;
841  table_['D'] = 13;
842  table_['e'] = 14;
843  table_['E'] = 14;
844  table_['f'] = 15;
845  table_['F'] = 15;
846  }
847  constexpr uint8_t operator[](const char& hex_digit) const {
848  return (hex_digit < 0) ? 0 : table_[static_cast<int>(hex_digit)];
849  }
850 };
851 
853 
854 inline uint8_t hex_to_binary(const char& usb, const char& lsb) {
855  return (hex_digit_to_decimal_table[usb] << 4) | hex_digit_to_decimal_table[lsb];
856 }
857 
858 std::vector<uint8_t> hex_string_to_binary_vector(const std::string& wkb_hex) {
859  auto num_bytes = wkb_hex.size() >> 1;
860  std::vector<uint8_t> wkb(num_bytes);
861  auto* chars = wkb_hex.data();
862  auto* bytes = wkb.data();
863  for (size_t i = 0; i < num_bytes; i++) {
864  auto const& usb = *chars++;
865  auto const& lsb = *chars++;
866  *bytes++ = hex_to_binary(usb, lsb);
867  }
868  return wkb;
869 }
870 
871 } // namespace
872 
873 OGRGeometry* GeoTypesFactory::createOGRGeometry(const std::string& wkt_or_wkb_hex) {
874  OGRGeometry* geom = nullptr;
875  OGRErr err = OGRERR_NONE;
876  if (wkt_or_wkb_hex.empty()) {
877  err = OGRERR_NOT_ENOUGH_DATA;
878  } else if (wkt_or_wkb_hex[0] == '0') { // all WKB hex strings start with a 0
879  err = GeoBase::createFromWkb(hex_string_to_binary_vector(wkt_or_wkb_hex), &geom);
880  } else {
881  err = GeoBase::createFromWktString(wkt_or_wkb_hex, &geom);
882  }
883  if (err != OGRERR_NONE) {
884  throw GeoTypesError("GeoFactory", err);
885  }
886  return geom;
887 }
888 
889 std::unique_ptr<GeoBase> GeoTypesFactory::createGeoType(
890  const std::string& wkt_or_wkb_hex) {
892 }
893 
894 std::unique_ptr<GeoBase> GeoTypesFactory::createGeoType(const std::vector<uint8_t>& wkb) {
895  OGRGeometry* geom = nullptr;
896  const auto err = GeoBase::createFromWkb(wkb, &geom);
897  if (err != OGRERR_NONE) {
898  throw GeoTypesError("GeoFactory", err);
899  }
901 }
902 
903 std::unique_ptr<GeoBase> GeoTypesFactory::createGeoType(OGRGeometry* geom) {
904  return GeoTypesFactory::createGeoTypeImpl(geom, false);
905 }
906 
907 bool GeoTypesFactory::getGeoColumns(const std::string& wkt_or_wkb_hex,
908  SQLTypeInfo& ti,
909  std::vector<double>& coords,
910  std::vector<double>& bounds,
911  std::vector<int>& ring_sizes,
912  std::vector<int>& poly_rings,
913  const bool promote_poly_to_mpoly) {
914  try {
915  if (wkt_or_wkb_hex.empty() || wkt_or_wkb_hex == "NULL") {
917  ti, coords, bounds, ring_sizes, poly_rings, promote_poly_to_mpoly);
918  return true;
919  }
920 
921  const auto geospatial_base = GeoTypesFactory::createGeoType(wkt_or_wkb_hex);
922 
923  if (!geospatial_base || !geospatial_base->transform(ti)) {
924  return false;
925  }
926 
927  getGeoColumnsImpl(geospatial_base,
928  ti,
929  coords,
930  bounds,
931  ring_sizes,
932  poly_rings,
933  promote_poly_to_mpoly);
934 
935  } catch (const std::exception& e) {
936  LOG(ERROR) << "Geospatial Import Error: " << e.what();
937  return false;
938  }
939 
940  return true;
941 }
942 
943 bool GeoTypesFactory::getGeoColumns(const std::vector<uint8_t>& wkb,
944  SQLTypeInfo& ti,
945  std::vector<double>& coords,
946  std::vector<double>& bounds,
947  std::vector<int>& ring_sizes,
948  std::vector<int>& poly_rings,
949  const bool promote_poly_to_mpoly) {
950  try {
951  const auto geospatial_base = GeoTypesFactory::createGeoType(wkb);
952 
953  if (!geospatial_base || !geospatial_base->transform(ti)) {
954  return false;
955  }
956 
957  getGeoColumnsImpl(geospatial_base,
958  ti,
959  coords,
960  bounds,
961  ring_sizes,
962  poly_rings,
963  promote_poly_to_mpoly);
964 
965  } catch (const std::exception& e) {
966  LOG(ERROR) << "Geospatial Import Error: " << e.what();
967  return false;
968  }
969 
970  return true;
971 }
972 
973 bool GeoTypesFactory::getGeoColumns(OGRGeometry* geom,
974  SQLTypeInfo& ti,
975  std::vector<double>& coords,
976  std::vector<double>& bounds,
977  std::vector<int>& ring_sizes,
978  std::vector<int>& poly_rings,
979  const bool promote_poly_to_mpoly) {
980  try {
981  const auto geospatial_base = GeoTypesFactory::createGeoType(geom);
982 
983  if (!geospatial_base || !geospatial_base->transform(ti)) {
984  return false;
985  }
986 
987  getGeoColumnsImpl(geospatial_base,
988  ti,
989  coords,
990  bounds,
991  ring_sizes,
992  poly_rings,
993  promote_poly_to_mpoly);
994 
995  } catch (const std::exception& e) {
996  LOG(ERROR) << "Geospatial Import Error: " << e.what();
997  return false;
998  }
999 
1000  return true;
1001 }
1002 
1003 bool GeoTypesFactory::getGeoColumns(const std::vector<std::string>* wkt_or_wkb_hex_column,
1004  SQLTypeInfo& ti,
1005  std::vector<std::vector<double>>& coords_column,
1006  std::vector<std::vector<double>>& bounds_column,
1007  std::vector<std::vector<int>>& ring_sizes_column,
1008  std::vector<std::vector<int>>& poly_rings_column,
1009  const bool promote_poly_to_mpoly) {
1010  try {
1011  for (const auto& wkt_or_wkb_hex : *wkt_or_wkb_hex_column) {
1012  std::vector<double> coords;
1013  std::vector<double> bounds;
1014  std::vector<int> ring_sizes;
1015  std::vector<int> poly_rings;
1016 
1017  SQLTypeInfo row_ti{ti};
1018  getGeoColumns(wkt_or_wkb_hex,
1019  row_ti,
1020  coords,
1021  bounds,
1022  ring_sizes,
1023  poly_rings,
1024  promote_poly_to_mpoly);
1025 
1026  if (ti.get_type() != row_ti.get_type()) {
1027  throw GeoTypesError("GeoFactory", "Columnar: Geometry type mismatch");
1028  }
1029  coords_column.push_back(coords);
1030  bounds_column.push_back(bounds);
1031  ring_sizes_column.push_back(ring_sizes);
1032  poly_rings_column.push_back(poly_rings);
1033  }
1034 
1035  } catch (const std::exception& e) {
1036  LOG(ERROR) << "Geospatial column Import Error: " << e.what();
1037  return false;
1038  }
1039 
1040  return true;
1041 }
1042 
1043 std::unique_ptr<GeoBase> GeoTypesFactory::createGeoTypeImpl(OGRGeometry* geom,
1044  const bool owns_geom_obj) {
1045  switch (wkbFlatten(geom->getGeometryType())) {
1046  case wkbPoint:
1047  return std::unique_ptr<GeoPoint>(new GeoPoint(geom, owns_geom_obj));
1048  case wkbLineString:
1049  return std::unique_ptr<GeoLineString>(new GeoLineString(geom, owns_geom_obj));
1050  case wkbPolygon:
1051  return std::unique_ptr<GeoPolygon>(new GeoPolygon(geom, owns_geom_obj));
1052  case wkbMultiPolygon:
1053  return std::unique_ptr<GeoMultiPolygon>(new GeoMultiPolygon(geom, owns_geom_obj));
1054  case wkbGeometryCollection:
1055  return std::unique_ptr<GeoGeometryCollection>(
1056  new GeoGeometryCollection(geom, owns_geom_obj));
1057  default:
1058  throw GeoTypesError(
1059  "GeoTypesFactory",
1060  "Unrecognized geometry type: " + std::string(geom->getGeometryName()));
1061  }
1062 }
1063 
1064 void GeoTypesFactory::getGeoColumnsImpl(const std::unique_ptr<GeoBase>& geospatial_base,
1065  SQLTypeInfo& ti,
1066  std::vector<double>& coords,
1067  std::vector<double>& bounds,
1068  std::vector<int>& ring_sizes,
1069  std::vector<int>& poly_rings,
1070  const bool promote_poly_to_mpoly) {
1071  switch (geospatial_base->getType()) {
1072  case GeoBase::GeoType::kPOINT: {
1073  const auto geospatial_point = dynamic_cast<GeoPoint*>(geospatial_base.get());
1074  CHECK(geospatial_point);
1075  geospatial_point->getColumns(coords);
1076  ti.set_type(kPOINT);
1077  break;
1078  }
1080  const auto geospatial_linestring =
1081  dynamic_cast<GeoLineString*>(geospatial_base.get());
1082  CHECK(geospatial_linestring);
1083  geospatial_linestring->getColumns(coords, bounds);
1084  ti.set_type(kLINESTRING);
1085  break;
1086  }
1088  const auto geospatial_poly = dynamic_cast<GeoPolygon*>(geospatial_base.get());
1089  CHECK(geospatial_poly);
1090  geospatial_poly->getColumns(coords, ring_sizes, bounds);
1091  if (promote_poly_to_mpoly) {
1092  if (ring_sizes.size()) {
1093  CHECK_GT(coords.size(), 0u);
1094  poly_rings.push_back(1 + geospatial_poly->getNumInteriorRings());
1095  }
1096  }
1097  ti.set_type(kPOLYGON);
1098  break;
1099  }
1101  const auto geospatial_mpoly = dynamic_cast<GeoMultiPolygon*>(geospatial_base.get());
1102  CHECK(geospatial_mpoly);
1103  geospatial_mpoly->getColumns(coords, ring_sizes, poly_rings, bounds);
1104  ti.set_type(kMULTIPOLYGON);
1105  break;
1106  }
1109  default:
1110  throw std::runtime_error("Unrecognized geospatial type");
1111  }
1112 }
1113 
1115  std::vector<double>& coords,
1116  std::vector<double>& bounds,
1117  std::vector<int>& ring_sizes,
1118  std::vector<int>& poly_rings,
1119  const bool promote_poly_to_mpoly) {
1120  auto t = ti.get_type();
1121  switch (t) {
1122  case kPOINT: {
1123  // NULL fixlen coords array
1124  coords.push_back(NULL_ARRAY_DOUBLE);
1125  coords.push_back(NULL_DOUBLE);
1126  } break;
1127  case kLINESTRING:
1128  case kPOLYGON:
1129  case kMULTIPOLYGON: {
1130  // Leaving coords array empty
1131  // NULL fixlen bounds array
1132  bounds.push_back(NULL_ARRAY_DOUBLE);
1133  bounds.push_back(NULL_DOUBLE);
1134  bounds.push_back(NULL_DOUBLE);
1135  bounds.push_back(NULL_DOUBLE);
1136  // Leaving ring_sizes and poly_rings arrays empty
1137  } break;
1138  default:
1139  throw std::runtime_error("Unsupported NULL geo");
1140  }
1141 }
1142 
1143 } // namespace Geospatial
OGRGeometry * geom_
Definition: Types.h:90
std::unique_ptr< GeoBase > optimized_run(GeoOp op, const GeoBase &other) const
Definition: Types.cpp:439
static std::unique_ptr< GeoBase > createGeoType(const std::string &wkt_or_wkb_hex)
Definition: Types.cpp:889
GeoPoint(const std::vector< double > &coords)
Definition: Types.cpp:536
#define NULL_DOUBLE
void getColumns(std::vector< double > &coords) const
Definition: Types.cpp:562
#define SRID_LAEA_START
Definition: Types.cpp:222
int32_t getNumInteriorRings() const
Definition: Types.cpp:708
#define SRID_NORTH_UTM_END
Definition: Types.cpp:212
GeoMultiPolygon(const std::vector< double > &coords, const std::vector< int32_t > &ring_sizes, const std::vector< int32_t > &poly_rings)
Definition: Types.cpp:714
bool owns_geom_obj_
Definition: Types.h:91
constexpr auto DOUBLE_MAX
Definition: Types.cpp:40
#define LOG(tag)
Definition: Logger.h:188
constexpr HexDigitToDecimalTable hex_digit_to_decimal_table
Definition: Types.cpp:852
tuple r
Definition: test_fsi.py:16
GeoLineString(const std::vector< double > &coords)
Definition: Types.cpp:580
GeoGeometryCollection(const std::vector< double > &coords, const std::vector< int32_t > &ring_sizes, const std::vector< int32_t > &poly_rings, const std::vector< int32_t > &geo_kinds)
Definition: Types.h:195
static void init()
Definition: GDAL.cpp:59
static void getGeoColumnsImpl(const std::unique_ptr< GeoBase > &geospatial_base, SQLTypeInfo &ti, std::vector< double > &coords, std::vector< double > &bounds, std::vector< int > &ring_sizes, std::vector< int > &poly_rings, const bool promote_poly_to_mpoly=false)
Definition: Types.cpp:1064
Constants for Builtin SQL Types supported by OmniSci.
static void getNullGeoColumns(SQLTypeInfo &ti, std::vector< double > &coords, std::vector< double > &bounds, std::vector< int > &ring_sizes, std::vector< int > &poly_rings, const bool promote_poly_to_mpoly=false)
Definition: Types.cpp:1114
HOST DEVICE SQLTypes get_type() const
Definition: sqltypes.h:314
static std::unique_ptr< Geospatial::GeoBase > createGeoTypeImpl(OGRGeometry *geom, const bool owns_geom_obj=true)
Definition: Types.cpp:1043
bool getWkb(std::vector< uint8_t > &) const
Definition: Types.cpp:186
#define CHECK_GT(x, y)
Definition: Logger.h:209
constexpr uint8_t operator[](const char &hex_digit) const
Definition: Types.cpp:847
static std::shared_ptr< OGRCoordinateTransformation > getTransformation(int32_t srid0, int32_t srid1)
Definition: Types.cpp:307
std::string to_string(char const *&&v)
bool isEmpty() const
Definition: Types.cpp:196
int process_poly_ring(OGRLinearRing *ring, std::vector< double > &coords, BoundingBox *bbox)
Definition: Types.cpp:70
virtual ~GeoBase()
Definition: Types.cpp:145
void update(double x, double y)
Definition: Types.cpp:54
static std::string OGRErrorToStr(const int ogr_err)
Definition: Types.cpp:118
#define SRID_NORTH_UTM_START
Definition: Types.cpp:210
static int createFromWkb(const std::vector< uint8_t > &wkb, OGRGeometry **geom)
Definition: Types.cpp:173
void getColumns(std::vector< double > &coords, std::vector< int32_t > &ring_sizes, std::vector< int32_t > &poly_rings, std::vector< double > &bounds) const
Definition: Types.cpp:753
static bool getGeoColumns(const std::string &wkt_or_wkb_hex, SQLTypeInfo &ti, std::vector< double > &coords, std::vector< double > &bounds, std::vector< int > &ring_sizes, std::vector< int > &poly_rings, const bool promote_poly_to_mpoly=false)
Definition: Types.cpp:907
virtual bool operator==(const GeoBase &other) const
Definition: Types.cpp:200
static OGRGeometry * createOGRGeometry(const std::string &wkt_or_wkb_hex)
Definition: Types.cpp:873
static int createFromWktString(const std::string &wkt, OGRGeometry **geom)
Definition: Types.cpp:154
void getColumns(std::vector< double > &coords, std::vector< double > &bounds) const
Definition: Types.cpp:602
std::mutex transformation_map_mutex_
Definition: Types.cpp:114
constexpr auto DOUBLE_MIN
Definition: Types.cpp:41
HOST DEVICE int get_input_srid() const
Definition: sqltypes.h:318
bool transform(int32_t srid0, int32_t srid1)
Definition: Types.cpp:383
#define NULL_ARRAY_DOUBLE
#define SRID_SOUTH_UTM_START
Definition: Types.cpp:216
#define SRID_SOUTH_UTM_END
Definition: Types.cpp:218
#define CHECK(condition)
Definition: Logger.h:197
char * t
std::string getWktString() const
Definition: Types.cpp:164
#define SRID_SOUTH_LAMBERT
Definition: Types.cpp:220
#define SRID_WORLD_MERCATOR
Definition: Types.cpp:208
#define SRID_LAEA_END
Definition: Types.cpp:224
uint8_t hex_to_binary(const char &usb, const char &lsb)
Definition: Types.cpp:854
std::unique_ptr< GeoBase > run(GeoOp op, const GeoBase &other) const
Definition: Types.cpp:406
#define SRID_NORTH_LAMBERT
Definition: Types.cpp:214
std::map< std::tuple< int32_t, int32_t >, std::shared_ptr< OGRCoordinateTransformation > > transformation_map_
Definition: Types.cpp:116
std::vector< uint8_t > hex_string_to_binary_vector(const std::string &wkb_hex)
Definition: Types.cpp:858
void getColumns(std::vector< double > &coords, std::vector< int32_t > &ring_sizes, std::vector< double > &bounds) const
Definition: Types.cpp:666
int32_t getBestPlanarSRID() const
Definition: Types.cpp:226
GeoPolygon(const std::vector< double > &coords, const std::vector< int32_t > &ring_sizes)
Definition: Types.cpp:634
HOST DEVICE int get_output_srid() const
Definition: sqltypes.h:320
HOST DEVICE void set_type(SQLTypes t)
Definition: sqltypes.h:404