diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fc1aed..56d0892 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(SEMPR_VERSION_MAJOR 0) -set(SEMPR_VERSION_MINOR 1) +set(SEMPR_VERSION_MINOR 3) set(SEMPR_VERSION_PATCH 0) ## diff --git a/cmake/UseODB.cmake b/cmake/UseODB.cmake index eb92c40..f8924fe 100644 --- a/cmake/UseODB.cmake +++ b/cmake/UseODB.cmake @@ -37,7 +37,7 @@ function(odb_compile outvar) #list(APPEND ODB_ARGS -x "${CMAKE_CXX_COMPILER}") - list(APPEND ODB_ARGS --show-sloc) + #list(APPEND ODB_ARGS --show-sloc) if(PARAM_MULTI_DATABASE) list(APPEND ODB_ARGS --multi-database "${PARAM_MULTI_DATABASE}") diff --git a/include/sempr/core/RDF.hpp b/include/sempr/core/RDF.hpp index b99e380..e9f2b74 100644 --- a/include/sempr/core/RDF.hpp +++ b/include/sempr/core/RDF.hpp @@ -3,67 +3,73 @@ #include #include +#include namespace sempr { const std::string& baseURI(); + const std::string buildURI(const std::string& id, const std::string& baseURI); + const std::string extractID(const std::string& uri, const std::string& baseURI); + namespace core { -namespace xsd { - const std::string& baseURI(); -} + namespace xsd { + const std::string& baseURI(); + } -namespace rdf { - const std::string& baseURI(); - const std::string& type(); -} + namespace rdf { + const std::string& baseURI(); + const std::string& type(); + } -namespace rdfs { -# define RDFS(name) const std::string& (name)(); - RDFS(baseURI) - RDFS(Resource) - RDFS(Class) - RDFS(subClassOf) - RDFS(subPropertyOf) - RDFS(domain) - RDFS(range) -# undef RDFS -} + namespace rdfs { + const std::string& baseURI(); + # define RDFS(name) const std::string& (name)(); + RDFS(baseURI) + RDFS(Resource) + RDFS(Class) + RDFS(subClassOf) + RDFS(subPropertyOf) + RDFS(domain) + RDFS(range) + # undef RDFS + } -namespace owl { -# define OWL(name) const std::string& (name)(); - OWL(baseURI) - OWL(Class) - OWL(FunctionalProperty) - OWL(Nothing) - OWL(ObjectProperty) - OWL(Restriction) - OWL(Thing) - OWL(allValuesFrom) - OWL(cardinality) - OWL(differentFrom) - OWL(disjointWith) - OWL(distinctMembers) - OWL(equivalentClass) - OWL(equivalentProperty) - OWL(hasValue) - OWL(intersectionOf) - OWL(inverseOf) - OWL(maxCardinality) - OWL(minCardinality) - OWL(onClass) - OWL(onDataRange) - OWL(onDatatype) - OWL(oneOf) - OWL(onProperty) - OWL(sameAs) - OWL(someValuesFrom) - OWL(unionOf) -# undef OWL -} + namespace owl { + const std::string& baseURI(); + # define OWL(name) const std::string& (name)(); + OWL(baseURI) + OWL(Class) + OWL(FunctionalProperty) + OWL(Nothing) + OWL(ObjectProperty) + OWL(Restriction) + OWL(Thing) + OWL(allValuesFrom) + OWL(cardinality) + OWL(differentFrom) + OWL(disjointWith) + OWL(distinctMembers) + OWL(equivalentClass) + OWL(equivalentProperty) + OWL(hasValue) + OWL(intersectionOf) + OWL(inverseOf) + OWL(maxCardinality) + OWL(minCardinality) + OWL(onClass) + OWL(onDataRange) + OWL(onDatatype) + OWL(oneOf) + OWL(onProperty) + OWL(sameAs) + OWL(someValuesFrom) + OWL(unionOf) + # undef OWL + } -} /* core */ + } /* core */ } /* sempr */ diff --git a/include/sempr/entity/GeometricObject.hpp b/include/sempr/entity/GeometricObject.hpp new file mode 100644 index 0000000..316ad72 --- /dev/null +++ b/include/sempr/entity/GeometricObject.hpp @@ -0,0 +1,47 @@ +#ifndef SEMPR_ENTITY_GEOMETRICOBJECT_H_ +#define SEMPR_ENTITY_GEOMETRICOBJECT_H_ + +#include +#include +#include +#include +#include + +#include + +namespace sempr { namespace entity { + + +#pragma db object +class GeometricObject : public SemanticEntity { + SEMPR_ENTITY +public: + using Ptr = std::shared_ptr; + + GeometricObject(); + GeometricObject(bool temporary); + GeometricObject(const core::IDGenBase*, bool temporary = false); + + const Geometry::Ptr geometry() const { return geometry_; } + void geometry(const Geometry::Ptr geometry); + + const std::string type() const { return type_; } + void type(const std::string& type); + + void registerProperty(const std::string& predicate, const std::string& object); + + // Gives all related object by a known predicate. (could be empty for all related objects) + std::set related(const std::string& predicate = ""); + +private: + friend class odb::access; + + Geometry::Ptr geometry_; + + std::string type_; +}; + +}} + + +#endif /* end of include guard: SEMPR_ENTITY_GEOMETRICOBJECT_H_ */ diff --git a/include/sempr/entity/RDFEntity.hpp b/include/sempr/entity/RDFEntity.hpp index 4575e21..309d89d 100644 --- a/include/sempr/entity/RDFEntity.hpp +++ b/include/sempr/entity/RDFEntity.hpp @@ -72,7 +72,7 @@ class RDFEntity : public Entity { virtual TripleIteratorWrapper end() const; protected: - RDFEntity(const core::IDGenBase*); + RDFEntity(const core::IDGenBase*, bool temporary = false); private: friend class odb::access; RDFEntity(); diff --git a/include/sempr/entity/RDFPropertyMap.hpp b/include/sempr/entity/RDFPropertyMap.hpp index 55a9b04..5c5eba9 100644 --- a/include/sempr/entity/RDFPropertyMap.hpp +++ b/include/sempr/entity/RDFPropertyMap.hpp @@ -1,7 +1,7 @@ #ifndef SEMPR_ENTITY_RDFPROPERTYMAP_HPP_ #define SEMPR_ENTITY_RDFPROPERTYMAP_HPP_ -#include +#include #include #include // for conversion to rdf-literals. diff --git a/include/sempr/entity/RDFValue.hpp b/include/sempr/entity/RDFValue.hpp index 04ca31c..0a1e350 100644 --- a/include/sempr/entity/RDFValue.hpp +++ b/include/sempr/entity/RDFValue.hpp @@ -2,7 +2,10 @@ #define SEMPR_ENTITY_RDFVALUE_HPP_ #include +#include +#include #include + #include #include #include diff --git a/include/sempr/entity/RDFVector.hpp b/include/sempr/entity/RDFVector.hpp index cda78e3..ab56ddb 100644 --- a/include/sempr/entity/RDFVector.hpp +++ b/include/sempr/entity/RDFVector.hpp @@ -2,12 +2,8 @@ #define SEMPR_ENTITY_RDFVECTOR_H_ #include -#include -#include -#include -#include -#include +#include #include @@ -26,20 +22,19 @@ class RDFVectorIterator : public TripleIterator { bool operator == (const TripleIterator& other) const override; }; - - #pragma db object class RDFVector : public RDFEntity { SEMPR_ENTITY public: using Ptr = std::shared_ptr; RDFVector(); - RDFVector(const core::IDGenBase*); + RDFVector(bool temporary); + RDFVector(const core::IDGenBase*, bool temporary = false); virtual ~RDFVector(){} void getTriples(std::vector& triples) const; const Triple& getTripleAt(const size_t index); - bool addTriple(const Triple& triple); + bool addTriple(const Triple& triple, bool replace = false); // if replace than an equal triple will be removed before, otherwise there a multiple entries possible bool removeTriple(const Triple& triple); void removeTripleAt(const size_t index); void clear(); @@ -48,11 +43,15 @@ class RDFVector : public RDFEntity { TripleIteratorWrapper begin() const override; TripleIteratorWrapper end() const override; + bool validity(const Triple& triple) const; + protected: friend class odb::access; std::vector triples_; }; + + }} #endif /* end of include guard: SEMPR_ENTITY_RDFVECTOR_H_ */ diff --git a/include/sempr/entity/SemanticEntity.hpp b/include/sempr/entity/SemanticEntity.hpp index 41d3add..e58685d 100644 --- a/include/sempr/entity/SemanticEntity.hpp +++ b/include/sempr/entity/SemanticEntity.hpp @@ -149,6 +149,8 @@ class RegisteredPropertyBase { std::string predicate() const; virtual std::string object() const = 0; virtual bool isValid() const = 0; + + bool operator==(const RegisteredPropertyBase& other) const; }; /** @@ -234,7 +236,7 @@ class RegisteredProperty : public RegisteredPropertyBase { public: MyEntity() { - registerPropetry("", myInt_); + registerProperty("", myInt_); registerProperty("", "", myFloat_); } }; @@ -250,9 +252,13 @@ class SemanticEntity : public RDFEntity { #pragma db transient std::vector properties_; + std::vector::iterator findProperty(const RegisteredPropertyBase& prop); + protected: - SemanticEntity(const core::IDGenBase*); SemanticEntity(); + SemanticEntity(bool temporary); + SemanticEntity(const core::IDGenBase*, bool temporary = false); + /** Registers the property at this semantic entity. This will provide the RDF-Triple: @@ -268,6 +274,13 @@ class SemanticEntity : public RDFEntity { properties_.push_back(rprop); } + template + void removeProperty(const std::string& predicate, T& property) + { + auto prop = findProperty(RegisteredProperty(predicate, property)); + properties_.erase(prop); + } + template void registerPropertyPlain(const std::string& predicate, T& property) { @@ -276,6 +289,13 @@ class SemanticEntity : public RDFEntity { properties_.push_back(rprop); } + template + void removePropertyPlain(const std::string& predicate, T& property) + { + auto prop = findProperty(RegisteredProperty(predicate, property)); + properties_.erase(prop); + } + /** Registers the property at this semantic entity. This allows setting both subject and predicate of the resulting triple. @@ -283,13 +303,19 @@ class SemanticEntity : public RDFEntity { Assumes the given property is a member of this SemanticEntity. */ template - void registerProperty(const std::string& subject, const std::string& predicate, - T& property) + void registerProperty(const std::string& subject, const std::string& predicate, T& property) { RegisteredPropertyBase* rprop = new RegisteredProperty(subject, predicate, property); properties_.push_back(rprop); } + template + void removeProperty(const std::string& subject, const std::string& predicate, T& property) + { + auto prop = findProperty(RegisteredProperty(subject, predicate, property)); + properties_.erase(prop); + } + template void registerPropertyPlain(const std::string& subject, const std::string& predicate, T& property) { @@ -298,6 +324,13 @@ class SemanticEntity : public RDFEntity { properties_.push_back(rprop); } + template + void removePropertyPlain(const std::string& subject, const std::string& predicate, T& property) + { + auto prop = findProperty(RegisteredProperty(subject, predicate, property)); + properties_.erase(prop); + } + public: ~SemanticEntity(); diff --git a/include/sempr/entity/Triple.hpp b/include/sempr/entity/Triple.hpp index 80e4e95..3eab918 100644 --- a/include/sempr/entity/Triple.hpp +++ b/include/sempr/entity/Triple.hpp @@ -3,7 +3,6 @@ #define SEMPR_ENTITY_TRIPLE #include - namespace sempr { namespace entity { #pragma db value @@ -11,30 +10,23 @@ struct Triple { Triple() {} Triple(const std::string& s, const std::string& p, const std::string& o) - : subject(s), predicate(p), object(o), document("") - { - } - - Triple(const std::string& s, const std::string& p, const std::string& o, - const std::string& d) - : subject(s), predicate(p), object(o), document(d) + : subject(s), predicate(p), object(o) { } Triple(const Triple& other) : subject(other.subject), predicate(other.predicate), - object(other.object), document(other.document) + object(other.object) { } - std::string subject, predicate, object, document; + std::string subject, predicate, object; bool operator==(const Triple& other) { - return (subject == other.subject) && + return (subject == other.subject) && (predicate == other.predicate) && - (object == other.object) && - (document == other.document); + (object == other.object); } }; diff --git a/include/sempr/entity/spatial/reference/EarthCenteredCS.hpp b/include/sempr/entity/spatial/reference/EarthCenteredCS.hpp index 5a192e8..4484b5e 100644 --- a/include/sempr/entity/spatial/reference/EarthCenteredCS.hpp +++ b/include/sempr/entity/spatial/reference/EarthCenteredCS.hpp @@ -21,6 +21,8 @@ class EarthCenteredCS : public GeocentricCS { EarthCenteredCS(); EarthCenteredCS(const core::IDGenBase*); + virtual std::size_t directionDimension(const CardinalDirection& direction) const override; + protected: virtual FilterPtr forward() const override; diff --git a/include/sempr/entity/spatial/reference/GeodeticCS.hpp b/include/sempr/entity/spatial/reference/GeodeticCS.hpp index 43bcfdc..319a466 100644 --- a/include/sempr/entity/spatial/reference/GeodeticCS.hpp +++ b/include/sempr/entity/spatial/reference/GeodeticCS.hpp @@ -22,6 +22,8 @@ class GeodeticCS : public GlobalCS { FilterList to(const GlobalCS::Ptr other) const override; + virtual std::size_t directionDimension(const CardinalDirection& direction) const override; + protected: FilterPtr forward() const override; FilterPtr reverse() const override; diff --git a/include/sempr/entity/spatial/reference/GlobalCS.hpp b/include/sempr/entity/spatial/reference/GlobalCS.hpp index 0ac177f..a56f134 100644 --- a/include/sempr/entity/spatial/reference/GlobalCS.hpp +++ b/include/sempr/entity/spatial/reference/GlobalCS.hpp @@ -4,13 +4,11 @@ #include #include -//#include - namespace sempr { namespace entity { /** Simple base class for ProjectionCS and GeographicCS, as both are root, and both need to - be able to create an OGRCoordinateTransformation to the other. It provides implementations of + be able to create a transformation to the other. It provides implementations of transformation[To|From]Root (which only return identity-matrices), getRoot (which returns this), and a method to compute the transformation from this to another global coordinate system. */ @@ -28,12 +26,20 @@ class GlobalCS : public SpatialReference { //from this to other virtual FilterList to(const GlobalCS::Ptr other) const; + enum CardinalDirection + { + NORTH, + EAST, + SOUTH, + WEST + }; + /** - * Check if this and the other global cs are equal. - * Note: This have to be override by derived class with attributes! + * @brief Find the dimension that representate the cardinal direction. + * + * @return std::size_t The dimension (X = 0, Y = 1, Z = 2) */ - virtual bool isEqual(const GlobalCS::Ptr other) const; - + virtual std::size_t directionDimension(const CardinalDirection& direction) const; protected: GlobalCS(); @@ -59,10 +65,6 @@ class GlobalCS : public SpatialReference { // Allow reverse call from childs inline FilterPtr reverse(const GlobalCS::Ptr other) const { return other->reverse(); } - /// used by both projection and geographic coordinate systems. - //#pragma db type("TEXT") - //OGRSpatialReference frame_; - private: friend class odb::access; diff --git a/include/sempr/entity/spatial/reference/LocalCS.hpp b/include/sempr/entity/spatial/reference/LocalCS.hpp index 52728fd..07005b3 100644 --- a/include/sempr/entity/spatial/reference/LocalCS.hpp +++ b/include/sempr/entity/spatial/reference/LocalCS.hpp @@ -41,6 +41,7 @@ class LocalCS : public SpatialReference { void setParent(ProjectionCS::Ptr); void setParent(GeocentricCS::Ptr); + bool isEqual(const SpatialReference::Ptr other) const override; /** Set the rotation-part of the transformation by specifying a rotation axis and an angle. diff --git a/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp b/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp index 2429f2e..ef90ff4 100644 --- a/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp +++ b/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp @@ -21,7 +21,9 @@ class LocalTangentPlaneCS : public GeocentricCS { LocalTangentPlaneCS(double lat0, double lon0, double h0 = 0); LocalTangentPlaneCS(double lat0, double lon0, double h0, const core::IDGenBase*); - bool isEqual(const GlobalCS::Ptr other) const override; + bool isEqual(const SpatialReference::Ptr other) const override; + + virtual std::size_t directionDimension(const CardinalDirection& direction) const override; protected: LocalTangentPlaneCS(); diff --git a/include/sempr/entity/spatial/reference/MilitaryGridReferenceSystem.hpp b/include/sempr/entity/spatial/reference/MilitaryGridReferenceSystem.hpp index 96a45c5..36d3f1e 100644 --- a/include/sempr/entity/spatial/reference/MilitaryGridReferenceSystem.hpp +++ b/include/sempr/entity/spatial/reference/MilitaryGridReferenceSystem.hpp @@ -21,7 +21,7 @@ class MilitaryGridReferenceSystem : public ProjectionCS { MilitaryGridReferenceSystem(int zone, char designator, const std::string& squareID); MilitaryGridReferenceSystem(int zone, char designator, const std::string& squareID, const core::IDGenBase*); - bool isEqual(const GlobalCS::Ptr other) const override; + bool isEqual(const SpatialReference::Ptr other) const override; int getZone() const override; bool isNorth() const override; diff --git a/include/sempr/entity/spatial/reference/ProjectionCS.hpp b/include/sempr/entity/spatial/reference/ProjectionCS.hpp index c575067..8d7c6f4 100644 --- a/include/sempr/entity/spatial/reference/ProjectionCS.hpp +++ b/include/sempr/entity/spatial/reference/ProjectionCS.hpp @@ -27,6 +27,7 @@ class ProjectionCS : public GlobalCS { virtual int getZone() const; virtual bool isNorth() const; + virtual std::size_t directionDimension(const CardinalDirection& direction) const override; // import or export a UTM/UPS zone string static ProjectionCS::Ptr importZone(const std::string& zonestr); diff --git a/include/sempr/entity/spatial/reference/SpatialReference.hpp b/include/sempr/entity/spatial/reference/SpatialReference.hpp index 1897a3f..1e87495 100644 --- a/include/sempr/entity/spatial/reference/SpatialReference.hpp +++ b/include/sempr/entity/spatial/reference/SpatialReference.hpp @@ -34,6 +34,12 @@ class SpatialReference : public Entity { /// not const -- might return "shared_from_this()"... virtual SpatialReference::Ptr getRoot(); + /** + * Check if this and the other cs are equal. (not the same!) + * Note: This have to be override by derived class with attributes! + */ + virtual bool isEqual(const SpatialReference::Ptr other) const; + /** Check if this SpatialReference is the child of another one. This is usefull to check if we need to recompute e.g. a Geometry based on the change of some arbitrary frame, as diff --git a/include/sempr/entity/spatial/reference/UniversalPolarStereographicCS.hpp b/include/sempr/entity/spatial/reference/UniversalPolarStereographicCS.hpp index 0ddeac2..97bc9fc 100644 --- a/include/sempr/entity/spatial/reference/UniversalPolarStereographicCS.hpp +++ b/include/sempr/entity/spatial/reference/UniversalPolarStereographicCS.hpp @@ -20,7 +20,7 @@ class UniversalPolarStereographicCS : public ProjectionCS { UniversalPolarStereographicCS(bool north); UniversalPolarStereographicCS(bool north, const core::IDGenBase*); - bool isEqual(const GlobalCS::Ptr other) const override; + bool isEqual(const SpatialReference::Ptr other) const override; int getZone() const override; bool isNorth() const override; diff --git a/include/sempr/entity/spatial/reference/UniversalTransverseMercatorCS.hpp b/include/sempr/entity/spatial/reference/UniversalTransverseMercatorCS.hpp index d416254..b799618 100644 --- a/include/sempr/entity/spatial/reference/UniversalTransverseMercatorCS.hpp +++ b/include/sempr/entity/spatial/reference/UniversalTransverseMercatorCS.hpp @@ -20,7 +20,7 @@ class UniversalTransverseMercatorCS : public ProjectionCS { UniversalTransverseMercatorCS(int zone, bool north = true); UniversalTransverseMercatorCS(int zone, bool north, const core::IDGenBase*); - bool isEqual(const GlobalCS::Ptr other) const override; + bool isEqual(const SpatialReference::Ptr other) const override; int getZone() const override; bool isNorth() const override; diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp new file mode 100644 index 0000000..b6dc92b --- /dev/null +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -0,0 +1,496 @@ +#ifndef SEMPR_PROCESSING_SPATIAL_CONCLUSION_HPP_ +#define SEMPR_PROCESSING_SPATIAL_CONCLUSION_HPP_ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include // required for EntityEvent +#include // required for EntityEvent + +#include +#include + +namespace sempr { namespace processing { + + +/** + * The SpatialConclusion is an extension to the SpatialIndex. + * It will provide RDF Triple for a set of spatial relations to allow Spatial SPARQL queries. + * The relations are pre assigned by this processing unit. + * + * @prefix geo: + * @prefix ogc: + * ogc:sfIntersects + * ogc:sfWithin + * ogc:sfContains + * ogc:sfOverlaps + * + * @prefix spatial: + * spatial:north + * spatial:south + * spatial:west + * spatial:east + * + * Note: To fullfill the GeoSPARQL like queries the SpatialEntity have to be marked in RDF as SubClassOf ogc:SpatialObject and the depending geometry as ogc:Geometry + * + * Note: Other JenaSpatial relations like nearby, withinCircle, withinBox and intersectBox are only covered by SpatialIndexQuery + * + * Info: Understanding spatial relations: http://edndoc.esri.com/arcsde/9.1/general_topics/understand_spatial_relations.htm + * + * Future on this will also provide 3D relations like: + * above + * below + * on (like above but connected!) + * under (like below but connected!) + * perpendicular + * + * Relations in discussion: + * ogc:sfEquals + * ogc:sfTouches + * ogc:sfDisjoint // negation of intersects + * ogc:sfCrosses // only for multipoint/polygon, multipoint/linestring, linestring/linestring, linestring/polygon, and linestring/multipolygon comparisons + * + * The SpatialEntity have to implement a geometry() method to get a geometry entity pointer. + * + * The ODB header for the SpatialEntity (*_odb.h) have to be included before this module get included. + */ + +template < std::size_t dim, class SpatialEntity> +class SpatialConclusion : public Module< core::EntityEvent, core::EntityEvent > +{ +public: + using Ptr = std::shared_ptr< SpatialConclusion >; + std::string type() const override { return "SpatialConclusion" + std::to_string(dim) + "D"; } + + typedef typename processing::SpatialIndexBase::ValuePair ValuePair; + typedef typename processing::SpatialIndexBase::Box Box; + + // isGlobal is set if both geometries are transformed in the same global reference system + //typedef std::function::Box& self,const SpatialIndex::Box& other, bool isGlobal)> CheckBoxFunction; + typedef std::function CheckFunction; + + SpatialConclusion(const std::weak_ptr< SpatialIndex >& spatialIndex) : + index_(spatialIndex) + { + initDefaultChecker(); + }; + + void process(std::shared_ptr< core::EntityEvent > event) override + { + typedef core::EntityEventBase::EventType EventType; + + // assume that the geometry of the spatial entity is already handeld! + switch(event->what()) { + case EventType::CREATED: + case EventType::LOADED: + addEntity(event->getEntity()); + break; + case EventType::REMOVED: + removeEntity(event->getEntity()); + break; + case EventType::CHANGED: + updateEntity(event->getEntity()); + break; + default: + // nothing + break; + } + + } + + void process(core::EntityEvent::Ptr refEvent) override + { + typedef core::EntityEvent::EventType EType; // will ony be called if the spatial reference itself is changed + + switch (refEvent->what()) + { + case EType::CREATED: + case EType::LOADED: + // nothing to do. + break; + case EType::CHANGED: + // check which geometries are affected and update them. + processChangedCS(refEvent->getEntity()); + break; + case EType::REMOVED: + // well... + // what happens if we remove a SpatialReference that some geometry is pointing to? + // (why would be do that? how can we prevent that?) + break; + } + } + + // register a new check function for a given predicate like north or east. + // The check functions could assume that the geometries are already converted in the same coordinate system! + bool registerCheckFunction(const std::string& relationPredicate, const CheckFunction& checker) + { + if (checkURI(relationPredicate)) + { + checkFunctions_[relationPredicate] = checker; + return true; + } else + { + return false; + } + } + + entity::RDFVector::Ptr getConclusion(const std::shared_ptr& entity) + { + return rdfMap_.at(entity->id()); + } + + +private: + std::weak_ptr< SpatialIndex > index_; + + std::map spatialGeometry_; + + std::map rdfMap_; // temporary rdf vector for every entity (mapped by id) + + std::map checkFunctions_; + + + void removeEntity(const std::shared_ptr& entity) + { + removeEntity(entity->id()); + } + + void removeEntity(const std::string& id) + { + rdfMap_[id]->removed(); // fire remove event + + removeGeometryLink(id); + + rdfMap_.erase(id); // remove rdf vector for the entity + removeBackRelation(id); // remove linked back relations ot his entity + } + + void processChangedCS(entity::SpatialReference::Ptr cs) + { + // we need to check every geometry currently in the index, if it is affected. + for (auto entry : spatialGeometry_) + { + if (entry.first->getCS()->isChildOf(cs)) + { + // well, in that case update it. + updateEntity(entry.first, entry.second); + } + } + } + + void addEntity(const std::shared_ptr& entity, bool change = false) + { + addEntity(entity->geometry(), entity->id(), change); + } + + void addEntity(const std::shared_ptr& geometry, const std::string& id, bool change = false) + { + if (indexedGeometry(geometry)) + { + if (!change) + { // new Entity + rdfMap_[id] = entity::RDFVector::Ptr(new entity::RDFVector(true)); // not persistant Entity + rdfMap_[id]->created(); + } + else + { // changed Entity + rdfMap_[id]->clear(); + rdfMap_[id]->changed(); + } + + spatialGeometry_[geometry] = id; // build up geometry link + checkEntity(id, geometry); + } + //otherwise skip this entity! + } + + bool indexedGeometry(const std::shared_ptr& geometry) + { + if (auto idx = index_.lock()) + { + return idx->geo2box_.find(geometry) != idx->geo2box_.end(); + } + + return false; + } + + bool removeGeometryLink(const std::string& id) + { + // the geometry could changed but the id have to be the same all the time + for (auto it = spatialGeometry_.begin(); it != spatialGeometry_.end(); ++it) + { + if ( it->second == id ) + { + spatialGeometry_.erase(it); + return true; + } + } + return false; + } + + void updateEntity(const std::shared_ptr& entity) + { + updateEntity(entity->geometry(), entity->id()); + } + + void updateEntity(const std::shared_ptr& geometry, const std::string& id) + { + // There are two alternative way to handle this case: + // 1. remove the old entity and add the updated one (easy but with many overhat) + // 2. check that have be changed and check specific for the changes. (not easy but efficient) + // For now we take the first option + removeGeometryLink(id); + removeBackRelation(id); + addEntity(geometry, id, true); + } + + void removeBackRelation(const std::string& id) + { + std::string objURI = sempr::buildURI(id, sempr::baseURI()); + + for (auto rdfIt = rdfMap_.begin(); rdfIt != rdfMap_.end(); ++rdfIt) + { + std::vector toRemove; + + for (auto tIt = rdfIt->second->begin(); tIt != rdfIt->second->end(); ++tIt) + { + // search for object id in back linked rfd triple + if ((*tIt).object == objURI) + { + toRemove.emplace_back(*tIt); + } + } + + bool rdfChanged = false; + for (auto remove : toRemove) + { + rdfIt->second->removeTriple(remove); + rdfChanged = true; + } + + if (rdfChanged) + rdfIt->second->changed(); + + } + + } + + + // Note: will not create a event for the self depending rdf vector + void checkEntity(const std::string& id, const entity::Geometry::Ptr& self) + { + auto idx = index_.lock(); + if (idx) + { + auto selfPair = idx->geo2box_[self]; + + std::set changedRDF; //set for all changed RDFVectors by this method + + // check every registered box check function + for (auto checkBoxIt = checkFunctions_.begin(); checkBoxIt != checkFunctions_.end(); ++checkBoxIt) + { + + for (auto other : idx->geo2box_) + { + if (selfPair.second == other.second.second) + continue; //skip conclusion if self == other! + + //check from self to others + bool selfRelated = checkBoxIt->second(selfPair, other.second, index_.lock()->rootCS_); + if (selfRelated) + { + // Build Triple: SelfId, Function predicate, OtherID + entity::Triple t( sempr::buildURI(id, sempr::baseURI()), + checkBoxIt->first, + sempr::buildURI(spatialGeometry_.at(other.first), sempr::baseURI()) ); + rdfMap_[id]->addTriple(t, true); + } + + //check from others to self + bool otherRelated = checkBoxIt->second(other.second, selfPair, index_.lock()->rootCS_); + if (otherRelated) + { + auto otherID = spatialGeometry_.at(other.first); + // Build Triple: OtherID, Function predicate, SelfId + entity::Triple t( sempr::buildURI(otherID, sempr::baseURI()), + checkBoxIt->first, + sempr::buildURI(id, sempr::baseURI()) ); + rdfMap_[otherID]->addTriple(t, true); + changedRDF.insert(rdfMap_[otherID]); //mark vector as changed + } + } + + } + + // notify changes of rdf vectors from others + for (auto rdfVector : changedRDF) + rdfVector->changed(); + } + + } + + bool checkURI(const std::string& uri) + { + bool isURI = true; + + try { + isURI = isURI && uri.at(0) == '<'; + isURI = isURI && uri.at(uri.length() - 1) == '>'; + isURI = isURI && uri.find(':') != std::string::npos; + } catch (const std::out_of_range& oor) { + return false; + } + + return isURI; + } + + bool checkShortcut(const std::string& uri) + { + bool isShortcut = true; + try { + isShortcut = isShortcut && uri.at(0) != '<'; + isShortcut = isShortcut && uri.at(uri.length() - 1) != '>'; + isShortcut = isShortcut && uri.find(':') != std::string::npos; // a shortcut need a ':' between prefix and id. + isShortcut = isShortcut && uri.find('#') == std::string::npos; // a shortcut will not contain a hash symbol. + } catch (const std::out_of_range& oor) { + return false; + } + + return isShortcut; + } + + // register the default set of check functions + void initDefaultChecker() + { + registerCheckFunction("", directionCheck); + registerCheckFunction("", directionCheck); + registerCheckFunction("", directionCheck); + registerCheckFunction("", directionCheck); + + registerCheckFunction("", intersectionCheck); + registerCheckFunction("", withinCheck); + registerCheckFunction("", containsCheck); + registerCheckFunction("", overlapsCheck); + + } + + // A generic direction check function for north east south west relations. The direction is equal to the GlobalCS direction enum. + // The test assume that both pairs are in the same global spatial reference system. + template + static bool directionCheck(const ValuePair& self, const ValuePair& other, const entity::SpatialReference::Ptr& ref) + { + auto globalRef = std::dynamic_pointer_cast(ref); + + if (globalRef) + { + const std::size_t axis = globalRef->directionDimension(direction); + bool positive = direction <= 1; // North and East are the positive directions + + auto selfBox = self.first; + auto otherBox = other.first; + + if (positive) + { + auto selfMin = getMin(selfBox, axis); + auto otherMax = getMax(otherBox, axis); + return selfMin >= otherMax; + } + else + { + auto selfMax = getMax(selfBox, axis); + auto otherMin = getMin(otherBox, axis); + return selfMax <= otherMin; + } + } + + return false; + } + + static bool intersectionCheck(const ValuePair& self, const ValuePair& other, const entity::SpatialReference::Ptr& ref) + { + if (boost::geometry::intersects(self.first, other.first)) + { + if (self.second && other.second) + { + return self.second->getGeometry()->intersects(other.second->getGeometry()); + } + return true; + } + return false; + } + + static bool withinCheck(const ValuePair& self, const ValuePair& other, const entity::SpatialReference::Ptr& ref) + { + if (boost::geometry::within(self.first, other.first)) + { + if (self.second && other.second) + { + return self.second->getGeometry()->within(other.second->getGeometry()); + } + return true; + } + return false; + } + + static bool containsCheck(const ValuePair& self, const ValuePair& other, const entity::SpatialReference::Ptr& ref) + { + if (boost::geometry::within(self.first, other.first)) + { + if (self.second && other.second) + { + return self.second->getGeometry()->contains(other.second->getGeometry()); + } + return true; + } + return false; + } + + static bool overlapsCheck(const ValuePair& self, const ValuePair& other, const entity::SpatialReference::Ptr& ref) + { + if (boost::geometry::overlaps(self.first, other.first)) + { + if (self.second && other.second) + { + return self.second->getGeometry()->overlaps(other.second->getGeometry()); + } + return true; + } + return false; + } + + + static double getMin(const Box& box, const std::size_t axis) + { + //return bg::get(box.min_corner()); //constexpr. error! + return processing::SpatialIndexBase::toEigen(box.min_corner())[axis]; + } + + static double getMax(const Box& box, const std::size_t axis) + { + //return bg::get(box.max_corner()); //constexpr. error + return processing::SpatialIndexBase::toEigen(box.max_corner())[axis]; + } + + +}; + +template +using SpatialConclusion2D = SpatialConclusion<2, SpatialEntity>; + +template +using SpatialConclusion3D = SpatialConclusion<3, SpatialEntity>; + + +}} + +#endif /* end of include guard SEMPR_PROCESSING_SPATIAL_CONCLUSION_HPP_ */ diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index f2291bb..31535f5 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -3,6 +3,9 @@ #include #include +#include + +#include #include #include @@ -11,17 +14,23 @@ #include +#include + #include // required for EntityEvent #include // required for EntityEvent #include #include +#include namespace sempr { namespace processing { namespace bg = boost::geometry; namespace bgi = boost::geometry::index; +template < std::size_t dim, class SpatialEntity> +class SpatialConclusion; + /** This class implements a spatial index for geometry-entities. It listens to events of Geometry @@ -36,61 +45,426 @@ namespace bgi = boost::geometry::index; The bridge between ProjectionCS and GeographicCS could be a bit more difficult as we will need coordinate transformations done by GDAL in the process. */ -class SpatialIndex - : public Module< core::EntityEvent, +/** + * Spatial Index for one specific root coordinate system. + * All entities that could not be tranformes to this coordinate system will be skipped! + * Note that the SpatialIndex currently only support 3D Indexing. 2D Geometries will be projected to the zero x/y plane. + */ +template< std::size_t dim = 3 > +class SpatialIndex : + public Module< core::EntityEvent, core::EntityEvent, - query::SpatialIndexQuery > + query::SpatialIndexQuery >, + SpatialIndexBase { public: - using Ptr = std::shared_ptr; - std::string type() const override; + typedef typename processing::SpatialIndexBase::Box Box; + typedef typename processing::SpatialIndexBase::Point Point; + typedef typename processing::SpatialIndexBase::ValuePair ValuePair; + typedef typename SpatialIndexBase::RTree RTree; - SpatialIndex(); + using Ptr = std::shared_ptr< SpatialIndex >; + std::string type() const override { return "SpatialIndex" + std::to_string(dim) + "D"; } - /** - Answer a SpatialIndexQuery - */ - void process(query::SpatialIndexQuery::Ptr query) override; + SpatialIndex(entity::SpatialReference::Ptr rootCS) : + rootCS_(rootCS) + {} /** - Specify what is stored in the R-Tree: - boxes, made out of points, consisting of 3 floats, in cartesian space. - NOTE: Boost seems to support geographic and spherical coordinates (lat-long etc) here, how - does this affect the RTree? Can we use this to support indexing on lat-lon later on? + Answer a SpatialIndexQuery */ - typedef bg::model::point bPoint; - typedef bg::model::box bBox; - typedef std::pair bValue; - typedef bgi::rtree > RTree; + void process(std::shared_ptr< query::SpatialIndexQuery > query) override + { + std::vector tmpResults; + + try + { + // Transform Box and Geom of the Pair into the root ref system of the index. + auto transformedPair = transformToRoot(query->refBoxGeometryPair(), query->refCS()); + + Box region = transformedPair.first; + + switch (query->mode()) + { + case SpatialQueryType::NEAREST: + rtree_.query(bgi::nearest(region, 1), std::back_inserter(tmpResults)); // Note: could be more than only the nearest one! + break; + + case SpatialQueryType::WITHIN: + rtree_.query(bgi::within(region), std::back_inserter(tmpResults)); + break; + case SpatialQueryType::NOT_WITHIN: + rtree_.query(!bgi::within(region), std::back_inserter(tmpResults)); + break; + + // TODO: contains is introduced in boost 1.55, but ros indigo needs 1.54. + // maybe its time for me to upgrade to 16.04 and kinetic... + case SpatialQueryType::CONTAINS: + rtree_.query(bgi::contains(region), std::back_inserter(tmpResults)); + break; + case SpatialQueryType::NOT_CONTAINS: + rtree_.query(!bgi::contains(region), std::back_inserter(tmpResults)); + break; + + case SpatialQueryType::INTERSECTS: + rtree_.query(bgi::intersects(region), std::back_inserter(tmpResults)); + break; + case SpatialQueryType::NOT_INTERSECTS: + rtree_.query(!bgi::intersects(region), std::back_inserter(tmpResults)); + break; + + case SpatialQueryType::COVERED: + rtree_.query(bgi::covered_by(region), std::back_inserter(tmpResults)); + break; + case SpatialQueryType::NOT_COVERED: + rtree_.query(!bgi::covered_by(region), std::back_inserter(tmpResults)); + break; + + case SpatialQueryType::OVERLAPS: + rtree_.query(bgi::overlaps(region), std::back_inserter(tmpResults)); + break; + case SpatialQueryType::NOT_OVERLAPS: + rtree_.query(!bgi::overlaps(region), std::back_inserter(tmpResults)); + break; + + default: + std::cout << "SpatialIndex: Mode " << int(query->mode()) << " not implemented." << std::endl; + } + + // Also check if the founded pairs will pass the geometry and not only the box check. + // Only changes if the search contains a reference geometry. + checkGeometry(transformedPair, query->mode(), tmpResults); + + } + catch (const TransformException& ex) + { + //ref geom of the query is in a different cs. Not results. + } + + for (auto result : tmpResults) + { + query->results.insert(findEntry(result)); + } + + } + + const std::map& getGeoBoxes() const { return geo2box_; } private: + template < std::size_t dimension, class SpatialEntity> + friend class SpatialConclusion; + /** - The actual R-Tree. + * The actual R-Tree. */ RTree rtree_; /** - A mapping of Geometry-->bValue for easier updates of the RTree + * The root reference system for this spatial index and R-Tree + */ + entity::SpatialReference::Ptr rootCS_; + + /** + * A mapping of Geometry-->bValue for easier updates of the RTree */ - std::map geo2box_; + std::map geo2box_; /** - Process changes of geometries: New are inserted into the RTree, changed are recomputed + * Process changes of geometries: New are inserted into the RTree, changed are recomputed */ - void process(core::EntityEvent::Ptr geoEvent) override; - void process(core::EntityEvent::Ptr refEvent) override; - void processChangedCS(entity::SpatialReference::Ptr cs); + void process(core::EntityEvent::Ptr geoEvent) override + { + typedef core::EntityEvent::EventType EType; + entity::Geometry::Ptr geo = geoEvent->getEntity(); + + switch (geoEvent->what()) + { + case EType::CREATED: + case EType::LOADED: + insertGeo(geo); + break; + case EType::CHANGED: + updateGeo(geo); + break; + case EType::REMOVED: + removeGeo(geo); + break; + } + } + + void process(core::EntityEvent::Ptr refEvent) override + { + typedef core::EntityEvent::EventType EType; // will ony be could if the spatial reference itself is changed + + switch (refEvent->what()) + { + case EType::CREATED: + case EType::LOADED: + // nothing to do. + break; + case EType::CHANGED: + // check which geometries are affected and update them. + processChangedCS(refEvent->getEntity()); + break; + case EType::REMOVED: + // well... + // what happens if we remove a SpatialReference that some geometry is pointing to? + // (why would be do that? how can we prevent that?) + break; + } + } + + void processChangedCS(entity::SpatialReference::Ptr cs) + { + // we need to check every geometry currently in the index, if it is affected. + for (auto entry : geo2box_) + { + if (entry.first->getCS()->isChildOf(cs)) + { + // well, in that case update it. + updateGeo(entry.first); + } + } + } // the actual processing: - void insertGeo(entity::Geometry::Ptr geo); - void updateGeo(entity::Geometry::Ptr geo); - void removeGeo(entity::Geometry::Ptr geo); + void insertGeo(entity::Geometry::Ptr geo) + { + // sanity: it needs a geometry, and a spatial reference. + if (!geo->getGeometry() || !geo->getCS()) return; + + // skip geometry with a wrong coordinate dimension + if (geo->getGeometry()->getCoordinateDimension() != dim) + { + std::cout << "2D Geometry in 3D Index! -- skip this!" << std::endl; + return; + } + + try + { + // create a new entry + ValuePair entry = createEntry(geo); + // save it in our map + geo2box_[geo] = entry; + // and insert it into the RTree + rtree_.insert(entry); + } + catch (const TransformException& ex) + { + // this will skip the geometry if there is no transformation to the root cs of the spatial index + } + } + + void updateGeo(entity::Geometry::Ptr geo) + { + // update? lets remove the old one first. + removeGeo(geo); + + // sanity: it needs a geometry, and a spatial reference. + if (!geo->getGeometry() || !geo->getCS()) return; + + // and re-insert it with updated data. + insertGeo(geo); + } + + void removeGeo(entity::Geometry::Ptr geo) + { + // find the map-entry + auto it = geo2box_.find(geo); + if (it != geo2box_.end()) + { + // remove from rtree + rtree_.remove(it->second); + // and from the map + geo2box_.erase(it); + } + } /** Create a pair of bounding-box and ptr */ - bValue createEntry(entity::Geometry::Ptr geo); + ValuePair createEntry(entity::Geometry::Ptr geo, bool query = false) const //could throw an exception if there is no tranformation to the rootCS + { + // get the 3D envelope of the geometry. + geos::geom::Coordinate geoMin, geoMax; + geo->findEnvelope(geoMin, geoMax); + + // build and transform a the envelope as + entity::MultiPoint::Ptr mpEntity = buildGeometryBox(geoMin, geoMax, geo->getCS()); + + Box box = buildBox(mpEntity); + + // create a transformed copy of the geometry. + auto geom = geo->clone(); + geom->transformToCS(rootCS_); + + return ValuePair(box, geom); + } + + + entity::Geometry::Ptr findEntry(const ValuePair value) const + { + for (auto it = geo2box_.begin(); it != geo2box_.end(); ++it) + { + if (it->second.second == value.second) + return it->first; + } + + return nullptr; + } + + + // check if a given pre-set of results also match the geometry based test. + void checkGeometry(const ValuePair& ref, SpatialQueryType queryType, std::vector& results) + { + if (ref.second) + { + switch (queryType) + { + case SpatialQueryType::WITHIN: + { + for (auto resultIt = results.begin(); resultIt != results.end();) + if (!ref.second->getGeometry()->within(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + else + ++resultIt; + + break; + } + case SpatialQueryType::NOT_WITHIN: + { + for (auto resultIt = results.begin(); resultIt != results.end();) + if (ref.second->getGeometry()->within(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + else + ++resultIt; + + break; + } + + case SpatialQueryType::CONTAINS: + { + for (auto resultIt = results.begin(); resultIt != results.end();) + if (!ref.second->getGeometry()->contains(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + else + ++resultIt; + + break; + } + case SpatialQueryType::NOT_CONTAINS: + { + for (auto resultIt = results.begin(); resultIt != results.end();) + if (ref.second->getGeometry()->contains(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + else + ++resultIt; + + break; + } + + case SpatialQueryType::INTERSECTS: + { + for (auto resultIt = results.begin(); resultIt != results.end();) + { + if (!ref.second->getGeometry()->intersects(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + else + ++resultIt; + } + break; + } + case SpatialQueryType::NOT_INTERSECTS: + { + for (auto resultIt = results.begin(); resultIt != results.end();) + if (ref.second->getGeometry()->intersects(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + else + ++resultIt; + + break; + } + + default: + std::cout << "SpatialIndex: Mode " << int(queryType) << " no Geometry Check implemented." << std::endl; + } + } + } + + + ValuePair transformToRoot(const ValuePair& pair, entity::SpatialReference::Ptr cs) const + { + ValuePair transPair; + + if (pair.second) + { + transPair.second = pair.second->clone(); + transPair.second->transformToCS(rootCS_); + } + + // Transform the box by the geometry of the box + auto min = this->toCoordinate(pair.first.min_corner()); + auto max = this->toCoordinate(pair.first.max_corner()); + auto boxGeom = buildGeometryBox(min, max, cs); + transPair.first = buildBox(boxGeom); + + return transPair; + } + + entity::MultiPoint::Ptr buildGeometryBox(const geos::geom::Coordinate& geoMin, const geos::geom::Coordinate& geoMax, entity::SpatialReference::Ptr srcCS) const + { + /* + if (dim == 3) && ( (geoMin.z != geoMin.z) || (geoMax.z != geoMax.z) ) + throw TransformException("2D Geometry in " + type() + " but marked as " + std::to_string(dim) + "D coordinate."); //throw exception otherwise boost rtree will throw a confusion one! + */ + + // this envelope is in the coordinate system of the geometry. But what we need is an envelope + // that is axis aligned with the root reference system. We could transform the geometry to root, + // take the envelope and transform it back, but that is just ridiculus. Instead: Create a + // bounding-box-geometry (8 points, one for each corner), transform it, and take its envelope. + // + // Note z-coordinate by default is NaN in 2D (defined by geos::geom) + // --- + // create a geometry with the matching extends: 8 point, every corner must be checked! + + entity::MultiPoint::Ptr mpEntity( new entity::MultiPoint() ); // Note: this wast IDs - recommended to use a factory in future + + std::vector cornerCoordinates = this->buildCorners(geoMin, geoMax); + mpEntity->setCoordinates(cornerCoordinates); + mpEntity->setCS(srcCS); + + // transfrom it to the reference system of the R-Tree + mpEntity->transformToCS(rootCS_); //could throw an exception if there is no transformation from geo cs to root cs + + return mpEntity; + } + + Box buildBox(entity::Geometry::Ptr geomBox) const + { + /* + // really strange happenings - this is different to the manually apply of the env filter!!! + geos::geom::Coordinate mpMin, mpMax; + geo->findEnvelope(mpMin, mpMax); + + // create the bBox out of bPoints. + bBox box( + bPoint(mpMin.x, mpMin.y, mpMin.z), + bPoint(mpMax.x, mpMax.y, mpMax.z) + ); + + */ + + EnvelopeFilter ef; + geomBox->getGeometry()->apply_ro(ef); + + auto min = this->toPoint(ef.getMin()); + auto max = this->toPoint(ef.getMax()); + return Box(min, max); + } + }; +typedef SpatialIndex<2> SpatialIndex2D; +typedef SpatialIndex<3> SpatialIndex3D; }} diff --git a/include/sempr/processing/SpatialIndexBase.hpp b/include/sempr/processing/SpatialIndexBase.hpp new file mode 100644 index 0000000..b9c12a9 --- /dev/null +++ b/include/sempr/processing/SpatialIndexBase.hpp @@ -0,0 +1,153 @@ +#ifndef SEMPR_PROCESSING_SPATIALINDEXBASE_HPP_ +#define SEMPR_PROCESSING_SPATIALINDEXBASE_HPP_ + +#include + +#include + +#include +#include +#include +#include + +#include // required for EntityEvent +#include // required for EntityEvent + +// SQLite is defining #define NOT_WITHIN 0 inside the sqlite3.h. We undfine it here locally because the processing units are not depending of SQLite. +#undef NOT_WITHIN + +namespace sempr { namespace processing { + +namespace bg = boost::geometry; +namespace bgi = boost::geometry::index; + +// negative constraints have to be odd! +enum class SpatialQueryType { + NEAREST, + WITHIN = 2, NOT_WITHIN, + CONTAINS = 4, NOT_CONTAINS, + INTERSECTS = 6, NOT_INTERSECTS, + COVERED = 8, NOT_COVERED, + OVERLAPS = 10, NOT_OVERLAPS +}; + +template +struct SpatialIndexBase +{ + /** + Specify what is stored in the R-Tree: + boxes, made out of points, consisting of 3 double, in cartesian space. + NOTE: Boost seems to support geographic and spherical coordinates (lat-long etc) here, how + does this affect the RTree? Can we use this to support indexing on lat-lon later on? + */ +}; + + +template<> +struct SpatialIndexBase<2> +{ + typedef bg::model::point Point; + typedef bg::model::box Box; + typedef std::pair ValuePair; // pair af a bounding box and a translated geometry clone + typedef bgi::rtree > RTree; + + typedef Eigen::Matrix Vector; + + + static geos::geom::Coordinate toCoordinate(const Point& point) + { + return geos::geom::Coordinate(bg::get<0>(point), bg::get<1>(point)); + } + + static Point toPoint(const geos::geom::Coordinate& coord) + { + return Point(coord.x, coord.y); + } + + static Point toPoint(const Vector& vec) + { + return Point(vec[0], vec[1]); + } + + static Vector toEigen(const geos::geom::Coordinate& coord) + { + return Vector(coord.x, coord.y); + } + + static Vector toEigen(const Point& point) + { + return Vector(bg::get<0>(point), bg::get<1>(point)); + } + + static std::vector buildCorners(const geos::geom::Coordinate& min, const geos::geom::Coordinate& max) + { + std::vector cornerCoordinates; + + geos::geom::Coordinate coord; + coord = geos::geom::Coordinate(min.x, min.y); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(min.x, max.y); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(max.x, min.y); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(max.x, max.y); cornerCoordinates.push_back(coord); + + return cornerCoordinates; + } +}; + +template<> +struct SpatialIndexBase<3> +{ + typedef bg::model::point Point; + typedef bg::model::box Box; + typedef std::pair ValuePair; // pair af a bounding box and a translated geometry clone + typedef bgi::rtree > RTree; + + typedef Eigen::Matrix Vector; + + + static geos::geom::Coordinate toCoordinate(const Point& point) + { + return geos::geom::Coordinate(bg::get<0>(point), bg::get<1>(point), bg::get<2>(point)); + } + + static Point toPoint(const geos::geom::Coordinate& coord) + { + return Point(coord.x, coord.y, coord.z); + } + + static Point toPoint(const Vector& vec) + { + return Point(vec[0], vec[1], vec[2]); + } + + static Vector toEigen(const geos::geom::Coordinate& coord) + { + return Vector(coord.x, coord.y, coord.z); + } + + static Vector toEigen(const Point& point) + { + return Vector(bg::get<0>(point), bg::get<1>(point), bg::get<2>(point)); + } + + static std::vector buildCorners(const geos::geom::Coordinate& min, const geos::geom::Coordinate& max) + { + std::vector cornerCoordinates; + + geos::geom::Coordinate coord; + coord = geos::geom::Coordinate(min.x, min.y, min.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(min.x, min.y, max.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(min.x, max.y, min.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(min.x, max.y, max.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(max.x, min.y, min.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(max.x, min.y, max.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(max.x, max.y, min.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(max.x, max.y, max.z); cornerCoordinates.push_back(coord); + + return cornerCoordinates; + } +}; + + +}} + +#endif /* end of include guard SEMPR_PROCESSING_SPATIALINDEX_HPP_ */ diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index 66ce1ce..1d7d87f 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -2,11 +2,19 @@ #define SEMPR_QUERY_SPATIALQUERY_HPP_ #include +#include #include +#include #include -#include #include +#include + +#include +#include +#include +#include + namespace sempr { namespace query { @@ -23,97 +31,212 @@ namespace sempr { namespace query { beyond the comparison of bounding boxes. Any work/checks on concrete geometries must be done by the user or a specialized processing module. */ -class SpatialIndexQuery : public Query, public core::OType { +template +class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery > { public: - using Ptr = std::shared_ptr; - ~SpatialIndexQuery(); + typedef processing::SpatialQueryType SpatialQueryType; + typedef processing::SpatialIndexBase SpatialIndexBase; + typedef typename SpatialIndexBase::Box Box; + typedef typename SpatialIndexBase::Point Point; + typedef typename SpatialIndexBase::ValuePair ValuePair; + typedef typename SpatialIndexBase::Vector Vector; - std::string type() const override { return "SpatialIndexQuery"; } + using Ptr = std::shared_ptr< SpatialIndexQuery >; + ~SpatialIndexQuery() {} + + std::string type() const override { return "SpatialIndexQuery" + std::to_string(dim) + "D"; } /** The set of geometries matching the criterium */ std::set results; - /* - NOTE: I'd prefer "NOT_WITHIN" etc instead of "NOTWITHIN", but sadly newer versions of - libsqlite3-dev (3.11) have a - #define NOT_WITHIN 0 - inside of sqlite3.h, which makes the preprocessor expand it here, and the compiler - throwing an error pointing at NOT_WITHIN... - */ - enum QueryType { - WITHIN = 0, NOTWITHIN, - CONTAINS, NOTCONTAINS, - INTERSECTS, NOTINTERSECTS - }; + /** gets the reference bounding box and geometry pair. It could either be 2D or 3D. The geometry will be null if its only a box based query. */ + ValuePair refBoxGeometryPair() { return refPair_; } + entity::SpatialReference::Ptr refCS() { return referenceCS_; } /** returns the mode of the query: WITHIN, NOT_WITHIN, ... */ - QueryType mode() const; + SpatialQueryType mode() const { return qtype_; } + /** set the mode of the query */ - void mode(QueryType m); - /** inverts the mode: WITHIN <-> NOTWITHIN etc. */ - void invert(); + void mode(SpatialQueryType m) { qtype_ = m; } - /** get hold of the reference geometry (the 8 cornerpoints of the bounding box) */ - entity::Geometry::Ptr refGeo(); + /** inverts the mode: WITHIN <-> NOTWITHIN etc. */ + void invert() + { + if (qtype_ == SpatialQueryType::NEAREST ) return; + /* inversion of query type: + positive (WITHIN, CONTAINS, INTERSECTS) are even (0, 2, 4), + negative (NOT_WITHIN, ...) are odd (1, 3, 5). + Just increment even and decrement odd values. */ + qtype_ = (int(qtype_) % 2 == 0) ? SpatialQueryType(int(qtype_)+1) : SpatialQueryType(int(qtype_)-1); + } /** Query for everything within the bounding box of 'geometry'. Passes the Envelope3D of the geometry to 'withinBox'. */ - static SpatialIndexQuery::Ptr withinBoxOf(entity::Geometry::Ptr geometry); - static SpatialIndexQuery::Ptr containsBoxOf(entity::Geometry::Ptr geometry); - static SpatialIndexQuery::Ptr intersectsBoxOf(entity::Geometry::Ptr geometry); + static SpatialIndexQuery::Ptr withinBoxOf(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createBoxQuery(geometry, SpatialQueryType::WITHIN); + } + + static SpatialIndexQuery::Ptr containsBoxOf(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createBoxQuery(geometry, SpatialQueryType::CONTAINS); + } + + static SpatialIndexQuery::Ptr intersectsBoxOf(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createBoxQuery(geometry, SpatialQueryType::INTERSECTS); + } + + static SpatialIndexQuery::Ptr nearestToBoxOf(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createBoxQuery(geometry, SpatialQueryType::NEAREST); + } + + static SpatialIndexQuery::Ptr coveredByBoxOf(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createBoxQuery(geometry, SpatialQueryType::COVERED); + } + + static SpatialIndexQuery::Ptr overlapsBoxOf(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createBoxQuery(geometry, SpatialQueryType::COVERED); + } + + + /** + Query for everything within the 'geometry'. + Passes the Envelope3D of the geometry to 'withinBox' to speed up the query. + */ + static SpatialIndexQuery::Ptr within(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createQuery(geometry, SpatialQueryType::WITHIN); + } + + static SpatialIndexQuery::Ptr contains(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createQuery(geometry, SpatialQueryType::CONTAINS); + } + + static SpatialIndexQuery::Ptr intersects(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createQuery(geometry, SpatialQueryType::INTERSECTS); + } /** Query for everything within the bbox specified. Explicit coordinate system. This creates a new GeometryCollection that is used for the query. */ - static SpatialIndexQuery::Ptr withinBox(const Eigen::Vector3d& lower, - const Eigen::Vector3d& upper, - entity::SpatialReference::Ptr cs); - static SpatialIndexQuery::Ptr containsBox( const Eigen::Vector3d& lower, - const Eigen::Vector3d& upper, - entity::SpatialReference::Ptr cs); - static SpatialIndexQuery::Ptr intersectsBox(const Eigen::Vector3d& lower, - const Eigen::Vector3d& upper, - entity::SpatialReference::Ptr cs); - /** - TODO: how to say "not within?" - This would be nice: - SpatialIndexQuery::Ptr q = !SpatialIndexQuery::within(geo); - But we also want to get a SpatialIndexQuery::Ptr, for which an overloaded operator!() could - break stuff. Implementing it only for SpatialIndexQuery (w/o Ptr) would lead to... - SpatialIndexQuery::Ptr q = !(*SpatialIndexQuery::within(geo)); - So what about: - SpatialIndexQuery::Ptr q = SpatialIndexQuery::within(geo); - q->invert(); - */ + static SpatialIndexQuery::Ptr withinBox(const Vector& lower, const Vector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialQueryType::WITHIN); + } + + static SpatialIndexQuery::Ptr containsBox(const Vector& lower, const Vector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialQueryType::CONTAINS); + } + + static SpatialIndexQuery::Ptr intersectsBox(const Vector& lower, const Vector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialQueryType::INTERSECTS); + } + + static SpatialIndexQuery::Ptr nearestBox(const Vector& lower, const Vector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialQueryType::NEAREST); + } + + static SpatialIndexQuery::Ptr coveredByBox(const Vector& lower, const Vector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialQueryType::COVERED); + } + + static SpatialIndexQuery::Ptr overlapsBox(const Vector& lower, const Vector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialQueryType::OVERLAPS); + } + + // TODO Query for a specific geometry relations not only Boxes! +private: + + // the actual type + SpatialQueryType qtype_; + // the reference pair of bounding box and geometry + ValuePair refPair_; - // TODO CONTAINS and INTERSECTS -private: - // helper: construct collection of bbox-corner-points from a geometry - void setupRefGeo(const Eigen::Vector3d& lower, const Eigen::Vector3d& upper, - entity::SpatialReference::Ptr cs); + // the reference coordinate system of this query. (same as geometry reference if geometry is not null) + entity::SpatialReference::Ptr referenceCS_; + + // use static methods to construct queries + SpatialIndexQuery() {} + + void setupPair(const Vector& lower, const Vector& upper, entity::Geometry::Ptr geo, entity::SpatialReference::Ptr cs) + { + ValuePair pair; + pair.first = createBox(lower, upper); + pair.second = geo; + + this->refPair_ = pair; + this->referenceCS_ = cs; + } + + Box createBox(const Vector& min, const Vector& max) + { + auto minP = SpatialIndexBase::toPoint(min); + auto maxP = SpatialIndexBase::toPoint(max); + return Box(minP, maxP); + } + + static SpatialIndexQuery::Ptr createQuery(entity::Geometry::Ptr geometry, SpatialQueryType type) + { + auto cs = geometry->getCS(); + if (!cs) return SpatialIndexQuery::Ptr(); + + geos::geom::Coordinate minCoord, maxCoord; + geometry->findEnvelope(minCoord, maxCoord); + + auto min = SpatialIndexBase::toEigen(minCoord); + auto max = SpatialIndexBase::toEigen(maxCoord); + + SpatialIndexQuery::Ptr query(new SpatialIndexQuery()); + query->setupPair(min, max, geometry, cs); + query->mode(type); + return query; + } // helper: create query from geo or upper/lower and a type - static SpatialIndexQuery::Ptr createQuery(entity::Geometry::Ptr geo, QueryType type); - static SpatialIndexQuery::Ptr createQuery(const Eigen::Vector3d& lower, const Eigen::Vector3d& upper, - entity::SpatialReference::Ptr cs, QueryType type); + static SpatialIndexQuery::Ptr createBoxQuery(entity::Geometry::Ptr geometry, SpatialQueryType type) + { + geos::geom::Coordinate min, max; + geometry->findEnvelope(min, max); + auto lower = SpatialIndexBase::toEigen(min); + auto upper = SpatialIndexBase::toEigen(max); - // use static methods to construct queries - SpatialIndexQuery(); + return createBoxQuery(lower, upper, geometry->getCS(), type); + } - // the actual type - QueryType qtype_; + static SpatialIndexQuery::Ptr createBoxQuery(const Vector& min, const Vector& max, + entity::SpatialReference::Ptr cs, SpatialQueryType type) + { + if (!cs) return SpatialIndexQuery::Ptr(); + + SpatialIndexQuery::Ptr query(new SpatialIndexQuery()); + query->setupPair(min, max, nullptr, cs); + query->mode(type); + return query; + } - // the reference geometry - entity::Geometry::Ptr refGeo_; }; +typedef SpatialIndexQuery<2> SpatialIndexQuery2D; +typedef SpatialIndexQuery<3> SpatialIndexQuery3D; + }} #endif /* end of include guard SEMPR_QUERY_SPATIALQUERY_HPP_ */ diff --git a/src/core/RDF.cpp b/src/core/RDF.cpp index 0240110..15deca7 100644 --- a/src/core/RDF.cpp +++ b/src/core/RDF.cpp @@ -16,6 +16,26 @@ namespace sempr { return t; } + /** + Build a URI based on the sempr::baseURI and the given string in "" format. + */ + const std::string buildURI(const std::string& id, const std::string& baseURI) { + return std::string( "<" + baseURI + id + ">" ); + } + + /** + Extract the ID of a given sempr URI + */ + const std::string extractID(const std::string& uri, const std::string& baseURI) { + auto pos = uri.find(baseURI); + if (pos != std::string::npos) { + auto id = uri.substr(pos + baseURI.length(), uri.length()-pos-baseURI.length()-1); + return id; + } + else + return ""; + } + namespace core { namespace xsd { diff --git a/src/entity/GeometricObject.cpp b/src/entity/GeometricObject.cpp new file mode 100644 index 0000000..3c66134 --- /dev/null +++ b/src/entity/GeometricObject.cpp @@ -0,0 +1,66 @@ +#include +#include + + +namespace sempr { namespace entity { + +SEMPR_ENTITY_SOURCE(GeometricObject) + +GeometricObject::GeometricObject() : + GeometricObject(new core::IDGen()) +{ +} + +GeometricObject::GeometricObject(bool temporary) : + GeometricObject(new core::IDGen(), temporary) +{ +} + +GeometricObject::GeometricObject(const core::IDGenBase* idgen, bool temporary) : + SemanticEntity(idgen, temporary) +{ + setDiscriminator(); + + SemanticEntity::registerProperty(sempr::buildURI("subClassOf", sempr::core::rdfs::baseURI()) , ""); +} + +void GeometricObject::geometry(const Geometry::Ptr geometry) +{ + if (geometry_) + SemanticEntity::removeProperty(sempr::buildURI(geometry_->id(), sempr::baseURI()), sempr::buildURI("subClassOf", sempr::core::rdfs::baseURI()), ""); + + geometry_ = geometry; + + if (geometry_) // cover nullptr set case + SemanticEntity::registerProperty(sempr::buildURI(geometry_->id(), sempr::baseURI()), sempr::buildURI("subClassOf", sempr::core::rdfs::baseURI()), ""); +} + +void GeometricObject::type(const std::string& type) +{ + SemanticEntity::removeProperty(sempr::buildURI("type", sempr::core::rdf::baseURI()), type_); // will remove the first equal one! + + type_ = type; + + SemanticEntity::registerProperty(sempr::buildURI("type", sempr::core::rdf::baseURI()), type_); +} + +void GeometricObject::registerProperty(const std::string& predicate, const std::string& object) +{ + std::string obj = object; // needed! if you try to passthrough object directly you will receive a compiler error based on type missmatch of const&! + SemanticEntity::registerProperty(predicate, obj); +} + +std::set GeometricObject::related(const std::string& predicate) +{ + std::set objects; + for(auto t : *this) + { + // empty predicate will add all related object to the result list + if ( predicate.empty() || t.predicate == predicate) + objects.insert(t.object); + } + return objects; +} + +} /* entity */ +} /* sempr */ diff --git a/src/entity/RDFDocument.cpp b/src/entity/RDFDocument.cpp index ceca016..d318a81 100644 --- a/src/entity/RDFDocument.cpp +++ b/src/entity/RDFDocument.cpp @@ -56,8 +56,7 @@ RDFDocument::Ptr RDFDocument::FromFile(const std::string &file) doc->addTriple( Triple( (*statements).subject().toN3().toStdString(), (*statements).predicate().toN3().toStdString(), - (*statements).object().toN3().toStdString(), - doc->id() ) + (*statements).object().toN3().toStdString() ) ); } diff --git a/src/entity/RDFEntity.cpp b/src/entity/RDFEntity.cpp index e24eb18..acdb84c 100644 --- a/src/entity/RDFEntity.cpp +++ b/src/entity/RDFEntity.cpp @@ -4,8 +4,8 @@ namespace sempr { namespace entity { -TripleIteratorWrapper::TripleIteratorWrapper(TripleIterator* impl) - : impl_(impl) +TripleIteratorWrapper::TripleIteratorWrapper(TripleIterator* impl) : + impl_(impl) { if (!impl_) throw std::exception(); } @@ -50,15 +50,15 @@ TripleIterator::~TripleIterator() SEMPR_ENTITY_SOURCE(RDFEntity); -RDFEntity::RDFEntity(const core::IDGenBase* idgen) - : Entity(idgen) +RDFEntity::RDFEntity(const core::IDGenBase* idgen, bool temporary) : + Entity(idgen, temporary) { } // private, only to be used by odb! -RDFEntity::RDFEntity() - : Entity() +RDFEntity::RDFEntity() : + Entity() { } diff --git a/src/entity/RDFPropertyMap.cpp b/src/entity/RDFPropertyMap.cpp index 693e4c9..b87e3df 100644 --- a/src/entity/RDFPropertyMap.cpp +++ b/src/entity/RDFPropertyMap.cpp @@ -17,7 +17,6 @@ const Triple RDFPropertyMapIterator::operator*() const t.subject = pmap_->subject_; t.predicate = "<" + it_->first + ">"; t.object = it_->second.toString(); - t.document = "<" + sempr::baseURI() + pmap_->id() + ">"; return t; } diff --git a/src/entity/RDFVector.cpp b/src/entity/RDFVector.cpp index 8c1d866..098159d 100644 --- a/src/entity/RDFVector.cpp +++ b/src/entity/RDFVector.cpp @@ -34,14 +34,19 @@ bool RDFVectorIterator::operator==(const TripleIterator& other) const SEMPR_ENTITY_SOURCE(RDFVector) -RDFVector::RDFVector(const core::IDGenBase* idgen) - : RDFEntity(idgen) +RDFVector::RDFVector(const core::IDGenBase* idgen, bool temporary) : + RDFEntity(idgen, temporary) { setDiscriminator(); } +RDFVector::RDFVector(bool temporary) : + RDFVector(new core::IDGen(), temporary) +{ +} -RDFVector::RDFVector() : RDFVector(new core::IDGen()) +RDFVector::RDFVector() : + RDFVector(new core::IDGen()) { } @@ -56,23 +61,36 @@ const Triple& RDFVector::getTripleAt(const size_t index) return triples_[index]; } -bool RDFVector::addTriple(const sempr::entity::Triple &triple) +bool RDFVector::addTriple(const Triple& triple, bool replace) +{ + bool isValid = validity(triple); + + if (isValid) + { + if (replace) + removeTriple(triple); + + triples_.push_back(triple); + //this->changed(); + } + + return isValid; +} + +bool RDFVector::validity(const Triple& triple) const { + Triple t = triple; // check if the triple is valid! - auto sub = Soprano::Node::fromN3(QString::fromStdString(triple.subject)); - auto pred = Soprano::Node::fromN3(QString::fromStdString(triple.predicate)); - auto obj = Soprano::Node::fromN3(QString::fromStdString(triple.object)); + auto sub = Soprano::Node::fromN3(QString::fromStdString(t.subject)); + auto pred = Soprano::Node::fromN3(QString::fromStdString(t.predicate)); + auto obj = Soprano::Node::fromN3(QString::fromStdString(t.object)); Soprano::Statement st(sub, pred, obj); - // but add anyway, necessary for RDFPropertyMap etc - triples_.push_back(triple); - //this->changed(); - return st.isValid(); } -bool RDFVector::removeTriple(const sempr::entity::Triple &triple) +bool RDFVector::removeTriple(const Triple& triple) { auto newEnd = std::remove(triples_.begin(), triples_.end(), triple); if (newEnd == triples_.end()) return false; diff --git a/src/entity/SemanticEntity.cpp b/src/entity/SemanticEntity.cpp index 401da52..94833e6 100644 --- a/src/entity/SemanticEntity.cpp +++ b/src/entity/SemanticEntity.cpp @@ -29,6 +29,13 @@ std::string RegisteredPropertyBase::predicate() const return predicate_; } +bool RegisteredPropertyBase::operator==(const RegisteredPropertyBase& other) const +{ + return hasSubject_ == other.hasSubject_ && + subject_ == other.subject_ && + predicate_ == other.predicate_ && + object() == other.object() ; +} /// SemanticEntity iterator SemanticEntityIterator::SemanticEntityIterator(SemanticEntityIterator::ConstIterator it, SemanticEntityIterator::ConstIterator end, const SemanticEntity* entity) @@ -79,17 +86,24 @@ void SemanticEntityIterator::makeValid() /// SemanticEntity SEMPR_ENTITY_SOURCE(SemanticEntity); -SemanticEntity::SemanticEntity(const core::IDGenBase* idgen) - : RDFEntity(idgen) +SemanticEntity::SemanticEntity() : + SemanticEntity(new core::IDGen()) { - this->setDiscriminator(); } -SemanticEntity::SemanticEntity() - : SemanticEntity(new core::IDGen()) +SemanticEntity::SemanticEntity(bool temporary) : + SemanticEntity(new core::IDGen(), temporary) { } +SemanticEntity::SemanticEntity(const core::IDGenBase* idgen, bool temporary) : + RDFEntity(idgen, temporary) +{ + this->setDiscriminator(); +} + + + SemanticEntity::~SemanticEntity() { for (auto rprop : properties_) @@ -98,6 +112,15 @@ SemanticEntity::~SemanticEntity() } } +std::vector::iterator SemanticEntity::findProperty(const RegisteredPropertyBase& prop) +{ + for (auto it = properties_.begin(); it != properties_.end(); ++it) + { + if ( (*it) && *(*it) == prop) + return it; + } + return properties_.end(); +} TripleIteratorWrapper SemanticEntity::begin() const { diff --git a/src/entity/spatial/filter/GeometryFilter.cpp b/src/entity/spatial/filter/GeometryFilter.cpp index 7690497..a0494d1 100644 --- a/src/entity/spatial/filter/GeometryFilter.cpp +++ b/src/entity/spatial/filter/GeometryFilter.cpp @@ -52,7 +52,7 @@ void EnvelopeFilter::filter_ro(const geom::CoordinateSequence& seq, std::size_t if (coord.z > max_.z) max_.z = coord.z; - if (min_.z == std::numeric_limits::max() || max_.z == std::numeric_limits::min()) + if (min_.z == + std::numeric_limits::max() || max_.z == - std::numeric_limits::max()) { //only 2d: min_.z = std::numeric_limits::quiet_NaN(); diff --git a/src/entity/spatial/reference/EarthCenteredCS.cpp b/src/entity/spatial/reference/EarthCenteredCS.cpp index 2eecc7e..265d5bf 100644 --- a/src/entity/spatial/reference/EarthCenteredCS.cpp +++ b/src/entity/spatial/reference/EarthCenteredCS.cpp @@ -29,5 +29,20 @@ FilterPtr EarthCenteredCS::reverse() const return FilterPtr(new ECEFReverseFilter(GeographicLib::Constants::WGS84_a(), GeographicLib::Constants::WGS84_f())); } +std::size_t EarthCenteredCS::directionDimension(const CardinalDirection& direction) const +{ + switch (direction) + { + case NORTH: + case SOUTH: + return 2; //z + case EAST: + case WEST: + return 1; //y + } + + return 0; +} + }} diff --git a/src/entity/spatial/reference/GeodeticCS.cpp b/src/entity/spatial/reference/GeodeticCS.cpp index fca14b2..6e98548 100644 --- a/src/entity/spatial/reference/GeodeticCS.cpp +++ b/src/entity/spatial/reference/GeodeticCS.cpp @@ -52,7 +52,20 @@ FilterPtr GeodeticCS::reverse() const return FilterPtr(nullptr); } +std::size_t GeodeticCS::directionDimension(const CardinalDirection& direction) const +{ + switch (direction) + { + case NORTH: + case SOUTH: + return 0; //x + case EAST: + case WEST: + return 1; //y + } + return 0; +} }} diff --git a/src/entity/spatial/reference/GlobalCS.cpp b/src/entity/spatial/reference/GlobalCS.cpp index 049fd7a..aa1a4e8 100644 --- a/src/entity/spatial/reference/GlobalCS.cpp +++ b/src/entity/spatial/reference/GlobalCS.cpp @@ -22,15 +22,6 @@ GlobalCS::~GlobalCS() { } -bool GlobalCS::isEqual(const GlobalCS::Ptr other) const -{ - // avoid expr. side effects warning: - auto& tmpOther = *other; - auto& tmpThis = *this; - - return typeid(tmpOther) == typeid(tmpThis); -} - SpatialReference::Ptr GlobalCS::getRoot() { // GlobalCS is always a root @@ -68,6 +59,11 @@ FilterPtr GlobalCS::reverse() const return FilterPtr(nullptr); } +std::size_t GlobalCS::directionDimension(const CardinalDirection& direction) const +{ + assert(1); + return 0; +} }} diff --git a/src/entity/spatial/reference/LocalCS.cpp b/src/entity/spatial/reference/LocalCS.cpp index 239f9c8..0424e96 100644 --- a/src/entity/spatial/reference/LocalCS.cpp +++ b/src/entity/spatial/reference/LocalCS.cpp @@ -17,6 +17,19 @@ LocalCS::LocalCS(const core::IDGenBase* idgen) this->setDiscriminator(); } +bool LocalCS::isEqual(const SpatialReference::Ptr other) const +{ + auto otherLocal = std::dynamic_pointer_cast(other); + + if(otherLocal) + { // check if origin is equal: if there is a parent than the parent also have to be equal. + return ( !parent_ || parent_->isEqual(otherLocal->parent_) ) && transform_.isApprox(otherLocal->transform_); + } + else + { + return false; + } +} SpatialReference::Ptr LocalCS::getRoot() { diff --git a/src/entity/spatial/reference/LocalTangentPlaneCS.cpp b/src/entity/spatial/reference/LocalTangentPlaneCS.cpp index c532ecf..3447b3f 100644 --- a/src/entity/spatial/reference/LocalTangentPlaneCS.cpp +++ b/src/entity/spatial/reference/LocalTangentPlaneCS.cpp @@ -40,7 +40,7 @@ LocalTangentPlaneCS::LocalTangentPlaneCS(double lat0, double lon0, double h0, co } -bool LocalTangentPlaneCS::isEqual(const GlobalCS::Ptr other) const +bool LocalTangentPlaneCS::isEqual(const SpatialReference::Ptr other) const { auto otherLTG = std::dynamic_pointer_cast(other); @@ -54,6 +54,22 @@ bool LocalTangentPlaneCS::isEqual(const GlobalCS::Ptr other) const } } +std::size_t LocalTangentPlaneCS::directionDimension(const CardinalDirection& direction) const +{ + // ENU / LTG (East-North-Up) + switch (direction) + { + case NORTH: + case SOUTH: + return 1; //y + case EAST: + case WEST: + return 0; //x + } + + return 0; +} + FilterPtr LocalTangentPlaneCS::forward() const { return FilterPtr(new LTGForwardFilter(lat0_, lon0_, h0_, GeographicLib::Constants::WGS84_a(), GeographicLib::Constants::WGS84_f())); diff --git a/src/entity/spatial/reference/MilitaryGridReferenceSystem.cpp b/src/entity/spatial/reference/MilitaryGridReferenceSystem.cpp index 95fed69..4db975b 100644 --- a/src/entity/spatial/reference/MilitaryGridReferenceSystem.cpp +++ b/src/entity/spatial/reference/MilitaryGridReferenceSystem.cpp @@ -57,7 +57,7 @@ std::string MilitaryGridReferenceSystem::getGZDSquareID() const return zoneStr + designator_ + squareID_; } -bool MilitaryGridReferenceSystem::isEqual(const GlobalCS::Ptr other) const +bool MilitaryGridReferenceSystem::isEqual(const SpatialReference::Ptr other) const { auto otherMGRS = std::dynamic_pointer_cast(other); diff --git a/src/entity/spatial/reference/ProjectionCS.cpp b/src/entity/spatial/reference/ProjectionCS.cpp index fdd8615..5ad0bf6 100644 --- a/src/entity/spatial/reference/ProjectionCS.cpp +++ b/src/entity/spatial/reference/ProjectionCS.cpp @@ -74,6 +74,21 @@ bool ProjectionCS::isNorth() const return true; } +std::size_t ProjectionCS::directionDimension(const CardinalDirection& direction) const +{ + switch (direction) + { + case NORTH: + case SOUTH: + return 1; //y + case EAST: + case WEST: + return 0; //x + } + + return 0; +} + ProjectionCS::Ptr ProjectionCS::importZone(const std::string& zonestr) { diff --git a/src/entity/spatial/reference/SpatialReference.cpp b/src/entity/spatial/reference/SpatialReference.cpp index 81dc190..0403997 100644 --- a/src/entity/spatial/reference/SpatialReference.cpp +++ b/src/entity/spatial/reference/SpatialReference.cpp @@ -20,6 +20,17 @@ SpatialReference::Ptr SpatialReference::getRoot() return SpatialReference::Ptr(NULL); } +bool SpatialReference::isEqual(const SpatialReference::Ptr other) const + { + // avoid expr. side effects warning: + auto& tmpOther = *other; + auto& tmpThis = *this; + + // type check for all childs without any attributes valid. Have to be override if child contain attributes. + return typeid(tmpOther) == typeid(tmpThis); + } + + Eigen::Affine3d SpatialReference::transformationToRoot() const { return Eigen::Affine3d::Identity(); diff --git a/src/entity/spatial/reference/UniversalPolarStereographicCS.cpp b/src/entity/spatial/reference/UniversalPolarStereographicCS.cpp index 6b028bc..c1f8bcf 100644 --- a/src/entity/spatial/reference/UniversalPolarStereographicCS.cpp +++ b/src/entity/spatial/reference/UniversalPolarStereographicCS.cpp @@ -36,7 +36,7 @@ bool UniversalPolarStereographicCS::isNorth() const return north_; } -bool UniversalPolarStereographicCS::isEqual(const GlobalCS::Ptr other) const +bool UniversalPolarStereographicCS::isEqual(const SpatialReference::Ptr other) const { auto otherUPS = std::dynamic_pointer_cast(other); diff --git a/src/entity/spatial/reference/UniversalTransverseMercatorCS.cpp b/src/entity/spatial/reference/UniversalTransverseMercatorCS.cpp index 2d1aeed..65bf7f8 100644 --- a/src/entity/spatial/reference/UniversalTransverseMercatorCS.cpp +++ b/src/entity/spatial/reference/UniversalTransverseMercatorCS.cpp @@ -43,7 +43,7 @@ bool UniversalTransverseMercatorCS::isNorth() const return north_; } -bool UniversalTransverseMercatorCS::isEqual(const GlobalCS::Ptr other) const +bool UniversalTransverseMercatorCS::isEqual(const SpatialReference::Ptr other) const { auto otherUTM = std::dynamic_pointer_cast(other); diff --git a/src/processing/CalculatePointsModule.cpp b/src/processing/CalculatePointsModule.cpp index 713a374..8bebcec 100644 --- a/src/processing/CalculatePointsModule.cpp +++ b/src/processing/CalculatePointsModule.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace sempr { namespace processing { @@ -32,23 +33,34 @@ void CalculatePointsModule::process(query::CalculatePointsQuery::Ptr query) cloud->loaded(); } - //auto spatial = query::SpatialIndexQuery<3>::intersectsBoxOf(query->entity()); - auto spatial = query::SpatialIndexQuery::intersectsBoxOf(query->entity()); - //auto coords = *((std::get<1>(spatial->refBoxGeometryPair()))->getGeometry()->getCoordinates()->toVector()); - auto coords = *((spatial->refGeo())->getGeometry()->getCoordinates()->toVector()); + /* This works only with the one Westerberg pointcloud currently, so why to ask for a cloud you only know? + * In any case is this no final code, so short it up + */ + /* + auto spatial = query::SpatialIndexQuery3D::intersectsBoxOf(query->entity()); - // TODO: Use this coords for the calculatePoints() function + //set y value from -DBL_MAX to +DBL_MAX, this assume that the Y Axis is the height axis! + boost::geometry::set<1>(spatial->refBoxGeometryPair().first.min_corner(), -DBL_MAX); + boost::geometry::set<1>(spatial->refBoxGeometryPair().first.max_corner(), DBL_MAX); + + */ + + //No MultiPoint Box geometry anymore since spatial index update + /* + auto coords = *(spatial->refBoxGeometryPair().second->getGeometry()->getCoordinates()->toVector()); + // TODO: Use this coords for the calculatePoints() function -- Note this will only work if the y axis is the height! coords[0].y = coords[1].y = coords[4].y = coords[5].y = -DBL_MAX; coords[2].y = coords[3].y = coords[6].y = coords[7].y = DBL_MAX; - //std::static_pointer_cast(std::get<1>(spatial->refBoxGeometryPair()))->setCoordinates(coords); - std::static_pointer_cast(spatial->refGeo())->setCoordinates(coords); - ask(spatial); + std::static_pointer_cast(spatial->refBoxGeometryPair().second)->setCoordinates(coords); + */ + + //ask(spatial); entity::PointCloud::Ptr entity = entity::PointCloud::Ptr(new entity::PointCloud()); - for(auto& c : spatial->results) + for(auto& c : clouds->results) { - if(c->discriminator() == "sempr::entity::PointCloud") + if(c->discriminator() == "sempr::entity::PointCloud") // could this be done by the dynmaic cast? { entity::PointCloud::Ptr cloud = std::static_pointer_cast(c); calculatePoints(cloud, query, p, r, g, b, colors); @@ -59,23 +71,22 @@ void CalculatePointsModule::process(query::CalculatePointsQuery::Ptr query) if(colors == true) { - entity->setChannel(11, entity::Channel(r)); - entity->setChannel(12, entity::Channel(g)); - entity->setChannel(13, entity::Channel(b)); + entity->setChannel(entity::COLOR_R, entity::Channel(r)); + entity->setChannel(entity::COLOR_G, entity::Channel(g)); + entity->setChannel(entity::COLOR_B, entity::Channel(b)); } query->results = entity; } void CalculatePointsModule::calculatePoints(const entity::PointCloud::Ptr cloud, query::CalculatePointsQuery::Ptr query, std::vector& p, std::vector& r, std::vector& g, std::vector& b, bool& colors) { - unsigned int i, j; double low = query->minHeight(); double high = query->maxHeight(); const std::vector * coords_ptr = query->entity()->getGeometry()->getCoordinates()->toVector(); const std::vector & coords = *coords_ptr; - if(cloud->hasChannel(11) && cloud->hasChannel(12) && cloud->hasChannel(13)) + if(cloud->hasChannel(entity::COLOR_R) && cloud->hasChannel(entity::COLOR_G) && cloud->hasChannel(entity::COLOR_B)) { colors = true; } @@ -90,7 +101,7 @@ void CalculatePointsModule::calculatePoints(const entity::PointCloud::Ptr cloud, double maxX = minX; double maxZ = minZ; - for(i = 1; i < coords.size() - 1; i++) + for(size_t i = 1; i < coords.size() - 1; i++) { if(coords[i].x < minX) minX = coords[i].x; @@ -109,7 +120,7 @@ void CalculatePointsModule::calculatePoints(const entity::PointCloud::Ptr cloud, const std::vector * cloud_coords_ptr = cloud->getGeometry()->getCoordinates()->toVector(); const std::vector & cloud_coords = *cloud_coords_ptr; - for (j = 0; j < cloud_coords.size(); j++) + for (size_t j = 0; j < cloud_coords.size(); j++) { if (cloud_coords[j].x < minX || cloud_coords[j].z < minZ || cloud_coords[j].x > maxX || cloud_coords[j].z > maxZ) { @@ -117,7 +128,7 @@ void CalculatePointsModule::calculatePoints(const entity::PointCloud::Ptr cloud, } else { - for(i = 1; i < coords.size(); i++) + for(size_t i = 1; i < coords.size(); i++) { intersections += checkIntersection(coords[i - 1], coords[i], outerPoint, cloud_coords[j]); } @@ -129,9 +140,9 @@ void CalculatePointsModule::calculatePoints(const entity::PointCloud::Ptr cloud, p.push_back(cloud_coords[j]); if(colors == true) { - r.emplace_back(cloud->getChannelUInt8(11)[j]); - g.emplace_back(cloud->getChannelUInt8(12)[j]); - b.emplace_back(cloud->getChannelUInt8(13)[j]); + r.emplace_back(cloud->getChannelUInt8(entity::COLOR_R)[j]); + g.emplace_back(cloud->getChannelUInt8(entity::COLOR_G)[j]); + b.emplace_back(cloud->getChannelUInt8(entity::COLOR_B)[j]); } } } diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp deleted file mode 100644 index d862f00..0000000 --- a/src/processing/SpatialIndex.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include -#include -#include -#include - -#include - -//#include - -namespace sempr { namespace processing { - -SpatialIndex::SpatialIndex() -{ - // nothing to do -} - - -std::string SpatialIndex::type() const -{ - return "SpatialIndex"; -} - -void SpatialIndex::process(query::SpatialIndexQuery::Ptr query) -{ - // TODO: transfer reference geometry into the frame of the spatial index - // (make a copy?) - // query->refGeo_ - - // create the AABB of the transformed query-volume. - - geos::geom::Coordinate min, max; - query->refGeo()->findEnvelope(min, max); - - bBox region( - bPoint(min.x, min.y, min.z), - bPoint(max.x, max.y, max.z) - ); - - - std::vector tmpResults; - - typedef query::SpatialIndexQuery::QueryType QueryType; - switch (query->mode()) { - - case QueryType::WITHIN: - rtree_.query(bgi::within(region), std::back_inserter(tmpResults)); - break; - case QueryType::NOTWITHIN: - rtree_.query(!bgi::within(region), std::back_inserter(tmpResults)); - break; - - // TODO: contains is introduced in boost 1.55, but ros indigo needs 1.54. - // maybe its time for me to upgrade to 16.04 and kinetic... - case QueryType::CONTAINS: - rtree_.query(bgi::contains(region), std::back_inserter(tmpResults)); - break; - case QueryType::NOTCONTAINS: - rtree_.query(!bgi::contains(region), std::back_inserter(tmpResults)); - break; - - case QueryType::INTERSECTS: - rtree_.query(bgi::intersects(region), std::back_inserter(tmpResults)); - break; - case QueryType::NOTINTERSECTS: - rtree_.query(!bgi::intersects(region), std::back_inserter(tmpResults)); - break; - - default: - std::cout << "SpatialIndex: Mode " << query->mode() << " not implemented." << '\n'; - } - - std::transform( tmpResults.begin(), tmpResults.end(), - std::inserter(query->results, query->results.end()), - [](bValue tmp) { return tmp.second; } - ); -} - - -void SpatialIndex::process(core::EntityEvent::Ptr geoEvent) -{ - typedef core::EntityEvent::EventType EType; - entity::Geometry::Ptr geo = geoEvent->getEntity(); - - switch (geoEvent->what()) { - case EType::CREATED: - case EType::LOADED: - insertGeo(geo); - break; - case EType::CHANGED: - updateGeo(geo); - break; - case EType::REMOVED: - removeGeo(geo); - break; - } -} - -void SpatialIndex::process(core::EntityEvent::Ptr refEvent) -{ - typedef core::EntityEvent::EventType EType; - switch (refEvent->what()) { - case EType::CREATED: - case EType::LOADED: - // nothing to do. - break; - case EType::CHANGED: - // check which geometries are affected and update them. - processChangedCS(refEvent->getEntity()); - break; - case EType::REMOVED: - // well... - // what happens if we remove a SpatialReference that some geometry is pointing to? - // (why would be do that? how can we prevent that?) - break; - } -} - - -void SpatialIndex::processChangedCS(entity::SpatialReference::Ptr cs) -{ - // we need to check every geometry currently in the index, if it is affected. - for (auto entry : geo2box_) - { - if (entry.first->getCS()->isChildOf(cs)) - { - // well, in that case update it. - updateGeo(entry.first); - } - } -} - - -SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) -{ - - // get the 3d envelope of the geometry. - geos::geom::Coordinate min, max; - geo->findEnvelope(min, max); - - // this envelope is in the coordinate system of the geometry. But what we need is an envelope - // that is axis aligned with the root reference system. We could transform the geometry to root, - // take the envelope and transform it back, but that is just ridiculus. Instead: Create a - // bounding-box-geometry (8 points, one for each corner), transform it, and take its envelope. - // --- - // create a geometry with the matching extends: 8 point, every corner must be checked! - - geos::geom::Coordinate coord; - std::vector cornerCoordinates; - coord = geos::geom::Coordinate(min.x, min.y, min.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(min.x, min.y, max.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(min.x, max.y, min.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(min.x, max.y, max.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(max.x, min.y, min.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(max.x, min.y, max.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(max.x, max.y, min.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(max.x, max.y, max.z); cornerCoordinates.push_back(coord); - - geos::geom::MultiPoint* mp = geos::geom::GeometryFactory::getDefaultInstance()->createMultiPoint(cornerCoordinates); - - // transform the geometry - LocalTransformationFilter tf(geo->getCS()->transformationToRoot()); - mp->apply_rw(tf); - - // get the new envelope - EnvelopeFilter ef; - mp->apply_ro(ef); - - // destroy the multi point after there usage: - geos::geom::GeometryFactory::getDefaultInstance()->destroyGeometry(mp); - - // create the bBox out of bPoints. - bBox box( - bPoint(ef.getMin().x, ef.getMin().y, ef.getMin().z), - bPoint(ef.getMax().x, ef.getMax().y, ef.getMax().z) - ); - - return bValue(box, geo); -} - - -void SpatialIndex::insertGeo(entity::Geometry::Ptr geo) -{ - // sanity: it needs a geometry, and a spatial reference. - if (!geo->getGeometry() || !geo->getCS()) return; - - // create a new entry - bValue entry = createEntry(geo); - // save it in our map - geo2box_[geo] = entry; - // and insert it into the RTree - rtree_.insert(entry); - - - // std::cout << "inserted " << geo->id() << " into spatial index. AABB: " - // << "(" << entry.first.min_corner().get<0>() << ", " << - // entry.first.min_corner().get<1>() << ", " << - // entry.first.min_corner().get<2>() << ") -- (" << - // entry.first.max_corner().get<0>() << ", " << - // entry.first.max_corner().get<1>() << ", " << - // entry.first.max_corner().get<2>() << ")" << '\n'; -} - -void SpatialIndex::updateGeo(entity::Geometry::Ptr geo) -{ - // update? lets remove the old one first. - removeGeo(geo); - - // sanity: it needs a geometry, and a spatial reference. - if (!geo->getGeometry() || !geo->getCS()) return; - - // and re-insert it with updated data. - insertGeo(geo); -} - -void SpatialIndex::removeGeo(entity::Geometry::Ptr geo) -{ - // find the map-entry - auto it = geo2box_.find(geo); - if (it != geo2box_.end()) - { - // remove from rtree - rtree_.remove(it->second); - // and from the map - geo2box_.erase(it); - } -} - -}} diff --git a/src/query/SpatialIndexQuery.cpp b/src/query/SpatialIndexQuery.cpp deleted file mode 100644 index b3bac03..0000000 --- a/src/query/SpatialIndexQuery.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include -#include - -#include - -namespace sempr { namespace query { - -SpatialIndexQuery::SpatialIndexQuery() -{ -} - -SpatialIndexQuery::~SpatialIndexQuery() -{ -} - -SpatialIndexQuery::QueryType SpatialIndexQuery::mode() const -{ - return qtype_; -} - -void SpatialIndexQuery::mode(SpatialIndexQuery::QueryType m) -{ - qtype_ = m; -} - -entity::Geometry::Ptr SpatialIndexQuery::refGeo() -{ - return refGeo_; -} - - -void SpatialIndexQuery::setupRefGeo(const Eigen::Vector3d &lower, const Eigen::Vector3d &upper, - entity::SpatialReference::Ptr cs) -{ - entity::MultiPoint::Ptr corners(new entity::MultiPoint()); - - corners->setCS(cs); - - geos::geom::Coordinate coord; - std::vector cornerCoordinates; - coord = geos::geom::Coordinate(lower.x(), lower.y(), lower.z()); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(lower.x(), lower.y(), upper.z()); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(lower.x(), upper.y(), lower.z()); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(lower.x(), upper.y(), upper.z()); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(upper.x(), lower.y(), lower.z()); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(upper.x(), lower.y(), upper.z()); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(upper.x(), upper.y(), lower.z()); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(upper.x(), upper.y(), upper.z()); cornerCoordinates.push_back(coord); - - corners->setCoordinates(cornerCoordinates); - - this->refGeo_ = corners; -} - -SpatialIndexQuery::Ptr SpatialIndexQuery::createQuery( - entity::Geometry::Ptr geometry, sempr::query::SpatialIndexQuery::QueryType type) -{ - geos::geom::Coordinate min, max; - geometry->findEnvelope(min, max); - - Eigen::Vector3d lower, upper; - - lower.x() = min.x; - lower.y() = min.y; - lower.z() = min.z; - upper.x() = max.x; - upper.y() = max.y; - upper.z() = max.z; - - return createQuery(lower, upper, geometry->getCS(), type); -} - -SpatialIndexQuery::Ptr SpatialIndexQuery::createQuery( - const Eigen::Vector3d &lower, const Eigen::Vector3d &upper, - entity::SpatialReference::Ptr cs, sempr::query::SpatialIndexQuery::QueryType type) -{ - if (!cs) return SpatialIndexQuery::Ptr(); - - SpatialIndexQuery::Ptr query(new SpatialIndexQuery()); - query->setupRefGeo(lower, upper, cs); - query->mode(type); - return query; -} - - -void SpatialIndexQuery::invert() -{ - /* - inversion of query type: - positive (WITHIN, CONTAINS, INTERSECTS) are even (0, 2, 4), - negative (NOT_WITHIN, ...) are odd (1, 3, 5). - Just increment even and decrement odd values. - */ - if (qtype_ % 2 == 0) qtype_ = QueryType(qtype_+1); - else qtype_ = QueryType(qtype_-1); -} - - -SpatialIndexQuery::Ptr SpatialIndexQuery::withinBox( const Eigen::Vector3d& lower, - const Eigen::Vector3d& upper, - entity::SpatialReference::Ptr cs) -{ - return SpatialIndexQuery::createQuery(lower, upper, cs, SpatialIndexQuery::WITHIN); -} - -SpatialIndexQuery::Ptr SpatialIndexQuery::containsBox( const Eigen::Vector3d& lower, - const Eigen::Vector3d& upper, - entity::SpatialReference::Ptr cs) -{ - return SpatialIndexQuery::createQuery(lower, upper, cs, SpatialIndexQuery::CONTAINS); -} - -SpatialIndexQuery::Ptr SpatialIndexQuery::intersectsBox( const Eigen::Vector3d& lower, - const Eigen::Vector3d& upper, - entity::SpatialReference::Ptr cs) -{ - return SpatialIndexQuery::createQuery(lower, upper, cs, SpatialIndexQuery::INTERSECTS); -} - - -SpatialIndexQuery::Ptr SpatialIndexQuery::withinBoxOf(entity::Geometry::Ptr geometry) -{ - return SpatialIndexQuery::createQuery(geometry, SpatialIndexQuery::WITHIN); -} - -SpatialIndexQuery::Ptr SpatialIndexQuery::containsBoxOf(entity::Geometry::Ptr geometry) -{ - return SpatialIndexQuery::createQuery(geometry, SpatialIndexQuery::CONTAINS); -} - -SpatialIndexQuery::Ptr SpatialIndexQuery::intersectsBoxOf(entity::Geometry::Ptr geometry) -{ - return SpatialIndexQuery::createQuery(geometry, SpatialIndexQuery::INTERSECTS); -} - -}} diff --git a/src/storage/DBObject.cpp b/src/storage/DBObject.cpp index f627893..496d629 100644 --- a/src/storage/DBObject.cpp +++ b/src/storage/DBObject.cpp @@ -9,7 +9,7 @@ DBObject::DBObject() } DBObject::DBObject(const core::IDGenBase* idgen, bool temporary) - : persisted_(false), idgenerator_(idgen), temporary_(temporary) + : persisted_(false), temporary_(temporary), idgenerator_(idgen) { id_ = idgen->generate(); setDiscriminator(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4fcf373..70f2a49 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -42,6 +42,10 @@ if(Boost_FOUND) target_link_libraries(sempr_spatial_index_tests ${DEPENDENCIES}) add_test(NAME sempr_spatial_index_tests COMMAND sempr_spatial_index_tests) + add_executable(sempr_spatial_conclusion_tests SEMPR_tests.cpp spatial_conclusion_tests.cpp) + target_link_libraries(sempr_spatial_conclusion_tests ${DEPENDENCIES}) + add_test(NAME sempr_spatial_conclusion_tests COMMAND sempr_spatial_conclusion_tests) + add_executable(sempr_entity_tests SEMPR_tests.cpp Entity_tests.cpp) target_link_libraries(sempr_entity_tests ${DEPENDENCIES}) add_test(NAME sempr_entity_tests COMMAND sempr_entity_tests) diff --git a/test/SemanticEntity_tests.cpp b/test/SemanticEntity_tests.cpp index fef4a0c..6992eaf 100644 --- a/test/SemanticEntity_tests.cpp +++ b/test/SemanticEntity_tests.cpp @@ -57,7 +57,7 @@ BOOST_AUTO_TEST_SUITE(entity_SemanticEntity) // debug: print triples: for (auto t : *entity) { - std::cout << t.subject << " " << t.predicate << " " << t.object << " " << t.document << std::endl; + std::cout << t.subject << " " << t.predicate << " " << t.object << std::endl; } } diff --git a/test/main.cpp b/test/main.cpp index 5de1bd0..1554dec 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -85,7 +85,7 @@ int main(int argc, char** args) DebugModule::Ptr debug(new DebugModule()); ActiveObjectStore::Ptr active( new ActiveObjectStore() ); SopranoModule::Ptr semantic( new SopranoModule() ); - SpatialIndex::Ptr spatial( new SpatialIndex() ); + SpatialIndex3D::Ptr spatial( new SpatialIndex3D(std::make_shared()) ); sempr::core::IDGenerator::getInstance().setStrategy( std::unique_ptr( new sempr::core::IncrementalIDGeneration(storage) ) @@ -120,7 +120,7 @@ int main(int argc, char** args) // query for everything in a box. Eigen::Vector3d lower{-0.1, -0.1, -0.1}; Eigen::Vector3d upper{5.2, 1.2, 1.2}; - auto q = SpatialIndexQuery::withinBox(lower, upper, cs); + auto q = SpatialIndexQuery3D::withinBox(lower, upper, cs); c.answerQuery(q); for (auto r : q->results) { std::cout << "SpatialIndexQuery result: " << r->id() << '\n'; diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp new file mode 100644 index 0000000..e8a7257 --- /dev/null +++ b/test/spatial_conclusion_tests.cpp @@ -0,0 +1,242 @@ +#include "test_utils.hpp" +using namespace testing; + + +geom::MultiPoint* setupQuadrangle(const std::array& min, const std::array& max) +{ + std::vector corners; + + corners.push_back(geom::Coordinate(min[0], min[1], min[2])); + corners.push_back(geom::Coordinate(min[0], min[1], max[2])); + corners.push_back(geom::Coordinate(min[0], max[1], min[2])); + corners.push_back(geom::Coordinate(min[0], max[1], max[2])); + corners.push_back(geom::Coordinate(max[0], min[1], min[2])); + corners.push_back(geom::Coordinate(max[0], min[1], max[2])); + corners.push_back(geom::Coordinate(max[0], max[1], min[2])); + corners.push_back(geom::Coordinate(max[0], max[1], max[2])); + + auto mp = geom::GeometryFactory::getDefaultInstance()->createMultiPoint(corners); + + return mp; +} + +std::vector getVechtaCoords() +{ + std::vector vechtaCorner; + + vechtaCorner.emplace_back(52.755742, 8.274784); + vechtaCorner.emplace_back(52.758035, 8.307160); + vechtaCorner.emplace_back(52.745131, 8.328818); + vechtaCorner.emplace_back(52.701042, 8.299618); + vechtaCorner.emplace_back(52.719873, 8.257034); + vechtaCorner.emplace_back(52.755742, 8.274784); //close the ring + + return vechtaCorner; +} + +std::vector getOsnaCoords() +{ + std::vector osnaCorner; + + osnaCorner.emplace_back(52.245902, 8.087810); + osnaCorner.emplace_back(52.302939, 8.034527); + osnaCorner.emplace_back(52.297650, 8.107368); + osnaCorner.emplace_back(52.245900, 8.087793); + osnaCorner.emplace_back(52.245902, 8.087810); //close the ring + + return osnaCorner; +} + +std::vector getBremenCoords() +{ + std::vector bremenCorner; + bremenCorner.emplace_back(53.096611, 8.688279); + bremenCorner.emplace_back(53.146918, 8.733290); + bremenCorner.emplace_back(53.110185, 8.867102); + bremenCorner.emplace_back(53.041551, 8.983798); + bremenCorner.emplace_back(53.029056, 8.858612); + bremenCorner.emplace_back(53.096611, 8.688279); //close the ring + + return bremenCorner; +} + +std::vector getNDSCoords() +{ + // counter clock wise + std::vector ndsCorner; + ndsCorner.emplace_back(52.241226, 7.0658); + ndsCorner.emplace_back(52.379696, 7.572223); + ndsCorner.emplace_back(52.474945, 7.603808); + ndsCorner.emplace_back(52.372092, 7.804957); + ndsCorner.emplace_back(52.365060, 7.9367); + ndsCorner.emplace_back(52.268337, 7.926824); + ndsCorner.emplace_back(52.365060, 7.93667); + ndsCorner.emplace_back(52.083415, 7.887888); + ndsCorner.emplace_back(52.075998, 8.196700); + ndsCorner.emplace_back(52.198737, 8.460202); + ndsCorner.emplace_back(52.454954, 8.332350); + ndsCorner.emplace_back(52.500513, 8.558474); + ndsCorner.emplace_back(52.531468, 8.652268); //NRW Northpoint + ndsCorner.emplace_back(52.400771, 8.724932); + ndsCorner.emplace_back(52.401876, 8.93696); + ndsCorner.emplace_back(52.497824, 9.095536); + ndsCorner.emplace_back(52.265462, 8.971608); + ndsCorner.emplace_back(51.854584, 9.328392); + ndsCorner.emplace_back(51.862115, 9.459723); + ndsCorner.emplace_back(51.650059, 9.374471); + ndsCorner.emplace_back(51.612036, 9.540668); + ndsCorner.emplace_back(51.340252, 9.568025); + ndsCorner.emplace_back(51.587427, 10.373788); + ndsCorner.emplace_back(51.566889, 10.658262); + ndsCorner.emplace_back(52.009182, 10.564431); + ndsCorner.emplace_back(52.056388, 10.964671); + ndsCorner.emplace_back(52.218803, 11.059229); //A2 Border + ndsCorner.emplace_back(52.842004, 10.764717); + ndsCorner.emplace_back(53.036002, 11.597555); //Elbe + ndsCorner.emplace_back(53.453140, 10.078754); + ndsCorner.emplace_back(53.414084, 9.978458); + ndsCorner.emplace_back(53.466578, 9.806722); + ndsCorner.emplace_back(53.554267, 9.774212); + ndsCorner.emplace_back(53.610751, 9.562691); + ndsCorner.emplace_back(53.877164, 9.230297); + ndsCorner.emplace_back(53.905287, 8.647583); + ndsCorner.emplace_back(53.662161, 8.455094); + ndsCorner.emplace_back(53.607053, 8.514606); + ndsCorner.emplace_back(53.603353, 8.65325); + ndsCorner.emplace_back(53.441515, 8.582983); + ndsCorner.emplace_back(53.215264, 8.656875); + ndsCorner.emplace_back(53.052565, 9.030710); + ndsCorner.emplace_back(53.007938, 8.939740); + ndsCorner.emplace_back(53.030989, 8.735467); + ndsCorner.emplace_back(53.170472, 8.611213); + ndsCorner.emplace_back(53.174157, 8.495368); + ndsCorner.emplace_back(53.549985, 8.511206); + ndsCorner.emplace_back(53.549985, 8.511206); + ndsCorner.emplace_back(53.521320, 8.232034); + ndsCorner.emplace_back(53.522269, 8.311878); + ndsCorner.emplace_back(53.398310, 8.250894); + ndsCorner.emplace_back(53.452971, 8.136036); + ndsCorner.emplace_back(53.448714, 8.094245); + ndsCorner.emplace_back(53.501105, 8.060751); + ndsCorner.emplace_back(53.542582, 8.1698); + ndsCorner.emplace_back(53.707251, 8.03243); + ndsCorner.emplace_back(53.663311, 7.231965); + ndsCorner.emplace_back(53.349005, 6.991342); + ndsCorner.emplace_back(53.328303, 7.244930); + ndsCorner.emplace_back(53.237153, 7.205646); + ndsCorner.emplace_back(52.635858, 7.045900); + ndsCorner.emplace_back(52.647768, 6.755056); + ndsCorner.emplace_back(52.486377, 6.697683); + ndsCorner.emplace_back(52.429083, 7.010758); + ndsCorner.emplace_back(52.241226, 7.0658); //close + + return ndsCorner; +} + + +BOOST_AUTO_TEST_SUITE(spatial_conclusion) + std::string dbfile = "test_spatial_conclusion_sqlite.db"; + + BOOST_AUTO_TEST_CASE(spatial_conclusion_id_uri) + { + GeometricObject::Ptr farfaraway( new GeometricObject() ); + + auto uri = sempr::buildURI(farfaraway->id(), sempr::baseURI()); + auto id = sempr::extractID(uri, sempr::baseURI()); + + BOOST_CHECK_EQUAL(id, farfaraway->id()); + } + + BOOST_AUTO_TEST_CASE(spatial_conclusion_cities) + { + Core core; + + GeodeticCS::Ptr globalCS(new GeodeticCS()); + + SpatialIndex2D::Ptr index(new SpatialIndex2D(globalCS)); + core.addModule(index); + + SpatialConclusion2D::Ptr conclusion(new SpatialConclusion2D(index)); + core.addModule(conclusion); + + SopranoModule::Ptr soprano(new SopranoModule()); + core.addModule(soprano); + + GeometricObject::Ptr osna( new GeometricObject() ); + { + Polygon::Ptr osnaPolygon( new Polygon() ); + osnaPolygon->setCoordinates(getOsnaCoords()); + osnaPolygon->setCS(globalCS); + core.addEntity(osnaPolygon); //will not be added by the super object! + + osna->geometry(osnaPolygon); + } + core.addEntity(osna); + + GeometricObject::Ptr vechta( new GeometricObject() ); + { + Polygon::Ptr vechtaPolygon( new Polygon() ); + vechtaPolygon->setCoordinates(getVechtaCoords()); + vechtaPolygon->setCS(globalCS); + core.addEntity(vechtaPolygon); //will not be added by the super object! + + vechta->geometry(vechtaPolygon); + } + core.addEntity(vechta); + + GeometricObject::Ptr bremen( new GeometricObject() ); + { + Polygon::Ptr bremenPolygon( new Polygon() ); + bremenPolygon->setCoordinates(getBremenCoords()); + bremenPolygon->setCS(globalCS); + core.addEntity(bremenPolygon); //will not be added by the super object! + + bremen->geometry(bremenPolygon); + } + core.addEntity(bremen); + + Polygon::Ptr ndsPolygon( new Polygon() ); + ndsPolygon->setCoordinates(getNDSCoords()); + ndsPolygon->setCS(globalCS); + auto queryNDS = SpatialIndexQuery2D::intersects(ndsPolygon); + + core.answerQuery(queryNDS); + BOOST_CHECK_EQUAL(queryNDS->results.size(), 2); // Osna, Vechta and Bremen are in NDS if the query use a box. But in real Bremen is no a part of NDS. + + auto vechtaContext = conclusion->getConclusion(vechta); + + BOOST_CHECK_EQUAL(vechtaContext->size(), 4); // In the view of Vechta, Bremen is in the north east and Osna in the south west. + + SPARQLQuery::Ptr query(new SPARQLQuery()); + query->prefixes["spatial"] = "http://jena.apache.org/spatial#"; + query->query = "SELECT ?o WHERE { ?o spatial:south " + sempr::buildURI(bremen->id(), sempr::baseURI()) + " . }"; + core.answerQuery(query); + + BOOST_CHECK_EQUAL(query->results.size(), 2); // Vechta and Osnabrück are in the south of Bremen. + + // Add NDS as GeometricObject + GeometricObject::Ptr nds( new GeometricObject() ); + nds->geometry(ndsPolygon); + core.addEntity(ndsPolygon); + core.addEntity(nds); + + // Get current context of Vechta + vechtaContext = conclusion->getConclusion(vechta); + BOOST_CHECK_EQUAL(vechtaContext->size(), 4+2); // In is within and intersects nds + + // Remove Bremen + bremen->geometry()->removed(); + bremen->removed(); + //delete bremen.get(); + + vechtaContext = conclusion->getConclusion(vechta); + BOOST_CHECK_EQUAL(vechtaContext->size(), 4); // In is within and intersects nds + + } + + BOOST_AUTO_TEST_CASE(spatial_conclusion_cleanup) + { + removeStorage(dbfile); + } + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 5f09224..242cb26 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -1,3 +1,4 @@ +#include #include "test_utils.hpp" using namespace testing; @@ -28,36 +29,103 @@ BOOST_AUTO_TEST_SUITE(spatial_index) { Core core; - SpatialIndex::Ptr index(new SpatialIndex()); + LocalCS::Ptr cs(new LocalCS()); + + SpatialIndex3D::Ptr index(new SpatialIndex<3>(cs)); core.addModule(index); + + //build up a quadrangle - LocalCS::Ptr cs(new LocalCS()); MultiPoint::Ptr mp( new MultiPoint() ); mp->setGeometry(setupQuadrangle({{1, 1, 1}}, {{10, 10, 10}})); mp->setCS(cs); core.addEntity(mp); - auto queryWithinBox = SpatialIndexQuery::withinBox(Eigen::Vector3d{0, 0, 0}, Eigen::Vector3d{10, 10 ,10}, cs); + auto queryWithinBox = SpatialIndexQuery3D::withinBox(Eigen::Vector3d{0, 0, 0}, Eigen::Vector3d{10, 10 ,10}, cs); core.answerQuery(queryWithinBox); BOOST_CHECK_EQUAL(queryWithinBox->results.size(), 1); - auto queryIntersecBox = SpatialIndexQuery::intersectsBox(Eigen::Vector3d{1, 1, 1}, Eigen::Vector3d{2, 2 ,2}, cs); + auto queryIntersecBox = SpatialIndexQuery3D::intersectsBox(Eigen::Vector3d{1, 1, 1}, Eigen::Vector3d{2, 2 ,2}, cs); core.answerQuery(queryIntersecBox); BOOST_CHECK_EQUAL(queryIntersecBox->results.size(), 1); } - BOOST_AUTO_TEST_CASE(spatial_index_complex) + /* + A case like this: + |\ + /| | \ + / | | \ + / / / \ + | | | / + \ \ \ / + \ | | / + \| |/ + + Two not intersecting geometries but there convex hull will cross. + + */ + BOOST_AUTO_TEST_CASE(spatial_index_2d_geom_check) { Core core; - SpatialIndex::Ptr index(new SpatialIndex()); + LocalCS::Ptr cs(new LocalCS()); + + SpatialIndex2D::Ptr index(new SpatialIndex2D(cs)); core.addModule(index); + Polygon::Ptr left( new Polygon() ); + { + std::vector coords; + + geos::geom::Coordinate coord; + coord = geos::geom::Coordinate(1, 4); coords.push_back(coord); + coord = geos::geom::Coordinate(3, 7); coords.push_back(coord); + coord = geos::geom::Coordinate(2, 5); coords.push_back(coord); + coord = geos::geom::Coordinate(2, 3); coords.push_back(coord); + coord = geos::geom::Coordinate(3, 1); coords.push_back(coord); + coord = geos::geom::Coordinate(1, 4); coords.push_back(coord); // close + + left->setCoordinates(coords); + } + left->setCS(cs); + core.addEntity(left); + + Polygon::Ptr right( new Polygon() ); + { + std::vector coords; + + geos::geom::Coordinate coord; + coord = geos::geom::Coordinate(2.5, 4); coords.push_back(coord); + coord = geos::geom::Coordinate(5.0, 8); coords.push_back(coord); + coord = geos::geom::Coordinate(5.0, 0); coords.push_back(coord); + coord = geos::geom::Coordinate(2.5, 4); coords.push_back(coord); // close + + right->setCoordinates(coords); + } + right->setCS(cs); + + auto queryInteresctionBox = SpatialIndexQuery2D::intersectsBoxOf(right); + core.answerQuery(queryInteresctionBox); + BOOST_CHECK_EQUAL(queryInteresctionBox->results.size(), 1); + + auto queryIntersection = SpatialIndexQuery2D::intersects(right); + core.answerQuery(queryIntersection); + BOOST_CHECK_EQUAL(queryIntersection->results.size(), 0); // shall be 0. + + } + + BOOST_AUTO_TEST_CASE(spatial_index_complex) + { + Core core; + // add a spatial refernce LocalCS::Ptr cs(new LocalCS()); core.addEntity(cs); + + SpatialIndex<3>::Ptr index(new SpatialIndex<3>(cs)); + core.addModule(index); // add a few geometries, all with y/z from 0 to 1, but with different x: // 0 1 2 3 4 5 6 7 8 9 10 @@ -99,7 +167,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) std::set expected_intersects = {{ "mp7", "mp8", "mp9", "mp10", "mp11", "mp12" }}; // within - auto query = SpatialIndexQuery::withinBox(Eigen::Vector3d{7.5, -1, -1}, Eigen::Vector3d{12.5, 2, 2}, cs); + auto query = SpatialIndexQuery<3>::withinBox(Eigen::Vector3d{7.5, -1, -1}, Eigen::Vector3d{12.5, 2, 2}, cs); core.answerQuery(query); BOOST_CHECK_EQUAL(query->results.size(), expected_within.size()); @@ -110,7 +178,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) // intersects query->results.clear(); - query->mode(SpatialIndexQuery::INTERSECTS); + query->mode(SpatialQueryType::INTERSECTS); core.answerQuery(query); BOOST_CHECK_EQUAL(query->results.size(), expected_intersects.size()); diff --git a/test/test_utils.hpp b/test/test_utils.hpp index 8df8f93..54bb492 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -35,6 +35,11 @@ #include + +#include +#include +#include + // reference systems #include #include @@ -48,6 +53,7 @@ #include #include +#include using namespace sempr::core; using namespace sempr::storage;