#include "common/GeoJson.h" #include #include namespace visione::geojson { namespace { struct Point { double x; double y; }; bool pointInRing(double latitude, double longitude, const Json::Value &ring, bool *onBoundary = nullptr) { if (!ring.isArray() || ring.size() < 4) { return false; } std::vector points; points.reserve(ring.size()); for (const auto &coordinate : ring) { if (!coordinate.isArray() || coordinate.size() < 2) { return false; } points.push_back({coordinate[0].asDouble(), coordinate[1].asDouble()}); } 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); if (intersects) { inside = !inside; } } return inside; } 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