1
0
Fork 0

added MultiPolygon GeoJSON constructor function (#7634)

This commit is contained in:
Heiko 2018-12-04 17:54:34 +01:00 committed by Frank Celler
parent 4376f9efb6
commit a94a402dde
4 changed files with 202 additions and 94 deletions

View File

@ -277,6 +277,32 @@ RETURN GEO_POLYGON([
@END_EXAMPLE_AQL @END_EXAMPLE_AQL
@endDocuBlock aqlGeoPolygon_2 @endDocuBlock aqlGeoPolygon_2
### GEO_MULTIPOLYGON()
`GEO_MULTIPOLYGON(polygons) → geoJson`
Construct a GeoJSON MultiPolygon. Needs at least two Polygons in an array.
See [GEO_POLYGON()](#geopolygon) for the rules of Polygon construction.
- **polygons** (array): array of polygons
- returns **geoJson** (object|null): a valid GeoJSON MultiPolygon
MultiPolygon comprised of a simple Polygon and a Polygon with hole:
@startDocuBlockInline aqlGeoMultiPolygon_1
@EXAMPLE_AQL{aqlGeoMultiPolygon_1}
RETURN GEO_MULTIPOLYGON([
[
[[40, 40], [20, 45], [45, 30], [40, 40]]
],
[
[[20, 35], [10, 30], [10, 10], [30, 5], [45, 20], [20, 35]],
[[30, 20], [20, 15], [20, 25], [30, 20]]
]
])
@END_EXAMPLE_AQL
@endDocuBlock aqlGeoMultiPolygon_1
Geo Index Functions Geo Index Functions
------------------- -------------------

View File

@ -358,6 +358,7 @@ void AqlFunctionFeature::addGeometryConstructors() {
add({"GEO_POINT", ".,.", flags, &Functions::GeoPoint}); add({"GEO_POINT", ".,.", flags, &Functions::GeoPoint});
add({"GEO_MULTIPOINT", ".", flags, &Functions::GeoMultiPoint}); add({"GEO_MULTIPOINT", ".", flags, &Functions::GeoMultiPoint});
add({"GEO_POLYGON", ".", flags, &Functions::GeoPolygon}); add({"GEO_POLYGON", ".", flags, &Functions::GeoPolygon});
add({"GEO_MULTIPOLYGON", ".", flags, &Functions::GeoMultiPolygon});
add({"GEO_LINESTRING", ".", flags, &Functions::GeoLinestring}); add({"GEO_LINESTRING", ".", flags, &Functions::GeoLinestring});
add({"GEO_MULTILINESTRING", ".", flags, &Functions::GeoMultiLinestring}); add({"GEO_MULTILINESTRING", ".", flags, &Functions::GeoMultiLinestring});
} }

View File

@ -1461,6 +1461,109 @@ AqlValue callApplyBackend(arangodb::aql::Query* query,
} }
} }
static Result parseGeoPolygon(VPackSlice polygon, VPackBuilder& b) {
// check if nested or not
bool unnested = false;
for (auto const& v : VPackArrayIterator(polygon)) {
if (v.isArray() && v.length() == 2) {
unnested = true;
}
}
if (unnested) {
b.openArray();
}
if (!polygon.isArray()) {
return Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"Polygon needs to be an array of positions.");
}
for (auto const& v : VPackArrayIterator(polygon)) {
if (v.isArray() && v.length() > 2) {
b.openArray();
for (auto const& coord : VPackArrayIterator(v)) {
if (coord.isNumber()) {
b.add(VPackValue(coord.getNumber<double>()));
} else if (coord.isArray()) {
if (coord.length() < 2) {
return Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"a Position needs at least two numeric values");
} else {
b.openArray();
for (auto const& innercord : VPackArrayIterator(coord)) {
if (innercord.isNumber()) {
b.add(VPackValue(innercord.getNumber<double>())); // TODO
} else if (innercord.isArray() && innercord.length() == 2) {
if (innercord.at(0).isNumber() && innercord.at(1).isNumber()) {
b.openArray();
b.add(VPackValue(innercord.at(0).getNumber<double>()));
b.add(VPackValue(innercord.at(1).getNumber<double>()));
b.close();
} else {
return Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"coordinate is not a number");
}
} else {
return Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"not an array describing a position");
}
}
b.close();
}
} else {
return Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"not an array containing positions");
}
}
b.close();
} else if (v.isArray() && v.length() == 2) {
if (polygon.length() > 2) {
b.openArray();
for (auto const& innercord : VPackArrayIterator(v)) {
if (innercord.isNumber()) {
b.add(VPackValue(innercord.getNumber<double>()));
} else if (innercord.isArray() && innercord.length() == 2) {
if (innercord.at(0).isNumber() && innercord.at(1).isNumber()) {
b.openArray();
b.add(VPackValue(innercord.at(0).getNumber<double>()));
b.add(VPackValue(innercord.at(1).getNumber<double>()));
b.close();
} else {
return Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"coordinate is not a number");
}
} else {
return Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"not a numeric value");
}
}
b.close();
} else {
return Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"a Polygon needs at least three positions");
}
} else {
return Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"not an array containing positions");
}
}
if (unnested) {
b.close();
}
return {TRI_ERROR_NO_ERROR};
}
} // namespace } // namespace
@ -5235,110 +5338,86 @@ AqlValue Functions::GeoPolygon(arangodb::aql::Query* query,
AqlValueMaterializer materializer(trx); AqlValueMaterializer materializer(trx);
VPackSlice s = materializer.slice(geoArray, false); VPackSlice s = materializer.slice(geoArray, false);
// check if nested or not Result res = ::parseGeoPolygon(s, b);
bool unnested = false; if (res.fail()) {
for (auto const& v : VPackArrayIterator(s)) { ::registerWarning(query, "GEO_POLYGON", res);
if (v.isArray() && v.length() == 2) { return AqlValue(arangodb::velocypack::Slice::nullSlice());
unnested = true;
}
}
if (unnested) {
b.openArray();
} }
for (auto const& v : VPackArrayIterator(s)) { b.close(); // coordinates
if (v.isArray() && v.length() > 2) { b.close(); // object
b.openArray();
for (auto const& coord : VPackArrayIterator(v)) { return AqlValue(b);
if (coord.isNumber()) { }
b.add(VPackValue(coord.getNumber<double>()));
} else if (coord.isArray()) { /// @brief function GEO_MULTIPOLYGON
if (coord.length() < 2) { AqlValue Functions::GeoMultiPolygon(arangodb::aql::Query* query,
::registerWarning(query, "GEO_POLYGON", Result( transaction::Methods* trx,
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, VPackFunctionParameters const& parameters) {
"a Position needs at least two numeric values")); size_t const n = parameters.size();
return AqlValue(arangodb::velocypack::Slice::nullSlice());
} else { if (n < 1) {
b.openArray(); // no parameters
for (auto const& innercord : VPackArrayIterator(coord)) { return AqlValue(AqlValueHintNull());
if (innercord.isNumber()) { }
b.add(VPackValue(innercord.getNumber<double>()));
} else if (innercord.isArray()) { AqlValue const& geoArray = extractFunctionParameterValue(parameters, 0);
if (innercord.at(0).isNumber() && innercord.at(1).isNumber()) {
b.openArray(); if (!geoArray.isArray()) {
b.add(VPackValue(innercord.at(0).getNumber<double>())); ::registerWarning(query, "GEO_MULTIPOLYGON",
b.add(VPackValue(innercord.at(1).getNumber<double>())); TRI_ERROR_QUERY_ARRAY_EXPECTED);
b.close(); return AqlValue(arangodb::velocypack::Slice::nullSlice());
} else { }
::registerWarning(query, "GEO_POLYGON", Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, AqlValueMaterializer materializer(trx);
"not a number")); VPackSlice s = materializer.slice(geoArray, false);
return AqlValue(arangodb::velocypack::Slice::nullSlice());
} /*
} else { return GEO_MULTIPOLYGON([
::registerWarning(query, "GEO_POLYGON", Result( [
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, [[40, 40], [20, 45], [45, 30], [40, 40]]
"not an array describing a position")); ],
return AqlValue(arangodb::velocypack::Slice::nullSlice()); [
} [[20, 35], [10, 30], [10, 10], [30, 5], [45, 20], [20, 35]],
} [[30, 20], [20, 15], [20, 25], [30, 20]]
b.close(); ]
} ])
} else { */
::registerWarning(query, "GEO_POLYGON", Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, TRI_ASSERT(s.isArray());
"not an array containing positions")); if (s.length() < 2) {
return AqlValue(arangodb::velocypack::Slice::nullSlice()); ::registerWarning(query, "GEO_MULTIPOLYGON", Result(
} TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
} "a MultiPolygon needs at least two Polygons inside."));
b.close(); return AqlValue(arangodb::velocypack::Slice::nullSlice());
} else if (v.isArray() && v.length() == 2) { }
if (s.length() > 2) {
b.openArray(); VPackBuilder b;
for (auto const& innercord : VPackArrayIterator(v)) { b.openObject();
if (innercord.isNumber()) { b.add("type", VPackValue("MultiPolygon"));
b.add(VPackValue(innercord.getNumber<double>())); b.add("coordinates", VPackValue(VPackValueType::Array));
} else if (innercord.isArray()) {
if (innercord.at(0).isNumber() && innercord.at(1).isNumber()) { for (auto const& arrayOfPolygons : VPackArrayIterator(s)) {
b.openArray(); if (!arrayOfPolygons.isArray()) {
b.add(VPackValue(innercord.at(0).getNumber<double>())); ::registerWarning(query, "GEO_MULTIPOLYGON", Result(
b.add(VPackValue(innercord.at(1).getNumber<double>()));
b.close();
} else {
::registerWarning(query, "GEO_POLYGON", Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"not a number"));
return AqlValue(arangodb::velocypack::Slice::nullSlice());
}
} else {
::registerWarning(query, "GEO_POLYGON", Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"not a numeric value"));
return AqlValue(arangodb::velocypack::Slice::nullSlice());
}
}
b.close();
} else {
::registerWarning(query, "GEO_POLYGON", Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"a Polygon needs at least three positions"));
return AqlValue(arangodb::velocypack::Slice::nullSlice());
}
} else {
::registerWarning(query, "GEO_POLYGON", Result(
TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH, TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH,
"not an array containing positions")); "a MultiPolygon needs at least two Polygons inside."));
return AqlValue(arangodb::velocypack::Slice::nullSlice()); return AqlValue(arangodb::velocypack::Slice::nullSlice());
} }
b.openArray(); //arrayOfPolygons
for (auto const& v : VPackArrayIterator(arrayOfPolygons)) {
Result res = ::parseGeoPolygon(v, b);
if (res.fail()) {
::registerWarning(query, "GEO_MULTIPOLYGON", res);
return AqlValue(arangodb::velocypack::Slice::nullSlice());
}
}
b.close(); //arrayOfPolygons close
} }
b.close(); b.close();
b.close(); b.close();
if (unnested) {
b.close();
}
return AqlValue(b); return AqlValue(b);
} }

View File

@ -321,6 +321,8 @@ struct Functions {
VPackFunctionParameters const&); VPackFunctionParameters const&);
static AqlValue GeoLinestring(arangodb::aql::Query*, transaction::Methods*, static AqlValue GeoLinestring(arangodb::aql::Query*, transaction::Methods*,
VPackFunctionParameters const&); VPackFunctionParameters const&);
static AqlValue GeoMultiPolygon(arangodb::aql::Query*, transaction::Methods*,
VPackFunctionParameters const&);
static AqlValue GeoMultiLinestring(arangodb::aql::Query*, transaction::Methods*, static AqlValue GeoMultiLinestring(arangodb::aql::Query*, transaction::Methods*,
VPackFunctionParameters const&); VPackFunctionParameters const&);
static AqlValue Flatten(arangodb::aql::Query*, transaction::Methods*, static AqlValue Flatten(arangodb::aql::Query*, transaction::Methods*,