1 /******************************************************************************
2  *
3  * Project:  FlatGeobuf driver
4  * Purpose:  Implements GeometryWriter class.
5  * Author:   Björn Harrtell <bjorn at wololo dot org>
6  *
7  ******************************************************************************
8  * Copyright (c) 2018-2019, Björn Harrtell <bjorn at wololo dot org>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include "geometrywriter.h"
30 
31 using namespace flatbuffers;
32 using namespace FlatGeobuf;
33 using namespace ogr_flatgeobuf;
34 
translateOGRwkbGeometryType(const OGRwkbGeometryType eGType)35 GeometryType GeometryWriter::translateOGRwkbGeometryType(const OGRwkbGeometryType eGType)
36 {
37     const auto flatType = wkbFlatten(eGType);
38     GeometryType geometryType = GeometryType::Unknown;
39     if (flatType <= 17)
40         geometryType = (GeometryType) flatType;
41     return geometryType;
42 }
43 
writePoint(const OGRPoint * p)44 void GeometryWriter::writePoint(const OGRPoint *p)
45 {
46     m_xy.push_back(p->getX());
47     m_xy.push_back(p->getY());
48     if (m_hasZ)
49         m_z.push_back(p->getZ());
50     if (m_hasM)
51         m_m.push_back(p->getM());
52 }
53 
writeMultiPoint(const OGRMultiPoint * mp)54 void GeometryWriter::writeMultiPoint(const OGRMultiPoint *mp)
55 {
56     for (const auto part: *mp )
57     {
58         if( !part->IsEmpty() )
59         {
60             writePoint(part);
61         }
62     }
63 }
64 
writeSimpleCurve(const OGRSimpleCurve * sc)65 uint32_t GeometryWriter::writeSimpleCurve(const OGRSimpleCurve *sc)
66 {
67     uint32_t numPoints = sc->getNumPoints();
68     const auto xyLength = m_xy.size();
69     m_xy.resize(xyLength + (numPoints * 2));
70     const auto zLength = m_z.size();
71     double *padfZOut = nullptr;
72     if (m_hasZ) {
73         m_z.resize(zLength + numPoints);
74         padfZOut = m_z.data() + zLength;
75     }
76     const auto mLength = m_m.size();
77     double *padfMOut = nullptr;
78     if (m_hasM) {
79         m_m.resize(mLength + numPoints);
80         padfMOut = m_m.data() + mLength;
81     }
82     sc->getPoints(reinterpret_cast<double*>(reinterpret_cast<OGRRawPoint *>(m_xy.data() + xyLength)), sizeof(OGRRawPoint),
83                   reinterpret_cast<double*>(reinterpret_cast<OGRRawPoint *>(m_xy.data() + xyLength)) + 1, sizeof(OGRRawPoint),
84                   padfZOut, sizeof(double),
85                   padfMOut, sizeof(double));
86     return numPoints;
87 }
88 
writeMultiLineString(const OGRMultiLineString * mls)89 void GeometryWriter::writeMultiLineString(const OGRMultiLineString *mls)
90 {
91     uint32_t e = 0;
92     for (const auto part: *mls)
93     {
94         if( !part->IsEmpty() )
95         {
96             m_ends.push_back(e += writeSimpleCurve(part));
97         }
98     }
99 }
100 
writePolygon(const OGRPolygon * p)101 void GeometryWriter::writePolygon(const OGRPolygon *p)
102 {
103     const auto exteriorRing = p->getExteriorRing();
104     const auto numInteriorRings = p->getNumInteriorRings();
105     uint32_t e = writeSimpleCurve(exteriorRing);
106     // NOTE: do not have to write ends if only exterior ring
107     if (numInteriorRings > 0) {
108         m_ends.push_back(e);
109         for (int i = 0; i < numInteriorRings; i++)
110             m_ends.push_back(e += writeSimpleCurve(p->getInteriorRing(i)));
111     }
112 }
113 
writeMultiPolygon(const OGRMultiPolygon * mp,int depth)114 const Offset<Geometry> GeometryWriter::writeMultiPolygon(const OGRMultiPolygon *mp, int depth)
115 {
116     std::vector<Offset<Geometry>> parts;
117     for (const auto part: *mp)
118     {
119         if( !part->IsEmpty() )
120         {
121             GeometryWriter writer { m_fbb, part, GeometryType::Polygon, m_hasZ, m_hasM };
122             parts.push_back(writer.write(depth + 1));
123         }
124     }
125     return CreateGeometryDirect(m_fbb, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, m_geometryType, &parts);
126 }
127 
writeGeometryCollection(const OGRGeometryCollection * gc,int depth)128 const Offset<Geometry> GeometryWriter::writeGeometryCollection(const OGRGeometryCollection *gc, int depth)
129 {
130     std::vector<Offset<Geometry>> parts;
131     for (const auto part: *gc)
132     {
133         if( !part->IsEmpty() )
134         {
135             GeometryWriter writer { m_fbb, part, m_hasZ, m_hasM };
136             parts.push_back(writer.write(depth + 1));
137         }
138     }
139     return CreateGeometryDirect(m_fbb, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, m_geometryType, &parts);
140 }
141 
writeCompoundCurve(const OGRCompoundCurve * cc,int depth)142 const Offset<Geometry> GeometryWriter::writeCompoundCurve(const OGRCompoundCurve *cc, int depth)
143 {
144     std::vector<Offset<Geometry>> parts;
145     for (const auto curve : *cc) {
146         GeometryWriter writer { m_fbb, curve, m_hasZ, m_hasM };
147         parts.push_back(writer.write(depth + 1));
148     }
149     return CreateGeometryDirect(m_fbb, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, m_geometryType, &parts);
150 }
151 
writeCurvePolygon(const OGRCurvePolygon * cp,int depth)152 const Offset<Geometry> GeometryWriter::writeCurvePolygon(const OGRCurvePolygon *cp, int depth)
153 {
154     std::vector<Offset<Geometry>> parts;
155     for (const auto curve : *cp) {
156         GeometryWriter writer { m_fbb, curve, m_hasZ, m_hasM };
157         parts.push_back(writer.write(depth + 1));
158     }
159     return CreateGeometryDirect(m_fbb, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, m_geometryType, &parts);
160 }
161 
writePolyhedralSurface(const OGRPolyhedralSurface * p,int depth)162 const Offset<Geometry> GeometryWriter::writePolyhedralSurface(const OGRPolyhedralSurface *p, int depth)
163 {
164     std::vector<Offset<Geometry>> parts;
165     for (const auto part: *p) {
166         GeometryWriter writer { m_fbb, part, m_hasZ, m_hasM };
167         parts.push_back(writer.write(depth + 1));
168     }
169     return CreateGeometryDirect(m_fbb, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, m_geometryType, &parts);
170 }
171 
writeTIN(const OGRTriangulatedSurface * ts)172 void GeometryWriter::writeTIN(const OGRTriangulatedSurface *ts)
173 {
174     const auto numGeometries = ts->getNumGeometries();
175     if (numGeometries == 1) {
176         const auto lr = ts->getGeometryRef(0)->getExteriorRing();
177         writeSimpleCurve(lr);
178         return;
179     }
180     uint32_t e = 0;
181     for (const auto part: *ts) {
182         const auto lr = part->getExteriorRing();
183         m_ends.push_back(e += writeSimpleCurve(lr));
184     }
185 }
186 
write(int depth)187 const Offset<Geometry> GeometryWriter::write(int depth)
188 {
189     bool unknownGeometryType = false;
190     if (depth == 0 && m_geometryType == GeometryType::Unknown) {
191         m_geometryType = translateOGRwkbGeometryType(m_ogrGeometry->getGeometryType());
192         unknownGeometryType = true;
193     }
194     switch (m_geometryType) {
195         case GeometryType::Point:
196             writePoint(m_ogrGeometry->toPoint()); break;
197         case GeometryType::MultiPoint:
198             writeMultiPoint(m_ogrGeometry->toMultiPoint()); break;
199         case GeometryType::LineString:
200             writeSimpleCurve(m_ogrGeometry->toLineString()); break;
201         case GeometryType::MultiLineString:
202             writeMultiLineString(m_ogrGeometry->toMultiLineString()); break;
203         case GeometryType::Polygon:
204             writePolygon(m_ogrGeometry->toPolygon()); break;
205         case GeometryType::MultiPolygon:
206             return writeMultiPolygon(m_ogrGeometry->toMultiPolygon(), depth);
207         case GeometryType::GeometryCollection:
208             return writeGeometryCollection(m_ogrGeometry->toGeometryCollection(), depth);
209         case GeometryType::CircularString:
210             writeSimpleCurve(m_ogrGeometry->toCircularString()); break;
211         case GeometryType::CompoundCurve:
212             return writeCompoundCurve(m_ogrGeometry->toCompoundCurve(), depth);
213         case GeometryType::CurvePolygon:
214             return writeCurvePolygon(m_ogrGeometry->toCurvePolygon(), depth);
215         case GeometryType::MultiCurve:
216             return writeGeometryCollection(m_ogrGeometry->toMultiCurve(), depth);
217         case GeometryType::MultiSurface:
218             return writeGeometryCollection(m_ogrGeometry->toMultiSurface(), depth);
219         case GeometryType::PolyhedralSurface:
220             return writePolyhedralSurface(m_ogrGeometry->toPolyhedralSurface(), depth);
221         case GeometryType::Triangle:
222             writePolygon(m_ogrGeometry->toTriangle()); break;
223         case GeometryType::TIN:
224             writeTIN(m_ogrGeometry->toTriangulatedSurface()); break;
225         default:
226             CPLError(CE_Failure, CPLE_AppDefined, "GeometryWriter::write: Unknown type %d", (int) m_geometryType);
227             return 0;
228     }
229     const auto pEnds = m_ends.empty() ? nullptr : &m_ends;
230     const auto pXy = m_xy.empty() ? nullptr : &m_xy;
231     const auto pZ = m_z.empty() ? nullptr : &m_z;
232     const auto pM = m_m.empty() ? nullptr : &m_m;
233     const auto geometryType = depth > 0 || unknownGeometryType ? m_geometryType : GeometryType::Unknown;
234     return FlatGeobuf::CreateGeometryDirect(m_fbb, pEnds, pXy, pZ, pM, nullptr, nullptr, geometryType);
235 }
236