Coverage Report

Created: 2025-03-15 06:58

/src/geos/src/io/GeoJSONReader.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2021 Jared Erickson
7
 *
8
 * This is free software; you can redistribute and/or modify it under
9
 * the terms of the GNU Lesser General Public Licence as published
10
 * by the Free Software Foundation.
11
 * See the COPYING file for more information.
12
 *
13
 **********************************************************************/
14
15
#include <geos/io/GeoJSONReader.h>
16
#include <geos/util/IllegalArgumentException.h>
17
#include <geos/io/ParseException.h>
18
#include <geos/geom/Coordinate.h>
19
#include <geos/geom/Point.h>
20
#include <geos/geom/LinearRing.h>
21
#include <geos/geom/LineString.h>
22
#include <geos/geom/Polygon.h>
23
#include <geos/geom/MultiPoint.h>
24
#include <geos/geom/MultiLineString.h>
25
#include <geos/geom/MultiPolygon.h>
26
#include <geos/geom/CoordinateSequence.h>
27
#include <geos/geom/PrecisionModel.h>
28
#include <geos/util.h>
29
30
#include <algorithm>
31
#include <ostream>
32
#include <sstream>
33
#include <cassert>
34
35
#define PROJ_COMPILATION
36
37
using namespace geos::geom;
38
using json = geos_nlohmann::json;
39
40
namespace geos {
41
namespace io { // geos.io
42
43
0
GeoJSONReader::GeoJSONReader(): GeoJSONReader(*(GeometryFactory::getDefaultInstance())) {}
44
45
0
GeoJSONReader::GeoJSONReader(const geom::GeometryFactory& gf) : geometryFactory(gf) {}
46
47
std::unique_ptr<geom::Geometry> GeoJSONReader::read(const std::string& geoJsonText) const
48
0
{
49
0
    try {
50
0
        const json& j = json::parse(geoJsonText);
51
0
        const std::string& type = j.at("type");
52
0
        if (type == "Feature") {
53
0
            return readFeatureForGeometry(j);
54
0
        }
55
0
        else if (type == "FeatureCollection") {
56
0
            return readFeatureCollectionForGeometry(j);
57
0
        }
58
0
        else {
59
0
            return readGeometry(j);
60
0
        }
61
0
    }
62
0
    catch (json::exception& ex) {
63
0
        throw ParseException("Error parsing JSON", ex.what());
64
0
    }
65
0
}
66
67
GeoJSONFeatureCollection GeoJSONReader::readFeatures(const std::string& geoJsonText) const
68
0
{
69
0
    try {
70
0
        const json& j = json::parse(geoJsonText);
71
0
        const std::string& type = j.at("type");
72
0
        if (type == "Feature") {
73
0
            const auto& feature = readFeature(j);
74
0
            return GeoJSONFeatureCollection { std::vector<GeoJSONFeature>{feature} };
75
0
        }
76
0
        else if (type == "FeatureCollection") {
77
0
            return readFeatureCollection(j);
78
0
        }
79
0
        else {
80
0
            auto g = readGeometry(j);
81
0
            return GeoJSONFeatureCollection { std::vector<GeoJSONFeature>{GeoJSONFeature{std::move(g), std::map<std::string, GeoJSONValue>{} }}};
82
0
        }
83
0
    }
84
0
    catch (json::exception& ex) {
85
0
        throw ParseException("Error parsing JSON", ex.what());
86
0
    }
87
0
}
88
89
std::unique_ptr<geom::Geometry> GeoJSONReader::readFeatureForGeometry(
90
    const geos_nlohmann::json& j) const
91
0
{
92
0
    const auto& geometryJson = j.at("geometry");
93
0
    auto geometry = readGeometry(geometryJson);
94
0
    return geometry;
95
0
}
96
97
GeoJSONFeature GeoJSONReader::readFeature(const geos_nlohmann::json& j) const
98
0
{
99
0
    const auto& geometryJson = j.at("geometry");
100
0
    const auto& properties = j.at("properties");
101
102
0
    std::string id = "";
103
0
    if (j.contains("id") && !j.at("id").is_null()) {
104
0
        if (j.at("id").is_string()) id = j.at("id").get<std::string>();
105
0
        if (j.at("id").is_number()) id = j.at("id").dump();
106
0
    }
107
108
0
    return GeoJSONFeature{readGeometry(geometryJson), readProperties(properties), id};
109
0
}
110
111
std::map<std::string, GeoJSONValue> GeoJSONReader::readProperties(
112
    const geos_nlohmann::json& p) const
113
0
{
114
0
    std::map<std::string, GeoJSONValue> map;
115
0
    for (const auto& prop : p.items()) {
116
0
        map[prop.key()] = std::move(readProperty(prop.value()));
117
0
    }
118
0
    return map;
119
0
}
120
121
GeoJSONValue GeoJSONReader::readProperty(
122
    const geos_nlohmann::json& value) const
123
0
{
124
0
    if (value.is_string()) {
125
0
        return GeoJSONValue { value.get<std::string>() };
126
0
    }
127
0
    else if (value.is_number()) {
128
0
        return GeoJSONValue { value.get<double>() };
129
0
    }
130
0
    else if (value.is_boolean()) {
131
0
        return GeoJSONValue { value.get<bool>() };
132
0
    }
133
0
    else if (value.is_array()) {
134
0
        std::vector<GeoJSONValue> v {};
135
0
        v.reserve(value.size());
136
0
        for (const auto& el : value.items()) {
137
0
            v.push_back(readProperty(el.value()));
138
0
        }
139
0
        return GeoJSONValue{ v };
140
0
    }
141
0
    else if (value.is_object()) {
142
0
        std::map<std::string, GeoJSONValue> v {};
143
0
        for (const auto& el : value.items()) {
144
0
            v[el.key()] = std::move(readProperty(el.value()));
145
0
        }
146
0
        return GeoJSONValue{ v };
147
0
    }
148
0
    else {
149
0
        return GeoJSONValue{};
150
0
    }
151
0
}
152
153
std::unique_ptr<geom::Geometry> GeoJSONReader::readFeatureCollectionForGeometry(
154
    const geos_nlohmann::json& j) const
155
0
{
156
0
    const auto& featuresJson = j.at("features");
157
0
    std::vector<std::unique_ptr<geom::Geometry>> geometries;
158
0
    geometries.reserve(featuresJson.size());
159
0
    for (const auto& featureJson : featuresJson) {
160
0
        auto g = readFeatureForGeometry(featureJson);
161
0
        geometries.push_back(std::move(g));
162
0
    }
163
0
    return geometryFactory.createGeometryCollection(std::move(geometries));
164
0
}
165
166
GeoJSONFeatureCollection GeoJSONReader::readFeatureCollection(
167
    const geos_nlohmann::json& j) const
168
0
{
169
0
    const auto& featuresJson = j.at("features");
170
0
    std::vector<GeoJSONFeature> features;
171
0
    features.reserve(featuresJson.size());
172
0
    for (const auto& featureJson : featuresJson) {
173
0
        features.push_back(readFeature(featureJson));
174
0
    }
175
0
    return GeoJSONFeatureCollection{std::move(features)};
176
0
}
177
178
179
std::unique_ptr<geom::Geometry> GeoJSONReader::readGeometry(
180
    const geos_nlohmann::json& j) const
181
0
{
182
0
    const std::string& type = j.at("type");
183
0
    if (type == "Point") {
184
0
        return readPoint(j);
185
0
    }
186
0
    else if (type == "LineString") {
187
0
        return readLineString(j);
188
0
    }
189
0
    else if (type == "Polygon") {
190
0
        return readPolygon(j);
191
0
    }
192
0
    else if (type == "MultiPoint") {
193
0
        return readMultiPoint(j);
194
0
    }
195
0
    else if (type == "MultiLineString") {
196
0
        return readMultiLineString(j);
197
0
    }
198
0
    else if (type == "MultiPolygon") {
199
0
        return readMultiPolygon(j);
200
0
    }
201
0
    else if (type == "GeometryCollection") {
202
0
        return readGeometryCollection(j);
203
0
    }
204
0
    else {
205
0
        throw ParseException{"Unknown geometry type!"};
206
0
    }
207
0
}
208
209
geom::Coordinate GeoJSONReader::readCoordinate(
210
    const std::vector<double>& coords) const
211
0
{
212
0
    if (coords.size() == 1) {
213
0
        throw  ParseException("Expected two or three coordinates found one");
214
0
    }
215
0
    else if (coords.size() == 2) {
216
0
        return geom::Coordinate { coords[0], coords[1] };
217
0
    }
218
0
    else if (coords.size() == 3) {
219
0
        return geom::Coordinate { coords[0], coords[1], coords[2] };
220
0
    }
221
0
    else {
222
0
        throw  ParseException("Expected two or three coordinates found more than three");
223
0
    }
224
0
}
225
226
std::unique_ptr<geom::Point> GeoJSONReader::readPoint(
227
    const geos_nlohmann::json& j) const
228
0
{
229
0
    const auto& coords = j.at("coordinates").get<std::vector<double>>();
230
0
    if (coords.size() == 1) {
231
0
        throw  ParseException("Expected two or three coordinates found one");
232
0
    }
233
0
    else if (coords.size() < 2) {
234
0
        return geometryFactory.createPoint(2);
235
0
    }
236
0
    else {
237
0
        const geom::Coordinate& coord = readCoordinate(coords);
238
0
        return std::unique_ptr<geom::Point>(geometryFactory.createPoint(coord));
239
0
    }
240
0
}
241
242
std::unique_ptr<geom::LineString> GeoJSONReader::readLineString(
243
    const geos_nlohmann::json& j) const
244
0
{
245
0
    const auto& coords = j.at("coordinates").get<std::vector<std::vector<double>>>();
246
0
    bool has_z = std::any_of(coords.begin(), coords.end(), [](auto v) { return v.size() > 2; });
247
0
    auto coordinates = detail::make_unique<CoordinateSequence>(0u, has_z, false);
248
0
    coordinates->reserve(coords.size());
249
0
    for (const auto& coord : coords) {
250
0
        const geom::Coordinate& c = readCoordinate(coord);
251
0
        coordinates->add(c);
252
0
    }
253
0
    return geometryFactory.createLineString(std::move(coordinates));
254
0
}
255
256
std::unique_ptr<geom::Polygon> GeoJSONReader::readPolygon(
257
    const geos_nlohmann::json& j) const
258
0
{
259
0
    const auto& polygonCoords = j.at("coordinates").get<std::vector<std::vector<std::vector<double>>>>();
260
0
    return readPolygon(polygonCoords);
261
0
}
262
263
std::unique_ptr<geom::Polygon> GeoJSONReader::readPolygon(
264
    const std::vector<std::vector<std::vector<double>>>& polygonCoords) const
265
0
{
266
0
    std::unique_ptr<geom::LinearRing> shell;
267
0
    std::vector<std::unique_ptr<geom::LinearRing>> rings;
268
0
    rings.reserve(polygonCoords.size());
269
0
    for (const auto& ring : polygonCoords) {
270
0
        bool has_z = std::any_of(ring.begin(), ring.end(), [](auto v) { return v.size() > 2; });
271
0
        auto coordinates = detail::make_unique<CoordinateSequence>(0u, has_z, false);
272
0
        coordinates->reserve(ring.size());
273
0
        for (const auto& coord : ring) {
274
0
            const geom::Coordinate& c = readCoordinate(coord);
275
0
            coordinates->add(c);
276
0
        }
277
0
        if (!shell) {
278
0
            shell = geometryFactory.createLinearRing(std::move(coordinates));
279
0
        }
280
0
        else {
281
0
            rings.push_back(geometryFactory.createLinearRing(std::move(coordinates)));
282
0
        }
283
0
    }
284
0
    if (!shell) {
285
0
        return geometryFactory.createPolygon(2);
286
0
    }
287
0
    else if (rings.size() == 0) {
288
0
        return geometryFactory.createPolygon(std::move(shell));
289
0
    }
290
0
    else {
291
0
        return geometryFactory.createPolygon(std::move(shell), std::move(rings));
292
0
    }
293
0
}
294
295
std::unique_ptr<geom::MultiPoint> GeoJSONReader::readMultiPoint(
296
    const geos_nlohmann::json& j) const
297
0
{
298
0
    const auto& coords = j.at("coordinates").get<std::vector<std::vector<double>>>();
299
0
    std::vector<std::unique_ptr<geom::Point>> points;
300
0
    points.reserve(coords.size());
301
0
    for (const auto& coord : coords) {
302
0
        const geom::Coordinate& c = readCoordinate(coord);
303
0
        points.push_back(std::unique_ptr<geom::Point>(geometryFactory.createPoint(c)));
304
0
    }
305
0
    return geometryFactory.createMultiPoint(std::move(points));
306
0
}
307
308
std::unique_ptr<geom::MultiLineString> GeoJSONReader::readMultiLineString(
309
    const geos_nlohmann::json& j) const
310
0
{
311
0
    const auto& listOfCoords = j.at("coordinates").get<std::vector<std::vector<std::vector<double>>>>();
312
0
    std::vector<std::unique_ptr<geom::LineString>> lines;
313
0
    lines.reserve(listOfCoords.size());
314
0
    for (const auto& coords :  listOfCoords) {
315
0
        bool has_z = std::any_of(coords.begin(), coords.end(), [](auto v) { return v.size() > 2; });
316
0
        auto coordinates = detail::make_unique<geom::CoordinateSequence>(0u, has_z, false);
317
0
        coordinates->reserve(coords.size());
318
0
        for (const auto& coord : coords) {
319
0
            const geom::Coordinate& c = readCoordinate(coord);
320
0
            coordinates->add(c);
321
0
        }
322
0
        lines.push_back(geometryFactory.createLineString(std::move(coordinates)));
323
0
    }
324
0
    return geometryFactory.createMultiLineString(std::move(lines));
325
0
}
326
327
std::unique_ptr<geom::MultiPolygon> GeoJSONReader::readMultiPolygon(
328
    const geos_nlohmann::json& j) const
329
0
{
330
0
    const auto& multiPolygonCoords = j.at("coordinates").get<std::vector<std::vector<std::vector<std::vector<double>>>>>();
331
0
    std::vector<std::unique_ptr<geom::Polygon>> polygons;
332
0
    polygons.reserve(multiPolygonCoords.size());
333
0
    for (const auto& polygonCoords : multiPolygonCoords) {
334
0
        polygons.push_back(readPolygon(polygonCoords));
335
0
    }
336
0
    return geometryFactory.createMultiPolygon(std::move(polygons));
337
0
}
338
339
std::unique_ptr<geom::GeometryCollection> GeoJSONReader::readGeometryCollection(
340
    const geos_nlohmann::json& j) const
341
0
{
342
0
    const auto& jsonGeometries = j.at("geometries");
343
0
    std::vector<std::unique_ptr<geom::Geometry>> geometries;
344
0
    geometries.reserve(jsonGeometries.size());
345
0
    for (const auto& jsonGeometry : jsonGeometries) {
346
0
        auto g = readGeometry(jsonGeometry);
347
0
        geometries.push_back(std::move(g));
348
0
    }
349
0
    return geometryFactory.createGeometryCollection(std::move(geometries));
350
0
}
351
352
} // namespace geos.io
353
} // namespace geos