diff --git a/.github/workflows/Linux.yml b/.github/workflows/Linux.yml index 9a739c06..b4f21942 100644 --- a/.github/workflows/Linux.yml +++ b/.github/workflows/Linux.yml @@ -95,7 +95,7 @@ jobs: openssl: 0 - name: Setup vcpkg - uses: lukka/run-vcpkg@v11 + uses: lukka/run-vcpkg@v11.1 with: vcpkgGitCommitId: 501db0f17ef6df184fcdbfbe0f87cde2313b6ab1 diff --git a/.github/workflows/MainDistributionPipeline.yml b/.github/workflows/MainDistributionPipeline.yml index bc6ca5b0..d5bc02de 100644 --- a/.github/workflows/MainDistributionPipeline.yml +++ b/.github/workflows/MainDistributionPipeline.yml @@ -20,7 +20,7 @@ concurrency: jobs: duckdb-stable-build: name: Build extension binaries - uses: duckdb/duckdb/.github/workflows/_extension_distribution.yml@main + uses: duckdb/duckdb/.github/workflows/_extension_distribution.yml@60ddc316ca0c1585f14d55aa73f9db59d8fc05d1 with: duckdb_version: v0.9.1 extension_name: spatial diff --git a/spatial/include/spatial/core/geometry/geometry.hpp b/spatial/include/spatial/core/geometry/geometry.hpp index fb6d1d37..b2e9994d 100644 --- a/spatial/include/spatial/core/geometry/geometry.hpp +++ b/spatial/include/spatial/core/geometry/geometry.hpp @@ -104,8 +104,7 @@ class Point { } string ToString() const; bool IsEmpty() const; - Vertex &GetVertex(); - const Vertex &GetVertex() const; + Vertex GetVertex() const; const VertexVector &Vertices() const { return vertices; diff --git a/spatial/include/spatial/core/geometry/vertex_vector.hpp b/spatial/include/spatial/core/geometry/vertex_vector.hpp index 9e839fa9..af06f911 100644 --- a/spatial/include/spatial/core/geometry/vertex_vector.hpp +++ b/spatial/include/spatial/core/geometry/vertex_vector.hpp @@ -66,39 +66,18 @@ class VertexVector { public: uint32_t count; uint32_t capacity; - Vertex *data; + data_ptr_t data; - explicit VertexVector(Vertex *data, uint32_t count, uint32_t capacity) + explicit VertexVector(data_ptr_t data, uint32_t count, uint32_t capacity) : count(count), capacity(capacity), data(data) { } // Create a VertexVector from an already existing buffer - static VertexVector FromBuffer(Vertex *buffer, uint32_t count) { + static VertexVector FromBuffer(data_ptr_t buffer, uint32_t count) { VertexVector array(buffer, count, count); return array; } - inline Vertex &operator[](uint32_t index) const { - D_ASSERT(index < count); - return data[index]; - } - - inline Vertex *begin() { - return data; - } - - inline Vertex *end() { - return data + count; - } - - const Vertex *begin() const { - return data; - } - - const Vertex *end() const { - return data + count; - } - inline uint32_t Count() const { return count; } @@ -109,7 +88,18 @@ class VertexVector { inline void Add(const Vertex &v) { D_ASSERT(count < capacity); - data[count++] = v; + Store(v, data + count * sizeof(Vertex)); + count++; + } + + inline void Set(uint32_t index, const Vertex &v) const { + D_ASSERT(index < count); + Store(v, data + index * sizeof(Vertex)); + } + + inline Vertex Get(uint32_t index) const { + D_ASSERT(index < count); + return Load(data + index * sizeof(Vertex)); } // Returns the number of bytes that this VertexVector requires to be serialized @@ -120,10 +110,6 @@ class VertexVector { void Serialize(Cursor &cursor) const; void SerializeAndUpdateBounds(Cursor &cursor, BoundingBox &bbox) const; - inline Vertex *Data() { - return data; - } - double Length() const; double SignedArea() const; double Area() const; diff --git a/spatial/src/spatial/core/functions/cast/geometry_cast.cpp b/spatial/src/spatial/core/functions/cast/geometry_cast.cpp index 28cfd26e..d88b6408 100644 --- a/spatial/src/spatial/core/functions/cast/geometry_cast.cpp +++ b/spatial/src/spatial/core/functions/cast/geometry_cast.cpp @@ -46,7 +46,7 @@ static bool GeometryToPoint2DCast(Vector &source, Vector &result, idx_t count, C if (point.IsEmpty()) { throw CastException("Cannot cast empty point GEOMETRY to POINT_2D"); } - auto &vertex = point.GetVertex(); + auto vertex = point.GetVertex(); return POINT_TYPE {vertex.x, vertex.y}; }); return true; @@ -103,8 +103,8 @@ static bool GeometryToLineString2DCast(Vector &source, Vector &result, idx_t cou ListVector::Reserve(result, total_coords); for (idx_t i = 0; i < line_size; i++) { - x_data[entry.offset + i] = line.Vertices()[i].x; - y_data[entry.offset + i] = line.Vertices()[i].y; + x_data[entry.offset + i] = line.Vertices().Get(i).x; + y_data[entry.offset + i] = line.Vertices().Get(i).y; } return entry; }); @@ -182,8 +182,8 @@ static bool GeometryToPolygon2DCast(Vector &source, Vector &result, idx_t count, ring_entries[total_rings + ring_idx] = ring_entry; for (idx_t j = 0; j < ring_size; j++) { - x_data[ring_entry.offset + j] = ring.data[j].x; - y_data[ring_entry.offset + j] = ring.data[j].y; + x_data[ring_entry.offset + j] = ring.Get(j).x; + y_data[ring_entry.offset + j] = ring.Get(j).y; } total_coords += ring_size; } @@ -217,16 +217,13 @@ static bool Box2DToGeometryCast(Vector &source, Vector &result, idx_t count, Cas auto geom = lstate.factory.CreatePolygon(1, &capacity); auto &shell = geom.Ring(0); - shell.data[0].x = minx; - shell.data[0].y = miny; - shell.data[1].x = maxx; - shell.data[1].y = miny; - shell.data[2].x = maxx; - shell.data[2].y = maxy; - shell.data[3].x = minx; - shell.data[3].y = maxy; - shell.data[4].x = minx; - shell.data[4].y = miny; + + shell.Set(0, Vertex(minx, miny)); + shell.Set(1, Vertex(maxx, miny)); + shell.Set(2, Vertex(maxx, maxy)); + shell.Set(3, Vertex(minx, maxy)); + shell.Set(4, Vertex(minx, miny)); + shell.count = 5; return lstate.factory.Serialize(result, Geometry(geom)); }); diff --git a/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp b/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp index 87f02d2d..cabe2ceb 100644 --- a/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_asgeojson.cpp @@ -60,7 +60,7 @@ class JSONAllocator { static void VerticesToGeoJSON(const VertexVector &vertices, yyjson_mut_doc *doc, yyjson_mut_val *arr) { // TODO: If the vertexvector is empty, do we null, add an empty array or a pair of NaN? for (uint32_t i = 0; i < vertices.count; i++) { - auto &vertex = vertices[i]; + auto vertex = vertices.Get(i); auto coord = yyjson_mut_arr(doc); yyjson_mut_arr_add_real(doc, coord, vertex.x); yyjson_mut_arr_add_real(doc, coord, vertex.y); @@ -75,7 +75,7 @@ static void ToGeoJSON(const Point &point, yyjson_mut_doc *doc, yyjson_mut_val *o yyjson_mut_obj_add_val(doc, obj, "coordinates", coords); if (!point.IsEmpty()) { - auto &vertex = point.GetVertex(); + auto vertex = point.GetVertex(); yyjson_mut_arr_add_real(doc, coords, vertex.x); yyjson_mut_arr_add_real(doc, coords, vertex.y); } diff --git a/spatial/src/spatial/core/functions/scalar/st_dump.cpp b/spatial/src/spatial/core/functions/scalar/st_dump.cpp index aa16d8cf..b41be6ac 100644 --- a/spatial/src/spatial/core/functions/scalar/st_dump.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_dump.cpp @@ -23,7 +23,7 @@ static void DumpFunction(DataChunk &args, ExpressionState &state, Vector &result idx_t total_geom_count = 0; idx_t total_path_count = 0; - for(idx_t out_row_idx = 0; out_row_idx < count; out_row_idx++) { + for (idx_t out_row_idx = 0; out_row_idx < count; out_row_idx++) { auto in_row_idx = geom_format.sel->get_index(out_row_idx); if (!geom_format.validity.RowIsValid(in_row_idx)) { @@ -39,38 +39,35 @@ static void DumpFunction(DataChunk &args, ExpressionState &state, Vector &result stack.emplace_back(geometry, vector()); - while(!stack.empty()) { + while (!stack.empty()) { auto current = stack.back(); auto current_geom = std::get<0>(current); auto current_path = std::get<1>(current); stack.pop_back(); - if(current_geom.Type() == GeometryType::MULTIPOINT) { + if (current_geom.Type() == GeometryType::MULTIPOINT) { auto mpoint = current_geom.GetMultiPoint(); for (int32_t i = 0; i < mpoint.Count(); i++) { auto path = current_path; path.push_back(i + 1); // path is 1-indexed stack.emplace_back(mpoint[i], path); } - } - else if(current_geom.Type() == GeometryType::MULTILINESTRING) { + } else if (current_geom.Type() == GeometryType::MULTILINESTRING) { auto mline = current_geom.GetMultiLineString(); for (int32_t i = 0; i < mline.Count(); i++) { auto path = current_path; path.push_back(i + 1); stack.emplace_back(mline[i], path); } - } - else if(current_geom.Type() == GeometryType::MULTIPOLYGON) { + } else if (current_geom.Type() == GeometryType::MULTIPOLYGON) { auto mpoly = current_geom.GetMultiPolygon(); for (int32_t i = 0; i < mpoly.Count(); i++) { auto path = current_path; path.push_back(i + 1); stack.emplace_back(mpoly[i], path); } - } - else if (current_geom.Type() == GeometryType::GEOMETRYCOLLECTION) { + } else if (current_geom.Type() == GeometryType::GEOMETRYCOLLECTION) { auto collection = current_geom.GetGeometryCollection(); for (int32_t i = 0; i < collection.Count(); i++) { auto path = current_path; @@ -105,7 +102,7 @@ static void DumpFunction(DataChunk &args, ExpressionState &state, Vector &result auto &result_path_vec = result_list_children[1]; auto geom_data = FlatVector::GetData(*result_geom_vec); - for(idx_t i = 0; i < geom_length; i++) { + for (idx_t i = 0; i < geom_length; i++) { // Write the geometry auto &item_blob = std::get<0>(items[i]); geom_data[geom_offset + i] = lstate.factory.Serialize(*result_geom_vec, item_blob); @@ -128,13 +125,13 @@ static void DumpFunction(DataChunk &args, ExpressionState &state, Vector &result auto &path_data_vec = ListVector::GetEntry(*result_path_vec); auto path_data = FlatVector::GetData(path_data_vec); - for(idx_t j = 0; j < path_length; j++) { + for (idx_t j = 0; j < path_length; j++) { path_data[path_offset + j] = path[j]; } } } - if(count == 1) { + if (count == 1) { result.SetVectorType(VectorType::CONSTANT_VECTOR); } } @@ -142,10 +139,11 @@ static void DumpFunction(DataChunk &args, ExpressionState &state, Vector &result void CoreScalarFunctions::RegisterStDump(DatabaseInstance &db) { ScalarFunctionSet set("ST_Dump"); - set.AddFunction(ScalarFunction({GeoTypes::GEOMETRY()}, - LogicalType::LIST(LogicalType::STRUCT({{"geom", GeoTypes::GEOMETRY()}, {"path", LogicalType::LIST(LogicalType::INTEGER)}})), - DumpFunction, - nullptr, nullptr, nullptr, GeometryFunctionLocalState::Init)); + set.AddFunction( + ScalarFunction({GeoTypes::GEOMETRY()}, + LogicalType::LIST(LogicalType::STRUCT( + {{"geom", GeoTypes::GEOMETRY()}, {"path", LogicalType::LIST(LogicalType::INTEGER)}})), + DumpFunction, nullptr, nullptr, nullptr, GeometryFunctionLocalState::Init)); ExtensionUtil::RegisterFunction(db, set); } diff --git a/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp b/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp index d65cfcc3..b17aa5af 100644 --- a/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_endpoint.cpp @@ -80,7 +80,7 @@ static void GeometryEndPointFunction(DataChunk &args, ExpressionState &state, Ve return string_t(); } - auto &point = line.Vertices()[point_count - 1]; + auto point = line.Vertices().Get(point_count - 1); return lstate.factory.Serialize(result, Geometry(lstate.factory.CreatePoint(point.x, point.y))); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp b/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp index 6127b2fb..f450d751 100644 --- a/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_exteriorring.cpp @@ -114,7 +114,7 @@ static void GeometryExteriorRingFunction(DataChunk &args, ExpressionState &state auto line = lstate.factory.CreateLineString(num_points); for (uint32_t i = 0; i < num_points; i++) { - line.Vertices().Add(shell[i]); + line.Vertices().Add(shell.Get(i)); } return lstate.factory.Serialize(result, Geometry(line)); }); diff --git a/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp b/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp index 7c69852f..5ba87e92 100644 --- a/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_flipcoordinates.cpp @@ -155,7 +155,9 @@ static void BoxFlipCoordinatesFunction(DataChunk &args, ExpressionState &state, //------------------------------------------------------------------------------ static void FlipVertexVector(VertexVector &vertices) { for (idx_t i = 0; i < vertices.count; i++) { - std::swap(vertices[i].x, vertices[i].y); + auto vertex = vertices.Get(i); + std::swap(vertex.x, vertex.y); + vertices.Set(i, vertex); } } static void FlipGeometry(Point &point) { diff --git a/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp b/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp index 181fb921..52666278 100644 --- a/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_makepolygon.cpp @@ -35,7 +35,7 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state } auto &shell_verts = shell.Vertices(); - if (shell_verts[0] != shell_verts[shell_vert_count - 1]) { + if (!shell_verts.IsClosed()) { throw InvalidInputException( "ST_MakePolygon shell must be closed (first and last vertex must be equal)"); } @@ -70,7 +70,7 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state } auto &ring_verts = hole.Vertices(); - if (ring_verts[0] != ring_verts[hole_count - 1]) { + if (!ring_verts.IsClosed()) { throw InvalidInputException(StringUtil::Format( "ST_MakePolygon hole #%lu must be closed (first and last vertex must be equal)", hole_idx + 1)); } @@ -84,8 +84,9 @@ static void MakePolygonFromRingsFunction(DataChunk &args, ExpressionState &state for (idx_t ring_idx = 0; ring_idx < rings.size(); ring_idx++) { auto &new_ring = rings[ring_idx]; auto &poly_ring = polygon.Ring(ring_idx); - for (auto &v : new_ring.Vertices()) { - poly_ring.Add(v); + + for (auto i = 0; i < new_ring.Vertices().Count(); i++) { + poly_ring.Add(new_ring.Vertices().Get(i)); } } @@ -111,13 +112,13 @@ static void MakePolygonFromShellFunction(DataChunk &args, ExpressionState &state } auto &line_verts = line.Vertices(); - if (line_verts[0] != line_verts[line_count - 1]) { + if (!line_verts.IsClosed()) { throw InvalidInputException("ST_MakePolygon shell must be closed (first and last vertex must be equal)"); } auto polygon = lstate.factory.CreatePolygon(1, &line_count); - for (auto &v : line_verts) { - polygon.Shell().Add(v); + for (uint32_t i = 0; i < line_count; i++) { + polygon.Shell().Add(line_verts.Get(i)); } return lstate.factory.Serialize(result, Geometry(polygon)); diff --git a/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp b/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp index 752e27f8..88efd4ef 100644 --- a/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_perimeter.cpp @@ -76,8 +76,8 @@ static double PolygonPerimeter(const Polygon &poly) { double perimeter = 0; for (auto &ring : poly.Rings()) { for (uint32_t i = 0; i < ring.Count() - 1; i++) { - auto &v1 = ring[i]; - auto &v2 = ring[i + 1]; + auto v1 = ring.Get(i); + auto v2 = ring.Get(i + 1); perimeter += std::sqrt(std::pow(v1.x - v2.x, 2) + std::pow(v1.y - v2.y, 2)); } } diff --git a/spatial/src/spatial/core/functions/scalar/st_pointn.cpp b/spatial/src/spatial/core/functions/scalar/st_pointn.cpp index 0d35a2de..4bb8511f 100644 --- a/spatial/src/spatial/core/functions/scalar/st_pointn.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_pointn.cpp @@ -90,7 +90,7 @@ static void GeometryPointNFunction(DataChunk &args, ExpressionState &state, Vect } auto actual_index = index < 0 ? point_count + index : index - 1; - auto &point = line.Vertices()[actual_index]; + auto point = line.Vertices().Get(actual_index); return lstate.factory.Serialize(result, Geometry(lstate.factory.CreatePoint(point.x, point.y))); }); } diff --git a/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp b/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp index 722c21c6..2e960cad 100644 --- a/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp +++ b/spatial/src/spatial/core/functions/scalar/st_startpoint.cpp @@ -80,7 +80,7 @@ static void GeometryStartPointFunction(DataChunk &args, ExpressionState &state, return string_t(); } - auto &point = line.Vertices()[0]; + auto point = line.Vertices().Get(0); return lstate.factory.Serialize(result, Geometry(lstate.factory.CreatePoint(point.x, point.y))); }); } diff --git a/spatial/src/spatial/core/geometry/geometry.cpp b/spatial/src/spatial/core/geometry/geometry.cpp index 9d400ace..138f0449 100644 --- a/spatial/src/spatial/core/geometry/geometry.cpp +++ b/spatial/src/spatial/core/geometry/geometry.cpp @@ -33,28 +33,22 @@ string Point::ToString() const { if (IsEmpty()) { return "POINT EMPTY"; } - auto &vert = vertices[0]; + auto vert = vertices.Get(0); if (std::isnan(vert.x) && std::isnan(vert.y)) { // This is a special case for WKB. WKB does not support empty points, // and instead writes a point with NaN coordinates. We therefore need to // check for this case and return POINT EMPTY instead to round-trip safely return "POINT EMPTY"; } - auto x = vertices[0].x; - auto y = vertices[0].y; - return StringUtil::Format("POINT (%s)", Utils::format_coord(x, y)); + return StringUtil::Format("POINT (%s)", Utils::format_coord(vert.x, vert.y)); } bool Point::IsEmpty() const { return vertices.Count() == 0; } -Vertex &Point::GetVertex() { - return vertices[0]; -} - -const Vertex &Point::GetVertex() const { - return vertices[0]; +Vertex Point::GetVertex() const { + return vertices.Get(0); } Point::operator Geometry() const { @@ -85,8 +79,8 @@ string LineString::ToString() const { string result = "LINESTRING ("; for (uint32_t i = 0; i < vertices.Count(); i++) { - auto x = vertices[i].x; - auto y = vertices[i].y; + auto x = vertices.Get(i).x; + auto y = vertices.Get(i).y; result += Utils::format_coord(x, y); if (i < vertices.Count() - 1) { result += ", "; @@ -150,8 +144,8 @@ string Polygon::ToString() const { for (uint32_t i = 0; i < num_rings; i++) { result += "("; for (uint32_t j = 0; j < rings[i].Count(); j++) { - auto x = rings[i][j].x; - auto y = rings[i][j].y; + auto x = rings[i].Get(j).x; + auto y = rings[i].Get(j).y; result += Utils::format_coord(x, y); if (j < rings[i].Count() - 1) { result += ", "; @@ -187,7 +181,7 @@ string MultiPoint::ToString() const { if (points[i].IsEmpty()) { str += "EMPTY"; } else { - auto &vert = points[i].GetVertex(); + auto vert = points[i].GetVertex(); str += Utils::format_coord(vert.x, vert.y); } if (i < num_points - 1) { @@ -249,7 +243,8 @@ string MultiLineString::ToString() const { } str += "("; bool first_vert = true; - for (auto &vert : line.Vertices()) { + for (uint32_t i = 0; i < line.Vertices().Count(); i++) { + auto vert = line.Vertices().Get(i); if (first_vert) { first_vert = false; } else { @@ -330,7 +325,8 @@ string MultiPolygon::ToString() const { } str += "("; bool first_vert = true; - for (auto &vert : ring) { + for (uint32_t v = 0; v < ring.Count(); v++) { + auto vert = ring.Get(v); if (first_vert) { first_vert = false; } else { diff --git a/spatial/src/spatial/core/geometry/geometry_factory.cpp b/spatial/src/spatial/core/geometry/geometry_factory.cpp index 68372017..01c2dab7 100644 --- a/spatial/src/spatial/core/geometry/geometry_factory.cpp +++ b/spatial/src/spatial/core/geometry/geometry_factory.cpp @@ -33,7 +33,7 @@ data_ptr_t GeometryFactory::ToWKB(const Geometry &geometry, uint32_t *size) { } VertexVector GeometryFactory::AllocateVertexVector(uint32_t capacity) { - auto data = reinterpret_cast(allocator.AllocateAligned(sizeof(Vertex) * capacity)); + auto data = allocator.AllocateAligned(sizeof(Vertex) * capacity); return VertexVector(data, 0, capacity); } @@ -563,11 +563,11 @@ Point GeometryFactory::DeserializePoint(Cursor &reader) { // Points can be empty too, in which case the count is 0 auto count = reader.Read(); if (count == 0) { - VertexVector vertex_data((Vertex *)reader.GetPtr(), 0, 0); + VertexVector vertex_data(reader.GetPtr(), 0, 0); return Point(vertex_data); } else { D_ASSERT(count == 1); - VertexVector vertex_data((Vertex *)reader.GetPtr(), 1, 1); + VertexVector vertex_data(reader.GetPtr(), 1, 1); // Move the pointer forward (in case we are reading from a collection type) reader.Skip(sizeof(Vertex)); return Point(vertex_data); @@ -581,7 +581,7 @@ LineString GeometryFactory::DeserializeLineString(Cursor &reader) { // 0 if the linestring is empty auto count = reader.Read(); // read data - VertexVector vertex_data((Vertex *)reader.GetPtr(), count, count); + VertexVector vertex_data(reader.GetPtr(), count, count); reader.Skip(count * sizeof(Vertex)); @@ -601,7 +601,7 @@ Polygon GeometryFactory::DeserializePolygon(Cursor &reader) { auto data_ptr = reader.GetPtr() + sizeof(uint32_t) * num_rings + ((num_rings % 2) * sizeof(uint32_t)); for (uint32_t i = 0; i < num_rings; i++) { auto count = reader.Read(); - rings[i] = VertexVector((Vertex *)data_ptr, count, count); + rings[i] = VertexVector(data_ptr, count, count); data_ptr += count * sizeof(Vertex); } reader.SetPtr(data_ptr); @@ -696,7 +696,7 @@ GeometryCollection GeometryFactory::DeserializeGeometryCollection(Cursor &reader VertexVector GeometryFactory::CopyVertexVector(const VertexVector &vector) { auto result = VertexVector(vector); - result.data = (Vertex *)allocator.AllocateAligned(vector.capacity * sizeof(Vertex)); + result.data = allocator.AllocateAligned(vector.capacity * sizeof(Vertex)); memcpy(result.data, vector.data, vector.capacity * sizeof(Vertex)); return result; } diff --git a/spatial/src/spatial/core/geometry/vertex_vector.cpp b/spatial/src/spatial/core/geometry/vertex_vector.cpp index dd5e29a7..a8ab838f 100644 --- a/spatial/src/spatial/core/geometry/vertex_vector.cpp +++ b/spatial/src/spatial/core/geometry/vertex_vector.cpp @@ -34,7 +34,7 @@ void VertexVector::Serialize(Cursor &cursor) const { void VertexVector::SerializeAndUpdateBounds(Cursor &cursor, BoundingBox &bbox) const { for (idx_t i = 0; i < count; i++) { - auto &p = data[i]; + auto p = Get(i); bbox.minx = std::min(bbox.minx, p.x); bbox.miny = std::min(bbox.miny, p.y); bbox.maxx = std::max(bbox.maxx, p.x); @@ -50,8 +50,8 @@ double VertexVector::Length() const { return 0.0; } for (uint32_t i = 0; i < count - 1; i++) { - auto &p1 = data[i]; - auto &p2 = data[i + 1]; + auto p1 = Get(i); + auto p2 = Get(i + 1); length += std::sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); } return length; @@ -67,11 +67,11 @@ double VertexVector::SignedArea() const { // We don't need to do this for the y coordinate because we already // subtract them between consecutive vertices double area = 0; - auto x0 = data[0].x; + auto x0 = Get(0).x; for (uint32_t i = 1; i < count - 1; ++i) { - auto x1 = data[i].x; - auto y1 = data[i + 1].y; - auto y2 = data[i - 1].y; + auto x1 = Get(i).x; + auto y1 = Get(i + 1).y; + auto y2 = Get(i - 1).y; area += (x1 - x0) * (y2 - y1); } @@ -152,7 +152,7 @@ bool VertexVector::IsClosed() const { if (count == 1) { return true; } - return data[0] == data[count - 1]; + return Get(0) == Get(count - 1); } bool VertexVector::IsEmpty() const { @@ -177,8 +177,8 @@ bool VertexVector::IsSimple() const { Contains VertexVector::ContainsVertex(const Vertex &p, bool ensure_closed) const { - auto &p1 = data[0]; - auto &p2 = data[count - 1]; + auto p1 = Get(0); + auto p2 = Get(count - 1); if (ensure_closed && p1 != p2) { throw InternalException("VertexVector::Contains: VertexVector is not closed"); @@ -187,7 +187,7 @@ Contains VertexVector::ContainsVertex(const Vertex &p, bool ensure_closed) const int winding_number = 0; for (uint32_t i = 0; i < count; i++) { - p2 = data[i]; + p2 = Get(i); if (p1 == p2) { p1 = p2; continue; @@ -218,9 +218,9 @@ std::tuple VertexVector::ClosestSegment(const Vertex &p) const double min_distance = std::numeric_limits::max(); uint32_t min_index = 0; // Loop over all segments and find the closest one - auto &p1 = data[0]; + auto p1 = Get(0); for (uint32_t i = 1; i < count; i++) { - auto &p2 = data[i]; + auto p2 = Get(i); auto distance = p.DistanceSquared(p1, p2); if (distance < min_distance) { min_distance = distance; @@ -243,7 +243,7 @@ std::tuple VertexVector::ClosetVertex(const Vertex &p) const { // Loop over all segments and find the closest one for (uint32_t i = 0; i < count; i++) { - auto &p1 = data[i]; + auto p1 = Get(i); auto distance = p.DistanceSquared(p1); if (distance < min_distance) { @@ -264,18 +264,18 @@ std::tuple VertexVector::LocateVertex(const Vertex &p) c return std::make_tuple(Vertex(), 0, 0); } if (count == 1) { - auto single = data[0]; + auto single = Get(0); return std::make_tuple(single, 0, p.Distance(single)); } auto min_distance = std::numeric_limits::max(); uint32_t min_index = 0; - auto &p1 = data[0]; - auto &p2 = data[1]; + auto p1 = Get(0); + auto p2 = Get(1); // Search for the closest segment for (uint32_t i = 1; i < count; i++) { - p2 = data[i]; + p2 = Get(i); auto seg_distance = p.DistanceSquared(p1, p2); if (seg_distance < min_distance) { min_distance = seg_distance; @@ -300,8 +300,8 @@ std::tuple VertexVector::LocateVertex(const Vertex &p) c } auto Vertex_length = 0.0; for (uint32_t i = 0; i < min_index; i++) { - p1 = data[i]; - p2 = data[i + 1]; + p1 = Get(i); + p2 = Get(i + 1); Vertex_length += p1.Distance(p2); } diff --git a/spatial/src/spatial/core/geometry/wkb_writer.cpp b/spatial/src/spatial/core/geometry/wkb_writer.cpp index 3ccdd430..b1e24fcd 100644 --- a/spatial/src/spatial/core/geometry/wkb_writer.cpp +++ b/spatial/src/spatial/core/geometry/wkb_writer.cpp @@ -145,7 +145,7 @@ void WKBWriter::Write(const Point &point, data_ptr_t &ptr) { WriteDouble(x, ptr); WriteDouble(y, ptr); } else { - auto &vertex = point.GetVertex(); + auto vertex = point.GetVertex(); WriteDouble(vertex.x, ptr); WriteDouble(vertex.y, ptr); } @@ -157,7 +157,8 @@ void WKBWriter::Write(const LineString &line, data_ptr_t &ptr) { auto num_points = line.Count(); WriteInt(num_points, ptr); - for (auto &vertex : line.Vertices()) { + for (uint32_t i = 0; i < line.Vertices().Count(); i++) { + auto vertex = line.Vertices().Get(i); WriteDouble(vertex.x, ptr); WriteDouble(vertex.y, ptr); } @@ -170,7 +171,9 @@ void WKBWriter::Write(const Polygon &polygon, data_ptr_t &ptr) { WriteInt(polygon.Count(), ptr); for (auto &ring : polygon.Rings()) { WriteInt(ring.Count(), ptr); - for (auto &vertex : ring) { + + for (uint32_t i = 0; i < ring.Count(); i++) { + auto vertex = ring.Get(i); WriteDouble(vertex.x, ptr); WriteDouble(vertex.y, ptr); } diff --git a/spatial/src/spatial/core/optimizer_rules.cpp b/spatial/src/spatial/core/optimizer_rules.cpp index 16a37a73..837ab0f1 100644 --- a/spatial/src/spatial/core/optimizer_rules.cpp +++ b/spatial/src/spatial/core/optimizer_rules.cpp @@ -44,35 +44,32 @@ class RangeJoinSpatialPredicateRewriter : public OptimizerExtension { join->conditions.push_back(std::move(cmp)); } - - static bool IsTableRefsDisjoint(unordered_set &left_table_indexes, - unordered_set &right_table_indexes, - unordered_set &left_bindings, - unordered_set &right_bindings) { + static bool IsTableRefsDisjoint(unordered_set &left_table_indexes, unordered_set &right_table_indexes, + unordered_set &left_bindings, unordered_set &right_bindings) { // Check that all the left-side bindings reference the left-side tables of the join, // as well as that all the right-side bindings reference the right-side tables of the join. // and that the left and right side bindings are disjoint. - for(auto &left_binding : left_bindings) { - if(right_bindings.find(left_binding) != right_bindings.end()) { + for (auto &left_binding : left_bindings) { + if (right_bindings.find(left_binding) != right_bindings.end()) { // The left side bindings reference the right side tables of the join. return false; } // Also check that the left side bindings are on the left side of the join - if(left_table_indexes.find(left_binding) == left_table_indexes.end()) { + if (left_table_indexes.find(left_binding) == left_table_indexes.end()) { // The left side bindings are not on the left side of the join. return false; } } - for(auto &right_binding : right_bindings) { - if(left_bindings.find(right_binding) != left_bindings.end()) { + for (auto &right_binding : right_bindings) { + if (left_bindings.find(right_binding) != left_bindings.end()) { // The right side bindings reference the left side tables of the join. return false; } // Also check that the right side bindings are on the right side of the join - if(right_table_indexes.find(right_binding) == right_table_indexes.end()) { + if (right_table_indexes.find(right_binding) == right_table_indexes.end()) { // The right side bindings are not on the right side of the join. return false; } @@ -128,14 +125,16 @@ class RangeJoinSpatialPredicateRewriter : public OptimizerExtension { // a JOIN b ON st_intersects(a.geom, b.geom) => OK // a JOIN b ON st_intersects(b.geom, a.geom) => OK // a JOIN b ON st_intersects(a.geom, st_union(a.geom, b.geom)) => NOT OK - auto can_split = IsTableRefsDisjoint(left_table_indexes, right_table_indexes, left_pred_bindings, right_pred_bindings); - if(!can_split) { + auto can_split = IsTableRefsDisjoint(left_table_indexes, right_table_indexes, left_pred_bindings, + right_pred_bindings); + if (!can_split) { // Try again with the left and right side of the predicate swapped // We can safely swap because the intersection operation we encode with the comparison join // is symmetric, so the order of the arguments wont matter in the "new" join condition we're // about to create. - can_split = IsTableRefsDisjoint(left_table_indexes, right_table_indexes, right_pred_bindings, left_pred_bindings); - if(!can_split) { + can_split = IsTableRefsDisjoint(left_table_indexes, right_table_indexes, right_pred_bindings, + left_pred_bindings); + if (!can_split) { // We cant optimize this join return; } @@ -145,35 +144,31 @@ class RangeJoinSpatialPredicateRewriter : public OptimizerExtension { // Lookup the st_xmin, st_xmax, st_ymin, st_ymax functions in the catalog auto &catalog = Catalog::GetSystemCatalog(context); - auto &xmin_func_set = catalog.GetEntry(context, CatalogType::SCALAR_FUNCTION_ENTRY, DEFAULT_SCHEMA, "st_xmin") - .Cast(); - auto &xmax_func_set = catalog.GetEntry(context, CatalogType::SCALAR_FUNCTION_ENTRY, DEFAULT_SCHEMA, "st_xmax") - .Cast(); - auto &ymin_func_set = catalog.GetEntry(context, CatalogType::SCALAR_FUNCTION_ENTRY, DEFAULT_SCHEMA, "st_ymin") - .Cast(); - auto &ymax_func_set = catalog.GetEntry(context, CatalogType::SCALAR_FUNCTION_ENTRY, DEFAULT_SCHEMA, "st_ymax") - .Cast(); + auto &xmin_func_set = + catalog.GetEntry(context, CatalogType::SCALAR_FUNCTION_ENTRY, DEFAULT_SCHEMA, "st_xmin") + .Cast(); + auto &xmax_func_set = + catalog.GetEntry(context, CatalogType::SCALAR_FUNCTION_ENTRY, DEFAULT_SCHEMA, "st_xmax") + .Cast(); + auto &ymin_func_set = + catalog.GetEntry(context, CatalogType::SCALAR_FUNCTION_ENTRY, DEFAULT_SCHEMA, "st_ymin") + .Cast(); + auto &ymax_func_set = + catalog.GetEntry(context, CatalogType::SCALAR_FUNCTION_ENTRY, DEFAULT_SCHEMA, "st_ymax") + .Cast(); auto &left_arg_type = left_pred_expr->return_type; auto &right_arg_type = right_pred_expr->return_type; - auto xmin_func_left = - xmin_func_set.functions.GetFunctionByArguments(context, {left_arg_type}); - auto xmax_func_left = - xmax_func_set.functions.GetFunctionByArguments(context, {left_arg_type}); - auto ymin_func_left = - ymin_func_set.functions.GetFunctionByArguments(context, {left_arg_type}); - auto ymax_func_left = - ymax_func_set.functions.GetFunctionByArguments(context, {left_arg_type}); - - auto xmin_func_right = - xmin_func_set.functions.GetFunctionByArguments(context, {right_arg_type}); - auto xmax_func_right = - xmax_func_set.functions.GetFunctionByArguments(context, {right_arg_type}); - auto ymin_func_right = - ymin_func_set.functions.GetFunctionByArguments(context, {right_arg_type}); - auto ymax_func_right = - ymax_func_set.functions.GetFunctionByArguments(context, {right_arg_type}); + auto xmin_func_left = xmin_func_set.functions.GetFunctionByArguments(context, {left_arg_type}); + auto xmax_func_left = xmax_func_set.functions.GetFunctionByArguments(context, {left_arg_type}); + auto ymin_func_left = ymin_func_set.functions.GetFunctionByArguments(context, {left_arg_type}); + auto ymax_func_left = ymax_func_set.functions.GetFunctionByArguments(context, {left_arg_type}); + + auto xmin_func_right = xmin_func_set.functions.GetFunctionByArguments(context, {right_arg_type}); + auto xmax_func_right = xmax_func_set.functions.GetFunctionByArguments(context, {right_arg_type}); + auto ymin_func_right = ymin_func_set.functions.GetFunctionByArguments(context, {right_arg_type}); + auto ymax_func_right = ymax_func_set.functions.GetFunctionByArguments(context, {right_arg_type}); // Create the new join condition @@ -231,7 +226,7 @@ class RangeJoinSpatialPredicateRewriter : public OptimizerExtension { ExpressionType::COMPARE_GREATERTHANOREQUALTO); new_join->children = std::move(any_join.children); - if(any_join.has_estimated_cardinality) { + if (any_join.has_estimated_cardinality) { new_join->estimated_cardinality = any_join.estimated_cardinality; new_join->has_estimated_cardinality = true; } diff --git a/spatial/src/spatial/gdal/file_handler.cpp b/spatial/src/spatial/gdal/file_handler.cpp index fbb68d5a..318e04cf 100644 --- a/spatial/src/spatial/gdal/file_handler.cpp +++ b/spatial/src/spatial/gdal/file_handler.cpp @@ -213,7 +213,6 @@ static int DuckDBStatCallback(void *userData, const char *filename, VSIStatBufL throw IOException("Unknown file type"); } - /* DuckDB doesnt have anything equivalent to these yet... Hopefully thats ok? pstatbuf->st_mtime = file->GetLastModifiedTime(); pstatbuf->st_uid = file->GetFileOwner(); diff --git a/spatial/src/spatial/gdal/functions/st_write.cpp b/spatial/src/spatial/gdal/functions/st_write.cpp index b28b1e81..266a52a0 100644 --- a/spatial/src/spatial/gdal/functions/st_write.cpp +++ b/spatial/src/spatial/gdal/functions/st_write.cpp @@ -57,10 +57,10 @@ struct GlobalState : public GlobalFunctionData { //===--------------------------------------------------------------------===// // The parameters are const in duckdb > 0.9.1, ifdef so we can build for both versions for now. #if DUCKDB_PATCH_VERSION == 1 -static unique_ptr Bind(ClientContext &context, CopyInfo &info, vector &names, +static unique_ptr Bind(ClientContext &context, CopyInfo &info, vector &names, vector &sql_types) { #else -static unique_ptr Bind(ClientContext &context, const CopyInfo &info, const vector &names, +static unique_ptr Bind(ClientContext &context, const CopyInfo &info, const vector &names, const vector &sql_types) { #endif GdalFileHandler::SetLocalClientContext(context); diff --git a/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp b/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp index 71bd2556..01f02edc 100644 --- a/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp +++ b/spatial/src/spatial/geographiclib/functions/st_area_spheroid.cpp @@ -85,7 +85,7 @@ static double PolygonArea(const core::Polygon &poly, GeographicLib::PolygonArea auto &ring = poly.Ring(ring_idx); // Note: the last point is the same as the first point, but geographiclib doesn't know that, for (uint32_t coord_idx = 0; coord_idx < ring.Count() - 1; coord_idx++) { - auto &coord = ring[coord_idx]; + auto coord = ring.Get(coord_idx); comp.AddPoint(coord.x, coord.y); } double ring_area; diff --git a/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp b/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp index 9b71ca18..9d09a32f 100644 --- a/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp +++ b/spatial/src/spatial/geographiclib/functions/st_length_spheroid.cpp @@ -59,7 +59,8 @@ static void GeodesicLineString2DFunction(DataChunk &args, ExpressionState &state //------------------------------------------------------------------------------ static double LineLength(const core::LineString &line, GeographicLib::PolygonArea &comp) { comp.Clear(); - for (auto &vert : line.Vertices()) { + for (uint32_t i = 0; i < line.Vertices().Count(); i++) { + auto vert = line.Vertices().Get(i); comp.AddPoint(vert.x, vert.y); } double _area; diff --git a/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp b/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp index 613a9f35..71c62b18 100644 --- a/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp +++ b/spatial/src/spatial/geographiclib/functions/st_perimeter_spheroid.cpp @@ -77,7 +77,7 @@ static double PolygonPerimeter(const core::Polygon &poly, GeographicLib::Polygon // Note: the last point is the same as the first point, but geographiclib doesn't know that, // so skip it. for (uint32_t coord_idx = 0; coord_idx < ring.Count() - 1; coord_idx++) { - auto &coord = ring[coord_idx]; + auto coord = ring.Get(coord_idx); comp.AddPoint(coord.x, coord.y); } double _ring_area; diff --git a/spatial/src/spatial/geos/functions/cast.cpp b/spatial/src/spatial/geos/functions/cast.cpp index ff4d9ab5..e6d5cb88 100644 --- a/spatial/src/spatial/geos/functions/cast.cpp +++ b/spatial/src/spatial/geos/functions/cast.cpp @@ -72,7 +72,8 @@ void GeosCastFunctions::Register(DatabaseInstance &db) { ExtensionUtil::RegisterCastFunction(db, core::GeoTypes::GEOMETRY(), LogicalType::VARCHAR, BoundCastInfo(GeometryToTextCast, nullptr, GEOSFunctionLocalState::InitCast)); - ExtensionUtil::RegisterCastFunction(db, LogicalType::VARCHAR, core::GeoTypes::GEOMETRY(), BoundCastInfo(TextToGeometryCast, nullptr, GEOSFunctionLocalState::InitCast)); + ExtensionUtil::RegisterCastFunction(db, LogicalType::VARCHAR, core::GeoTypes::GEOMETRY(), + BoundCastInfo(TextToGeometryCast, nullptr, GEOSFunctionLocalState::InitCast)); }; } // namespace geos diff --git a/spatial/src/spatial/geos/functions/scalar/st_geomfromtext.cpp b/spatial/src/spatial/geos/functions/scalar/st_geomfromtext.cpp index 61f32825..c27aaa91 100644 --- a/spatial/src/spatial/geos/functions/scalar/st_geomfromtext.cpp +++ b/spatial/src/spatial/geos/functions/scalar/st_geomfromtext.cpp @@ -47,21 +47,20 @@ static void GeometryFromWKTFunction(DataChunk &args, ExpressionState &state, Vec UnaryExecutor::ExecuteWithNulls( input, result, count, [&](string_t &wkt, ValidityMask &mask, idx_t idx) { - try { - auto geos_geom = reader.Read(wkt); - auto multidimensional = (GEOSHasZ_r(lstate.ctx.GetCtx(), geos_geom.get()) == 1); - if (multidimensional) { - throw InvalidInputException("3D/4D geometries are not supported"); - } - return lstate.ctx.Serialize(result, geos_geom); - } - catch (InvalidInputException &error) { - if(!info.ignore_invalid) { - throw; - } - mask.SetInvalid(idx); - return string_t(); - } + try { + auto geos_geom = reader.Read(wkt); + auto multidimensional = (GEOSHasZ_r(lstate.ctx.GetCtx(), geos_geom.get()) == 1); + if (multidimensional) { + throw InvalidInputException("3D/4D geometries are not supported"); + } + return lstate.ctx.Serialize(result, geos_geom); + } catch (InvalidInputException &error) { + if (!info.ignore_invalid) { + throw; + } + mask.SetInvalid(idx); + return string_t(); + } }); } diff --git a/spatial/src/spatial/geos/functions/scalar/st_linemerge.cpp b/spatial/src/spatial/geos/functions/scalar/st_linemerge.cpp index 31e3d354..4bace476 100644 --- a/spatial/src/spatial/geos/functions/scalar/st_linemerge.cpp +++ b/spatial/src/spatial/geos/functions/scalar/st_linemerge.cpp @@ -27,21 +27,25 @@ static void LineMergeFunction(DataChunk &args, ExpressionState &state, Vector &r static void LineMergeFunctionWithDirected(DataChunk &args, ExpressionState &state, Vector &result) { auto &lstate = GEOSFunctionLocalState::ResetAndGet(state); auto &ctx = lstate.ctx.GetCtx(); - BinaryExecutor::Execute(args.data[0], args.data[1], result, args.size(), [&](string_t &geometry_blob, bool directed) { - auto geometry = lstate.ctx.Deserialize(geometry_blob); - auto convex_hull_geometry = directed ? make_uniq_geos(ctx, GEOSLineMergeDirected_r(ctx, geometry.get())) - : make_uniq_geos(ctx, GEOSLineMerge_r(ctx, geometry.get())); - - return lstate.ctx.Serialize(result, convex_hull_geometry); - }); + BinaryExecutor::Execute( + args.data[0], args.data[1], result, args.size(), [&](string_t &geometry_blob, bool directed) { + auto geometry = lstate.ctx.Deserialize(geometry_blob); + auto convex_hull_geometry = directed ? make_uniq_geos(ctx, GEOSLineMergeDirected_r(ctx, geometry.get())) + : make_uniq_geos(ctx, GEOSLineMerge_r(ctx, geometry.get())); + + return lstate.ctx.Serialize(result, convex_hull_geometry); + }); } void GEOSScalarFunctions::RegisterStLineMerge(DatabaseInstance &db) { ScalarFunctionSet set("ST_LineMerge"); - set.AddFunction(ScalarFunction({GeoTypes::GEOMETRY()}, GeoTypes::GEOMETRY(), LineMergeFunction, nullptr, nullptr, nullptr, GEOSFunctionLocalState::Init)); - set.AddFunction(ScalarFunction({GeoTypes::GEOMETRY(), LogicalType::BOOLEAN}, GeoTypes::GEOMETRY(), LineMergeFunctionWithDirected, nullptr, nullptr, nullptr, GEOSFunctionLocalState::Init)); + set.AddFunction(ScalarFunction({GeoTypes::GEOMETRY()}, GeoTypes::GEOMETRY(), LineMergeFunction, nullptr, nullptr, + nullptr, GEOSFunctionLocalState::Init)); + set.AddFunction(ScalarFunction({GeoTypes::GEOMETRY(), LogicalType::BOOLEAN}, GeoTypes::GEOMETRY(), + LineMergeFunctionWithDirected, nullptr, nullptr, nullptr, + GEOSFunctionLocalState::Init)); ExtensionUtil::RegisterFunction(db, set); } diff --git a/spatial/src/spatial/proj/functions.cpp b/spatial/src/spatial/proj/functions.cpp index 5900cdcd..db4c2206 100644 --- a/spatial/src/spatial/proj/functions.cpp +++ b/spatial/src/spatial/proj/functions.cpp @@ -234,26 +234,29 @@ static void TransformGeometry(PJ *crs, core::Point &point) { if (point.IsEmpty()) { return; } - auto &vertex = point.GetVertex(); + auto vertex = point.GetVertex(); auto transformed = proj_trans(crs, PJ_FWD, proj_coord(vertex.x, vertex.y, 0, 0)).xy; - vertex.x = transformed.x; - vertex.y = transformed.y; + point.Vertices().Set(0, core::Vertex(transformed.x, transformed.y)); } static void TransformGeometry(PJ *crs, core::LineString &line) { - for (auto &vert : line.Vertices()) { + + for (uint32_t i = 0; i < line.Vertices().Count(); i++) { + auto vert = line.Vertices().Get(i); auto transformed = proj_trans(crs, PJ_FWD, proj_coord(vert.x, vert.y, 0, 0)).xy; - vert.x = transformed.x; - vert.y = transformed.y; + + core::Vertex new_vert(transformed.x, transformed.y); + line.Vertices().Set(i, new_vert); } } static void TransformGeometry(PJ *crs, core::Polygon &poly) { for (auto &ring : poly.Rings()) { - for (auto &vert : ring) { + for (uint32_t i = 0; i < ring.Count(); i++) { + auto vert = ring.Get(i); auto transformed = proj_trans(crs, PJ_FWD, proj_coord(vert.x, vert.y, 0, 0)).xy; - vert.x = transformed.x; - vert.y = transformed.y; + core::Vertex new_vert(transformed.x, transformed.y); + ring.Set(i, new_vert); } } }