feat: implement spatial boundary checks for Bari municipal area in GeoJSON handling
This commit is contained in:
+114
-10
@@ -1,5 +1,6 @@
|
||||
#include "common/GeoJson.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
namespace visione::geojson
|
||||
@@ -11,17 +12,12 @@ struct Point
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
bool pointInPolygon(double latitude, double longitude, const Json::Value &geojson)
|
||||
bool pointInRing(double latitude,
|
||||
double longitude,
|
||||
const Json::Value &ring,
|
||||
bool *onBoundary = nullptr)
|
||||
{
|
||||
if (!geojson.isObject() || geojson["type"].asString() != "Polygon" ||
|
||||
!geojson["coordinates"].isArray() || geojson["coordinates"].empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &ring = geojson["coordinates"][0];
|
||||
if (!ring.isArray() || ring.size() < 4)
|
||||
{
|
||||
return false;
|
||||
@@ -41,10 +37,27 @@ bool pointInPolygon(double latitude, double longitude, const Json::Value &geojso
|
||||
const auto x = longitude;
|
||||
const auto y = latitude;
|
||||
bool inside = false;
|
||||
constexpr double epsilon = 1e-9;
|
||||
for (size_t i = 0, j = points.size() - 1; i < points.size(); j = i++)
|
||||
{
|
||||
const auto &pi = points[i];
|
||||
const auto &pj = points[j];
|
||||
const auto cross =
|
||||
(x - pi.x) * (pj.y - pi.y) - (y - pi.y) * (pj.x - pi.x);
|
||||
const auto minX = std::min(pi.x, pj.x) - epsilon;
|
||||
const auto maxX = std::max(pi.x, pj.x) + epsilon;
|
||||
const auto minY = std::min(pi.y, pj.y) - epsilon;
|
||||
const auto maxY = std::max(pi.y, pj.y) + epsilon;
|
||||
if (std::abs(cross) <= epsilon && x >= minX && x <= maxX && y >= minY &&
|
||||
y <= maxY)
|
||||
{
|
||||
if (onBoundary)
|
||||
{
|
||||
*onBoundary = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool intersects =
|
||||
((pi.y > y) != (pj.y > y)) &&
|
||||
(x < (pj.x - pi.x) * (y - pi.y) / (pj.y - pi.y) + pi.x);
|
||||
@@ -55,5 +68,96 @@ bool pointInPolygon(double latitude, double longitude, const Json::Value &geojso
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
} // namespace visione::geojson
|
||||
|
||||
bool polygonContainsPoint(double latitude,
|
||||
double longitude,
|
||||
const Json::Value &polygon)
|
||||
{
|
||||
if (!polygon.isObject() || polygon["type"].asString() != "Polygon" ||
|
||||
!polygon["coordinates"].isArray() || polygon["coordinates"].empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool onBoundary = false;
|
||||
if (!pointInRing(latitude, longitude, polygon["coordinates"][0], &onBoundary))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (onBoundary)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for (Json::ArrayIndex i = 1; i < polygon["coordinates"].size(); ++i)
|
||||
{
|
||||
if (pointInRing(latitude, longitude, polygon["coordinates"][i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool pointInPolygon(double latitude, double longitude, const Json::Value &geojson)
|
||||
{
|
||||
return polygonContainsPoint(latitude, longitude, geojson);
|
||||
}
|
||||
|
||||
bool pointInGeometry(double latitude, double longitude, const Json::Value &geojson)
|
||||
{
|
||||
if (!geojson.isObject() || !geojson.isMember("type"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto type = geojson["type"].asString();
|
||||
if (type == "Polygon")
|
||||
{
|
||||
return polygonContainsPoint(latitude, longitude, geojson);
|
||||
}
|
||||
if (type == "MultiPolygon" && geojson["coordinates"].isArray())
|
||||
{
|
||||
for (const auto &polygonCoords : geojson["coordinates"])
|
||||
{
|
||||
Json::Value polygon(Json::objectValue);
|
||||
polygon["type"] = "Polygon";
|
||||
polygon["coordinates"] = polygonCoords;
|
||||
if (polygonContainsPoint(latitude, longitude, polygon))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool polygonInsideGeometry(const Json::Value &candidate, const Json::Value &container)
|
||||
{
|
||||
if (!candidate.isObject() || candidate["type"].asString() != "Polygon" ||
|
||||
!candidate["coordinates"].isArray() || candidate["coordinates"].empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &ring = candidate["coordinates"][0];
|
||||
if (!ring.isArray() || ring.size() < 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (const auto &coordinate : ring)
|
||||
{
|
||||
if (!coordinate.isArray() || coordinate.size() < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!pointInGeometry(coordinate[1].asDouble(),
|
||||
coordinate[0].asDouble(),
|
||||
container))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace visione::geojson
|
||||
|
||||
Reference in New Issue
Block a user