/src/gdal/ogr/ogrmultipoint.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OpenGIS Simple Features Reference Implementation |
4 | | * Purpose: The OGRMultiPoint class. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 1999, Frank Warmerdam |
9 | | * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "ogr_geometry.h" |
16 | | |
17 | | #include <cstddef> |
18 | | #include <cstdio> |
19 | | #include <cstring> |
20 | | #include <new> |
21 | | |
22 | | #include "cpl_conv.h" |
23 | | #include "cpl_error.h" |
24 | | #include "cpl_vsi.h" |
25 | | #include "ogr_core.h" |
26 | | #include "ogr_p.h" |
27 | | |
28 | | /************************************************************************/ |
29 | | /* OGRMultiPoint( const OGRMultiPoint& ) */ |
30 | | /************************************************************************/ |
31 | | |
32 | | /** |
33 | | * \brief Copy constructor. |
34 | | */ |
35 | | |
36 | 0 | OGRMultiPoint::OGRMultiPoint(const OGRMultiPoint &) = default; |
37 | | |
38 | | /************************************************************************/ |
39 | | /* operator=( const OGRMultiPoint&) */ |
40 | | /************************************************************************/ |
41 | | |
42 | | /** |
43 | | * \brief Assignment operator. |
44 | | */ |
45 | | |
46 | | OGRMultiPoint &OGRMultiPoint::operator=(const OGRMultiPoint &other) |
47 | 0 | { |
48 | 0 | if (this != &other) |
49 | 0 | { |
50 | 0 | OGRGeometryCollection::operator=(other); |
51 | 0 | } |
52 | 0 | return *this; |
53 | 0 | } |
54 | | |
55 | | /************************************************************************/ |
56 | | /* clone() */ |
57 | | /************************************************************************/ |
58 | | |
59 | | OGRMultiPoint *OGRMultiPoint::clone() const |
60 | | |
61 | 0 | { |
62 | 0 | return new (std::nothrow) OGRMultiPoint(*this); |
63 | 0 | } |
64 | | |
65 | | /************************************************************************/ |
66 | | /* getGeometryType() */ |
67 | | /************************************************************************/ |
68 | | |
69 | | OGRwkbGeometryType OGRMultiPoint::getGeometryType() const |
70 | | |
71 | 4.25k | { |
72 | 4.25k | if ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED)) |
73 | 1.69k | return wkbMultiPointZM; |
74 | 2.55k | else if (flags & OGR_G_MEASURED) |
75 | 6 | return wkbMultiPointM; |
76 | 2.54k | else if (flags & OGR_G_3D) |
77 | 2.14k | return wkbMultiPoint25D; |
78 | 408 | else |
79 | 408 | return wkbMultiPoint; |
80 | 4.25k | } |
81 | | |
82 | | /************************************************************************/ |
83 | | /* getDimension() */ |
84 | | /************************************************************************/ |
85 | | |
86 | | int OGRMultiPoint::getDimension() const |
87 | | |
88 | 0 | { |
89 | 0 | return 0; |
90 | 0 | } |
91 | | |
92 | | /************************************************************************/ |
93 | | /* getGeometryName() */ |
94 | | /************************************************************************/ |
95 | | |
96 | | const char *OGRMultiPoint::getGeometryName() const |
97 | | |
98 | 650 | { |
99 | 650 | return "MULTIPOINT"; |
100 | 650 | } |
101 | | |
102 | | /************************************************************************/ |
103 | | /* isCompatibleSubType() */ |
104 | | /************************************************************************/ |
105 | | |
106 | | OGRBoolean |
107 | | OGRMultiPoint::isCompatibleSubType(OGRwkbGeometryType eGeomType) const |
108 | 2.57k | { |
109 | 2.57k | return wkbFlatten(eGeomType) == wkbPoint; |
110 | 2.57k | } |
111 | | |
112 | | /************************************************************************/ |
113 | | /* exportToWkt() */ |
114 | | /* */ |
115 | | /* Translate this structure into its well known text format */ |
116 | | /* equivalent. */ |
117 | | /************************************************************************/ |
118 | | |
119 | | std::string OGRMultiPoint::exportToWkt(const OGRWktOptions &opts, |
120 | | OGRErr *err) const |
121 | 0 | { |
122 | 0 | try |
123 | 0 | { |
124 | 0 | std::string wkt = getGeometryName(); |
125 | 0 | wkt += wktTypeString(opts.variant); |
126 | |
|
127 | 0 | bool first(true); |
128 | | // OGRMultiPoint has a begin()/end(). |
129 | 0 | for (const OGRPoint *poPoint : this) |
130 | 0 | { |
131 | 0 | if (poPoint->IsEmpty()) |
132 | 0 | continue; |
133 | | |
134 | 0 | if (first) |
135 | 0 | wkt += '('; |
136 | 0 | else |
137 | 0 | wkt += ','; |
138 | 0 | first = false; |
139 | |
|
140 | 0 | if (opts.variant == wkbVariantIso) |
141 | 0 | wkt += '('; |
142 | |
|
143 | 0 | wkt += OGRMakeWktCoordinateM( |
144 | 0 | poPoint->getX(), poPoint->getY(), poPoint->getZ(), |
145 | 0 | poPoint->getM(), poPoint->Is3D(), |
146 | 0 | poPoint->IsMeasured() && (opts.variant == wkbVariantIso), opts); |
147 | |
|
148 | 0 | if (opts.variant == wkbVariantIso) |
149 | 0 | wkt += ')'; |
150 | 0 | } |
151 | |
|
152 | 0 | if (err) |
153 | 0 | *err = OGRERR_NONE; |
154 | 0 | if (first) |
155 | 0 | wkt += "EMPTY"; |
156 | 0 | else |
157 | 0 | wkt += ')'; |
158 | 0 | return wkt; |
159 | 0 | } |
160 | 0 | catch (const std::bad_alloc &e) |
161 | 0 | { |
162 | 0 | CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what()); |
163 | 0 | if (err) |
164 | 0 | *err = OGRERR_FAILURE; |
165 | 0 | return std::string(); |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | | /************************************************************************/ |
170 | | /* importFromWkt() */ |
171 | | /************************************************************************/ |
172 | | |
173 | | OGRErr OGRMultiPoint::importFromWkt(const char **ppszInput) |
174 | | |
175 | 650 | { |
176 | 650 | const char *pszInputBefore = *ppszInput; |
177 | 650 | int bHasZ = FALSE; |
178 | 650 | int bHasM = FALSE; |
179 | 650 | bool bIsEmpty = false; |
180 | 650 | OGRErr eErr = importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty); |
181 | 650 | flags = 0; |
182 | 650 | if (eErr != OGRERR_NONE) |
183 | 3 | return eErr; |
184 | 647 | if (bHasZ) |
185 | 199 | flags |= OGR_G_3D; |
186 | 647 | if (bHasM) |
187 | 11 | flags |= OGR_G_MEASURED; |
188 | 647 | if (bIsEmpty) |
189 | 2 | return OGRERR_NONE; |
190 | | |
191 | 645 | char szToken[OGR_WKT_TOKEN_MAX] = {}; |
192 | 645 | const char *pszInput = *ppszInput; |
193 | | |
194 | 645 | const char *pszPreScan = OGRWktReadToken(pszInput, szToken); |
195 | 645 | OGRWktReadToken(pszPreScan, szToken); |
196 | | |
197 | | // Do we have an inner bracket? |
198 | 645 | if (EQUAL(szToken, "(") || EQUAL(szToken, "EMPTY")) |
199 | 13 | { |
200 | 13 | *ppszInput = pszInputBefore; |
201 | 13 | return importFromWkt_Bracketed(ppszInput, bHasM, bHasZ); |
202 | 13 | } |
203 | | |
204 | | /* -------------------------------------------------------------------- */ |
205 | | /* Read the point list. */ |
206 | | /* -------------------------------------------------------------------- */ |
207 | 632 | OGRRawPoint *paoPoints = nullptr; |
208 | 632 | double *padfZ = nullptr; |
209 | 632 | double *padfM = nullptr; |
210 | 632 | int flagsFromInput = flags; |
211 | 632 | int nMaxPoint = 0; |
212 | 632 | int nPointCount = 0; |
213 | | |
214 | 632 | pszInput = OGRWktReadPointsM(pszInput, &paoPoints, &padfZ, &padfM, |
215 | 632 | &flagsFromInput, &nMaxPoint, &nPointCount); |
216 | 632 | if (pszInput == nullptr) |
217 | 18 | { |
218 | 18 | CPLFree(paoPoints); |
219 | 18 | CPLFree(padfZ); |
220 | 18 | CPLFree(padfM); |
221 | 18 | return OGRERR_CORRUPT_DATA; |
222 | 18 | } |
223 | 614 | if ((flagsFromInput & OGR_G_3D) && !(flags & OGR_G_3D)) |
224 | 325 | { |
225 | 325 | flags |= OGR_G_3D; |
226 | 325 | bHasZ = TRUE; |
227 | 325 | } |
228 | 614 | if ((flagsFromInput & OGR_G_MEASURED) && !(flags & OGR_G_MEASURED)) |
229 | 185 | { |
230 | 185 | flags |= OGR_G_MEASURED; |
231 | 185 | bHasM = TRUE; |
232 | 185 | } |
233 | | |
234 | | /* -------------------------------------------------------------------- */ |
235 | | /* Transform raw points into point objects. */ |
236 | | /* -------------------------------------------------------------------- */ |
237 | 3.05k | for (int iGeom = 0; iGeom < nPointCount; iGeom++) |
238 | 2.43k | { |
239 | 2.43k | OGRPoint *poPoint = |
240 | 2.43k | new OGRPoint(paoPoints[iGeom].x, paoPoints[iGeom].y); |
241 | 2.43k | if (bHasM) |
242 | 1.14k | { |
243 | 1.14k | if (padfM != nullptr) |
244 | 1.14k | poPoint->setM(padfM[iGeom]); |
245 | 0 | else |
246 | 0 | poPoint->setM(0.0); |
247 | 1.14k | } |
248 | 2.43k | if (bHasZ) |
249 | 2.29k | { |
250 | 2.29k | if (padfZ != nullptr) |
251 | 2.29k | poPoint->setZ(padfZ[iGeom]); |
252 | 0 | else |
253 | 0 | poPoint->setZ(0.0); |
254 | 2.29k | } |
255 | | |
256 | 2.43k | eErr = addGeometryDirectly(poPoint); |
257 | 2.43k | if (eErr != OGRERR_NONE) |
258 | 0 | { |
259 | 0 | CPLFree(paoPoints); |
260 | 0 | CPLFree(padfZ); |
261 | 0 | CPLFree(padfM); |
262 | 0 | delete poPoint; |
263 | 0 | return eErr; |
264 | 0 | } |
265 | 2.43k | } |
266 | | |
267 | 614 | CPLFree(paoPoints); |
268 | 614 | CPLFree(padfZ); |
269 | 614 | CPLFree(padfM); |
270 | | |
271 | 614 | *ppszInput = pszInput; |
272 | | |
273 | 614 | return OGRERR_NONE; |
274 | 614 | } |
275 | | |
276 | | /************************************************************************/ |
277 | | /* importFromWkt_Bracketed() */ |
278 | | /* */ |
279 | | /* This operates similar to importFromWkt(), but reads a format */ |
280 | | /* with brackets around each point. This is the form defined */ |
281 | | /* in the BNF of the SFSQL spec. It is called from */ |
282 | | /* importFromWkt(). */ |
283 | | /************************************************************************/ |
284 | | |
285 | | OGRErr OGRMultiPoint::importFromWkt_Bracketed(const char **ppszInput, int bHasM, |
286 | | int bHasZ) |
287 | | |
288 | 13 | { |
289 | | /* -------------------------------------------------------------------- */ |
290 | | /* Skip MULTIPOINT keyword. */ |
291 | | /* -------------------------------------------------------------------- */ |
292 | 13 | char szToken[OGR_WKT_TOKEN_MAX] = {}; |
293 | 13 | const char *pszInput = *ppszInput; |
294 | 13 | pszInput = OGRWktReadToken(pszInput, szToken); |
295 | | |
296 | 13 | if (bHasZ || bHasM) |
297 | 1 | { |
298 | | // Skip Z, M or ZM. |
299 | 1 | pszInput = OGRWktReadToken(pszInput, szToken); |
300 | 1 | } |
301 | | |
302 | | /* -------------------------------------------------------------------- */ |
303 | | /* Read points till we get to the closing bracket. */ |
304 | | /* -------------------------------------------------------------------- */ |
305 | | |
306 | 13 | OGRRawPoint *paoPoints = nullptr; |
307 | 13 | double *padfZ = nullptr; |
308 | 13 | double *padfM = nullptr; |
309 | | |
310 | 152 | while ((pszInput = OGRWktReadToken(pszInput, szToken)) != nullptr && |
311 | 152 | (EQUAL(szToken, "(") || EQUAL(szToken, ","))) |
312 | 150 | { |
313 | 150 | const char *pszNext = OGRWktReadToken(pszInput, szToken); |
314 | 150 | if (EQUAL(szToken, "EMPTY")) |
315 | 0 | { |
316 | 0 | OGRPoint *poGeom = new OGRPoint(0.0, 0.0); |
317 | 0 | poGeom->empty(); |
318 | 0 | const OGRErr eErr = addGeometryDirectly(poGeom); |
319 | 0 | if (eErr != OGRERR_NONE) |
320 | 0 | { |
321 | 0 | CPLFree(paoPoints); |
322 | 0 | delete poGeom; |
323 | 0 | return eErr; |
324 | 0 | } |
325 | | |
326 | 0 | pszInput = pszNext; |
327 | |
|
328 | 0 | continue; |
329 | 0 | } |
330 | | |
331 | 150 | int flagsFromInput = flags; |
332 | 150 | int nMaxPoint = 0; |
333 | 150 | int nPointCount = 0; |
334 | 150 | pszInput = OGRWktReadPointsM(pszInput, &paoPoints, &padfZ, &padfM, |
335 | 150 | &flagsFromInput, &nMaxPoint, &nPointCount); |
336 | | |
337 | 150 | if (pszInput == nullptr || nPointCount != 1) |
338 | 11 | { |
339 | 11 | CPLFree(paoPoints); |
340 | 11 | CPLFree(padfZ); |
341 | 11 | CPLFree(padfM); |
342 | 11 | return OGRERR_CORRUPT_DATA; |
343 | 11 | } |
344 | 139 | if ((flagsFromInput & OGR_G_3D) && !(flags & OGR_G_3D)) |
345 | 9 | { |
346 | 9 | flags |= OGR_G_3D; |
347 | 9 | bHasZ = TRUE; |
348 | 9 | } |
349 | 139 | if ((flagsFromInput & OGR_G_MEASURED) && !(flags & OGR_G_MEASURED)) |
350 | 5 | { |
351 | 5 | flags |= OGR_G_MEASURED; |
352 | 5 | bHasM = TRUE; |
353 | 5 | } |
354 | | |
355 | 139 | OGRPoint *poPoint = new OGRPoint(paoPoints[0].x, paoPoints[0].y); |
356 | 139 | if (bHasM) |
357 | 31 | { |
358 | 31 | if (padfM != nullptr) |
359 | 31 | poPoint->setM(padfM[0]); |
360 | 0 | else |
361 | 0 | poPoint->setM(0.0); |
362 | 31 | } |
363 | 139 | if (bHasZ) |
364 | 130 | { |
365 | 130 | if (padfZ != nullptr) |
366 | 130 | poPoint->setZ(padfZ[0]); |
367 | 0 | else |
368 | 0 | poPoint->setZ(0.0); |
369 | 130 | } |
370 | | |
371 | 139 | const OGRErr eErr = addGeometryDirectly(poPoint); |
372 | 139 | if (eErr != OGRERR_NONE) |
373 | 0 | { |
374 | 0 | CPLFree(paoPoints); |
375 | 0 | CPLFree(padfZ); |
376 | 0 | CPLFree(padfM); |
377 | 0 | delete poPoint; |
378 | 0 | return eErr; |
379 | 0 | } |
380 | 139 | } |
381 | | |
382 | | /* -------------------------------------------------------------------- */ |
383 | | /* Cleanup. */ |
384 | | /* -------------------------------------------------------------------- */ |
385 | 2 | CPLFree(paoPoints); |
386 | 2 | CPLFree(padfZ); |
387 | 2 | CPLFree(padfM); |
388 | | |
389 | 2 | if (!EQUAL(szToken, ")")) |
390 | 1 | return OGRERR_CORRUPT_DATA; |
391 | | |
392 | 1 | *ppszInput = pszInput; |
393 | | |
394 | 1 | return OGRERR_NONE; |
395 | 2 | } |
396 | | |
397 | | /************************************************************************/ |
398 | | /* hasCurveGeometry() */ |
399 | | /************************************************************************/ |
400 | | |
401 | | OGRBoolean OGRMultiPoint::hasCurveGeometry(int /* bLookForNonLinear */) const |
402 | 617 | { |
403 | 617 | return FALSE; |
404 | 617 | } |