From ca78e6213ead33f47d4d304df17fb323391ee727 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Wed, 5 Sep 2018 16:30:55 +0100 Subject: [PATCH 01/64] Allow temporary entites. Currently the temp entities uses valid IDs and will wast them. --- include/sempr/entity/Entity.hpp | 3 +- include/sempr/entity/RDFVector.hpp | 4 +-- include/sempr/entity/example/Person.hpp | 3 +- include/sempr/entity/spatial/Geometry.hpp | 4 +++ .../entity/spatial/reference/GeocentricCS.hpp | 2 +- .../entity/spatial/reference/GeodeticCS.hpp | 2 +- .../entity/spatial/reference/GlobalCS.hpp | 10 ++----- .../spatial/reference/LocalTangentPlaneCS.hpp | 2 +- .../reference/MilitaryGridReferenceSystem.hpp | 2 +- .../entity/spatial/reference/ProjectionCS.hpp | 2 +- .../UniversalPolarStereographicCS.hpp | 2 +- .../UniversalTransverseMercatorCS.hpp | 2 +- include/sempr/processing/ModuleBase.hpp | 9 ++++-- include/sempr/storage/DBObject.hpp | 7 ++++- src/entity/Entity.cpp | 4 +-- src/entity/RDFVector.cpp | 11 +++---- src/entity/example/Person.cpp | 8 +++-- src/entity/spatial/Geometry.cpp | 6 ++++ src/entity/spatial/reference/GeocentricCS.cpp | 2 +- src/entity/spatial/reference/GeodeticCS.cpp | 2 +- src/entity/spatial/reference/GlobalCS.cpp | 4 +-- .../spatial/reference/LocalTangentPlaneCS.cpp | 2 +- .../reference/MilitaryGridReferenceSystem.cpp | 2 +- src/entity/spatial/reference/ProjectionCS.cpp | 2 +- .../UniversalPolarStereographicCS.cpp | 2 +- .../UniversalTransverseMercatorCS.cpp | 2 +- src/processing/ModuleBase.cpp | 5 ++++ src/storage/DBObject.cpp | 4 +-- src/storage/ODBStorage.cpp | 18 ++++++++++- test/storage_tests.cpp | 30 +++++++++++++++++++ 30 files changed, 115 insertions(+), 43 deletions(-) diff --git a/include/sempr/entity/Entity.hpp b/include/sempr/entity/Entity.hpp index d66b06d..bec0af8 100644 --- a/include/sempr/entity/Entity.hpp +++ b/include/sempr/entity/Entity.hpp @@ -77,7 +77,8 @@ namespace entity { class Entity : public storage::DBObject, public std::enable_shared_from_this { public: Entity(); - Entity(const core::IDGenBase* idgen); + + Entity(const core::IDGenBase* idgen, bool temporary = false); virtual ~Entity(){} diff --git a/include/sempr/entity/RDFVector.hpp b/include/sempr/entity/RDFVector.hpp index 7727c96..8153e21 100644 --- a/include/sempr/entity/RDFVector.hpp +++ b/include/sempr/entity/RDFVector.hpp @@ -39,10 +39,10 @@ class RDFVector : public RDFEntity { virtual ~RDFVector(){} void getTriples(std::vector& triples) const; - Triple& getTripleAt(const size_t& index); + const Triple& getTripleAt(const size_t index); bool addTriple(const Triple& triple); bool removeTriple(const Triple& triple); - void removeTripleAt(const size_t& index); + void removeTripleAt(const size_t index); void clear(); size_t size() const; diff --git a/include/sempr/entity/example/Person.hpp b/include/sempr/entity/example/Person.hpp index 40f8713..dafd5f5 100644 --- a/include/sempr/entity/example/Person.hpp +++ b/include/sempr/entity/example/Person.hpp @@ -15,7 +15,8 @@ class Person : public Entity { enum Gender { MALE, FEMALE, UNKNOWN }; Person(); - Person(const core::IDGenBase*); + Person(bool temporary); + Person(const core::IDGenBase*, bool temporary = false); virtual ~Person(){} /** diff --git a/include/sempr/entity/spatial/Geometry.hpp b/include/sempr/entity/spatial/Geometry.hpp index 30745bd..8b506c4 100644 --- a/include/sempr/entity/spatial/Geometry.hpp +++ b/include/sempr/entity/spatial/Geometry.hpp @@ -82,6 +82,10 @@ class Geometry : public Entity */ virtual geom::Geometry* getGeometryMut(); + /** + * @brief Apply a give filter or a sorted list of filters to all coordinates of the geometry as read write filter. + * This will not fire an event! + */ void apply(Filter& filter); void apply(FilterList& filterList); diff --git a/include/sempr/entity/spatial/reference/GeocentricCS.hpp b/include/sempr/entity/spatial/reference/GeocentricCS.hpp index 538984b..7716f1d 100644 --- a/include/sempr/entity/spatial/reference/GeocentricCS.hpp +++ b/include/sempr/entity/spatial/reference/GeocentricCS.hpp @@ -18,7 +18,7 @@ class GeocentricCS : public GlobalCS { public: using Ptr = std::shared_ptr; - virtual FilterList to(const GlobalCS::Ptr other) override; + virtual FilterList to(const GlobalCS::Ptr other) const override; protected: diff --git a/include/sempr/entity/spatial/reference/GeodeticCS.hpp b/include/sempr/entity/spatial/reference/GeodeticCS.hpp index 2b78eb8..43bcfdc 100644 --- a/include/sempr/entity/spatial/reference/GeodeticCS.hpp +++ b/include/sempr/entity/spatial/reference/GeodeticCS.hpp @@ -20,7 +20,7 @@ class GeodeticCS : public GlobalCS { GeodeticCS(); GeodeticCS(const core::IDGenBase*); - FilterList to(const GlobalCS::Ptr other) override; + FilterList to(const GlobalCS::Ptr other) const override; protected: FilterPtr forward() const override; diff --git a/include/sempr/entity/spatial/reference/GlobalCS.hpp b/include/sempr/entity/spatial/reference/GlobalCS.hpp index 1cdcd96..0ac177f 100644 --- a/include/sempr/entity/spatial/reference/GlobalCS.hpp +++ b/include/sempr/entity/spatial/reference/GlobalCS.hpp @@ -25,20 +25,14 @@ class GlobalCS : public SpatialReference { Eigen::Affine3d transformationFromRoot() const override; virtual ~GlobalCS(); - /** - Create an OGRCoordinateTransformation from this to other. Only available for global - coordinate systems that use a OGRSpatialReference, as this task is simply delegated to GDAL. - */ - //std::shared_ptr to(GlobalCS::Ptr other); - //from this to other - virtual FilterList to(const GlobalCS::Ptr other); + virtual FilterList to(const GlobalCS::Ptr other) const; /** * Check if this and the other global cs are equal. * Note: This have to be override by derived class with attributes! */ - virtual bool isEqual(const GlobalCS::Ptr other); + virtual bool isEqual(const GlobalCS::Ptr other) const; protected: diff --git a/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp b/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp index 8e211ee..2429f2e 100644 --- a/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp +++ b/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp @@ -21,7 +21,7 @@ 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) override; + bool isEqual(const GlobalCS::Ptr other) const override; protected: LocalTangentPlaneCS(); diff --git a/include/sempr/entity/spatial/reference/MilitaryGridReferenceSystem.hpp b/include/sempr/entity/spatial/reference/MilitaryGridReferenceSystem.hpp index 2f62194..96a45c5 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) override; + bool isEqual(const GlobalCS::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 dcebbab..c575067 100644 --- a/include/sempr/entity/spatial/reference/ProjectionCS.hpp +++ b/include/sempr/entity/spatial/reference/ProjectionCS.hpp @@ -22,7 +22,7 @@ class ProjectionCS : public GlobalCS { public: using Ptr = std::shared_ptr; - virtual FilterList to(const GlobalCS::Ptr other) override; + virtual FilterList to(const GlobalCS::Ptr other) const override; virtual int getZone() const; virtual bool isNorth() const; diff --git a/include/sempr/entity/spatial/reference/UniversalPolarStereographicCS.hpp b/include/sempr/entity/spatial/reference/UniversalPolarStereographicCS.hpp index 5813eb1..0ddeac2 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) override; + bool isEqual(const GlobalCS::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 9006b3a..d416254 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) override; + bool isEqual(const GlobalCS::Ptr other) const override; int getZone() const override; bool isNorth() const override; diff --git a/include/sempr/processing/ModuleBase.hpp b/include/sempr/processing/ModuleBase.hpp index 67a1e14..b22912c 100644 --- a/include/sempr/processing/ModuleBase.hpp +++ b/include/sempr/processing/ModuleBase.hpp @@ -24,10 +24,15 @@ class ModuleBase : public core::Observer { friend class core::Core; protected: /** - Allows modules to ask queries. - */ + * Allows modules to ask queries. + */ void ask(query::Query::Ptr q); + /** + * Return the pointer to the registered core + */ + core::Core* core(); + public: using Ptr = std::shared_ptr; diff --git a/include/sempr/storage/DBObject.hpp b/include/sempr/storage/DBObject.hpp index 2851931..d91b448 100644 --- a/include/sempr/storage/DBObject.hpp +++ b/include/sempr/storage/DBObject.hpp @@ -35,7 +35,7 @@ class DBObject { strategy for the most derived type. (--> Chair_1 instead of DBObject_37) */ - DBObject(const core::IDGenBase* idgen); + DBObject(const core::IDGenBase* idgen, bool temporary = false); /** Copy constructors are too confusing, so I disabled them for all entities. @@ -56,6 +56,7 @@ class DBObject { */ bool persisted() const { return persisted_; } + bool temporary() const { return temporary_; } std::string id() const { return id_; } @@ -105,6 +106,10 @@ class DBObject { #pragma db transient mutable bool persisted_; + /// A temporoy object will never be stored in the database. + #pragma db transient + bool temporary_; + protected: /// The wrapper from which the given id was generated. /// this is used to revoke the id in preLoad. diff --git a/src/entity/Entity.cpp b/src/entity/Entity.cpp index 5a1c9eb..92f8e86 100644 --- a/src/entity/Entity.cpp +++ b/src/entity/Entity.cpp @@ -8,8 +8,8 @@ namespace sempr { namespace entity { SEMPR_ENTITY_SOURCE(Entity) -Entity::Entity(const core::IDGenBase* idgen) - : DBObject(idgen), announced_(false) +Entity::Entity(const core::IDGenBase* idgen, bool temporary) + : DBObject(idgen, temporary), announced_(false) { // get the eventbroker broker_ = core::EventBroker::getInstance(); diff --git a/src/entity/RDFVector.cpp b/src/entity/RDFVector.cpp index 9c39333..8c1d866 100644 --- a/src/entity/RDFVector.cpp +++ b/src/entity/RDFVector.cpp @@ -51,7 +51,7 @@ void RDFVector::getTriples(std::vector &triples) const triples.insert(triples.end(), triples_.begin(), triples_.end()); } -Triple& RDFVector::getTripleAt(const size_t &index) +const Triple& RDFVector::getTripleAt(const size_t index) { return triples_[index]; } @@ -67,7 +67,7 @@ bool RDFVector::addTriple(const sempr::entity::Triple &triple) // but add anyway, necessary for RDFPropertyMap etc triples_.push_back(triple); - this->changed(); + //this->changed(); return st.isValid(); } @@ -78,18 +78,19 @@ bool RDFVector::removeTriple(const sempr::entity::Triple &triple) if (newEnd == triples_.end()) return false; triples_.erase(newEnd, triples_.end()); - this->changed(); + //this->changed(); return true; } -void RDFVector::removeTripleAt(const size_t& index) { +void RDFVector::removeTripleAt(const size_t index) { triples_.erase(triples_.begin() + index); + //this->changed(); } void RDFVector::clear() { triples_.clear(); - this->changed(); + //this->changed(); } size_t RDFVector::size() const { diff --git a/src/entity/example/Person.cpp b/src/entity/example/Person.cpp index afa6889..facee29 100644 --- a/src/entity/example/Person.cpp +++ b/src/entity/example/Person.cpp @@ -7,8 +7,8 @@ namespace sempr { namespace entity { SEMPR_ENTITY_SOURCE(Person) -Person::Person(const core::IDGenBase* idgen) - : Entity(idgen) +Person::Person(const core::IDGenBase* idgen, bool temporary) + : Entity(idgen, temporary) { setDiscriminator(); prop_ = RDFPropertyMap::Ptr(new RDFPropertyMap(*this)); @@ -27,6 +27,10 @@ Person::Person(const core::IDGenBase* idgen) m("type", core::rdf::baseURI()) = RDFResource("<" + sempr::baseURI() + "Person" + ">"); } +Person::Person(bool temporary) : Person(new core::IDGen(), temporary) +{ +} + Person::Person() : Person(new core::IDGen()) { } diff --git a/src/entity/spatial/Geometry.cpp b/src/entity/spatial/Geometry.cpp index 431a0e4..8c4e618 100644 --- a/src/entity/spatial/Geometry.cpp +++ b/src/entity/spatial/Geometry.cpp @@ -54,6 +54,8 @@ Geometry* Geometry::raw_clone() const void Geometry::setCS(SpatialReference::Ptr cs) { referenceFrame_ = cs; + + //this->changed(); } SpatialReference::Ptr Geometry::getCS() const @@ -108,6 +110,8 @@ void Geometry::transformToCS(SpatialReference::Ptr cs) // geometry = toOther * fromThis * geometry LocalTransformationFilter tf(toOther * fromThis); apply(tf); + + //this->changed(); } // 2. the geometries have different roots but both a global (e.g., one is on WGS84, @@ -139,6 +143,8 @@ void Geometry::transformToCS(SpatialReference::Ptr cs) auto rootToCS = cs->transformationFromRoot(); LocalTransformationFilter tfToCS(rootToCS); apply(tfToCS); + + //this->changed(); } else { diff --git a/src/entity/spatial/reference/GeocentricCS.cpp b/src/entity/spatial/reference/GeocentricCS.cpp index 289dba2..3c2e721 100644 --- a/src/entity/spatial/reference/GeocentricCS.cpp +++ b/src/entity/spatial/reference/GeocentricCS.cpp @@ -19,7 +19,7 @@ GeocentricCS::GeocentricCS(const core::IDGenBase* idgen) : this->setDiscriminator(); } -FilterList GeocentricCS::to(const GlobalCS::Ptr other) +FilterList GeocentricCS::to(const GlobalCS::Ptr other) const { //transform from this (Geodetic) auto otherGeodetic = std::dynamic_pointer_cast(other); diff --git a/src/entity/spatial/reference/GeodeticCS.cpp b/src/entity/spatial/reference/GeodeticCS.cpp index 897cdb3..fca14b2 100644 --- a/src/entity/spatial/reference/GeodeticCS.cpp +++ b/src/entity/spatial/reference/GeodeticCS.cpp @@ -18,7 +18,7 @@ GeodeticCS::GeodeticCS(const core::IDGenBase* idgen) this->setDiscriminator(); } -FilterList GeodeticCS::to(const GlobalCS::Ptr other) +FilterList GeodeticCS::to(const GlobalCS::Ptr other) const { if (isEqual(other)) diff --git a/src/entity/spatial/reference/GlobalCS.cpp b/src/entity/spatial/reference/GlobalCS.cpp index 47a26a2..049fd7a 100644 --- a/src/entity/spatial/reference/GlobalCS.cpp +++ b/src/entity/spatial/reference/GlobalCS.cpp @@ -22,7 +22,7 @@ GlobalCS::~GlobalCS() { } -bool GlobalCS::isEqual(const GlobalCS::Ptr other) +bool GlobalCS::isEqual(const GlobalCS::Ptr other) const { // avoid expr. side effects warning: auto& tmpOther = *other; @@ -47,7 +47,7 @@ Eigen::Affine3d GlobalCS::transformationFromRoot() const return Eigen::Affine3d::Identity(); } -FilterList GlobalCS::to(const GlobalCS::Ptr other) +FilterList GlobalCS::to(const GlobalCS::Ptr other) const { //only for ODB support - not abstract assert(1); diff --git a/src/entity/spatial/reference/LocalTangentPlaneCS.cpp b/src/entity/spatial/reference/LocalTangentPlaneCS.cpp index 5b06bae..c532ecf 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) +bool LocalTangentPlaneCS::isEqual(const GlobalCS::Ptr other) const { auto otherLTG = std::dynamic_pointer_cast(other); diff --git a/src/entity/spatial/reference/MilitaryGridReferenceSystem.cpp b/src/entity/spatial/reference/MilitaryGridReferenceSystem.cpp index c1954a1..95fed69 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) +bool MilitaryGridReferenceSystem::isEqual(const GlobalCS::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 0ec29e1..fdd8615 100644 --- a/src/entity/spatial/reference/ProjectionCS.cpp +++ b/src/entity/spatial/reference/ProjectionCS.cpp @@ -20,7 +20,7 @@ ProjectionCS::ProjectionCS(const core::IDGenBase* idgen) : this->setDiscriminator(); } -FilterList ProjectionCS::to(const GlobalCS::Ptr other) +FilterList ProjectionCS::to(const GlobalCS::Ptr other) const { //transform from this (Geodetic) auto otherGeodetic = std::dynamic_pointer_cast(other); diff --git a/src/entity/spatial/reference/UniversalPolarStereographicCS.cpp b/src/entity/spatial/reference/UniversalPolarStereographicCS.cpp index 34eb589..6b028bc 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) +bool UniversalPolarStereographicCS::isEqual(const GlobalCS::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 6ab0ec9..2d1aeed 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) +bool UniversalTransverseMercatorCS::isEqual(const GlobalCS::Ptr other) const { auto otherUTM = std::dynamic_pointer_cast(other); diff --git a/src/processing/ModuleBase.cpp b/src/processing/ModuleBase.cpp index b1f1f47..612902f 100644 --- a/src/processing/ModuleBase.cpp +++ b/src/processing/ModuleBase.cpp @@ -12,6 +12,11 @@ void ModuleBase::ask(query::Query::Ptr q) if (this->core_) { this->core_->answerQuery(q); } } +core::Core* ModuleBase::core() +{ + return core_; +} + } /* processing */ } /* sempr */ diff --git a/src/storage/DBObject.cpp b/src/storage/DBObject.cpp index 82009d7..7e4f4fe 100644 --- a/src/storage/DBObject.cpp +++ b/src/storage/DBObject.cpp @@ -8,8 +8,8 @@ DBObject::DBObject() { } -DBObject::DBObject(const core::IDGenBase* idgen) - : persisted_(false), idgenerator_(idgen) +DBObject::DBObject(const core::IDGenBase* idgen, bool temporary) + : persisted_(false), idgenerator_(idgen), temporary_(temporary) { id_ = idgen->generate(); setDiscriminator(); diff --git a/src/storage/ODBStorage.cpp b/src/storage/ODBStorage.cpp index af29b7e..8a9c01a 100644 --- a/src/storage/ODBStorage.cpp +++ b/src/storage/ODBStorage.cpp @@ -32,6 +32,12 @@ void ODBStorage::save( std::vector& in ) { // std::cout << "got bulk data: " << in.size() << " entries" << std::endl; odb::transaction t(db_->begin()); for(auto o : in){ + + // continue if o is only an temporary object + if(o->temporary()) + continue; + + // otherwise persist it try { // std::cout << "save " << o->id() << '\n'; if (!o->persisted()) { @@ -45,11 +51,16 @@ void ODBStorage::save( std::vector& in ) { std::cout << "ODBStorage::save: object " << o->id() << " already persistent. (batch!)" << "\n"; throw ex; } + } t.commit(); } void ODBStorage::save( DBObject::Ptr o ) { + + if(o->temporary()) + return; + // std::cout << "single-save: " << o->id() << '\n'; try { odb::transaction t(db_->begin()); @@ -85,6 +96,10 @@ void ODBStorage::loadAll(std::vector &data) { } void ODBStorage::remove(DBObject::Ptr data) { + + if(!data->persisted()) + return; + odb::transaction t( db_->begin() ); db_->erase(data); t.commit(); @@ -95,7 +110,8 @@ void ODBStorage::remove(std::vector& data) odb::transaction t( db_->begin() ); for (auto o : data) { // std::cout << "remove: " << o->id() << '\n'; - db_->erase(o); + if(o->persisted()) + db_->erase(o); } t.commit(); } diff --git a/test/storage_tests.cpp b/test/storage_tests.cpp index 6e28c34..f88dffd 100644 --- a/test/storage_tests.cpp +++ b/test/storage_tests.cpp @@ -169,6 +169,7 @@ BOOST_AUTO_TEST_CASE(update) { //core.addModule(debug); } + BOOST_AUTO_TEST_CASE(deletion) { std::string id; @@ -249,3 +250,32 @@ BOOST_AUTO_TEST_SUITE(register_children_no_duplicates) removeStorage(db_path); } BOOST_AUTO_TEST_SUITE_END() + + +BOOST_AUTO_TEST_SUITE(persistent) + std::string db_path = "test_sqlite.db"; + + BOOST_AUTO_TEST_CASE(persistent_check) + { + + { + ODBStorage::Ptr storage = setUpStorage(db_path, true); + Core core; + DBUpdateModule::Ptr updater(new DBUpdateModule(storage)); + core.addModule(updater); + + Person::Ptr person(new Person(false)); + core.addEntity(person); + + Person::Ptr person2(new Person(true)); + core.addEntity(person2); + + updater->updateDatabase(); + + BOOST_CHECK(person->persisted()); + BOOST_CHECK(!person2->persisted()); + } + + removeStorage(db_path); + } +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From cbbe37b83cab8f719e39d8eab238a0b175c73531 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Thu, 6 Sep 2018 10:42:38 +0100 Subject: [PATCH 02/64] Add a RDFValue based Triple implementation and remove document from triple struct. This will close #33 --- include/sempr/entity/RDFPropertyMap.hpp | 2 +- include/sempr/entity/RDFValue.hpp | 5 +++++ include/sempr/entity/RDFVector.hpp | 11 ++++------- include/sempr/entity/Triple.hpp | 21 +++++++++++---------- src/entity/RDFDocument.cpp | 3 +-- src/entity/RDFPropertyMap.cpp | 1 - src/entity/RDFVector.cpp | 17 ++++++++++++----- src/storage/DBObject.cpp | 2 +- test/SemanticEntity_tests.cpp | 2 +- 9 files changed, 36 insertions(+), 28 deletions(-) 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..3536607 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 @@ -235,6 +238,8 @@ template <> RDFValue& RDFValue::operator = (const RDFResource& other); // template <> RDFValue& RDFValue::operator = (const char& value); // done via overloading inside class. +typedef std::tuple RDFValueTriple; + }} #endif /* end of include guard: SEMPR_ENTITY_RDFVALUE_HPP_ */ diff --git a/include/sempr/entity/RDFVector.hpp b/include/sempr/entity/RDFVector.hpp index 8153e21..97c604b 100644 --- a/include/sempr/entity/RDFVector.hpp +++ b/include/sempr/entity/RDFVector.hpp @@ -2,13 +2,8 @@ #define SEMPR_ENTITY_RDFVECTOR_H_ #include -#include -#include -#include -#include -// #include -#include +#include #include @@ -40,7 +35,7 @@ class RDFVector : public RDFEntity { 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(); @@ -54,6 +49,8 @@ class RDFVector : public RDFEntity { std::vector triples_; }; + + }} #endif /* end of include guard: SEMPR_ENTITY_RDFVECTOR_H_ */ diff --git a/include/sempr/entity/Triple.hpp b/include/sempr/entity/Triple.hpp index 80e4e95..10235e1 100644 --- a/include/sempr/entity/Triple.hpp +++ b/include/sempr/entity/Triple.hpp @@ -3,6 +3,7 @@ #define SEMPR_ENTITY_TRIPLE #include +#include namespace sempr { namespace entity { @@ -10,31 +11,31 @@ namespace sempr { namespace entity { 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 RDFValueTriple& valueTriple) // allow implicit type convert from complex to simple triple { + subject = std::get<0>(valueTriple).toString(); + predicate = std::get<1>(valueTriple); + object = std::get<2>(valueTriple).toString(); } - 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) + Triple(const std::string& s, const std::string& p, const std::string& o) + : 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/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/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..69bc41a 100644 --- a/src/entity/RDFVector.cpp +++ b/src/entity/RDFVector.cpp @@ -56,7 +56,7 @@ const Triple& RDFVector::getTripleAt(const size_t index) return triples_[index]; } -bool RDFVector::addTriple(const sempr::entity::Triple &triple) +bool RDFVector::addTriple(const sempr::entity::Triple &triple, bool replace) { // check if the triple is valid! auto sub = Soprano::Node::fromN3(QString::fromStdString(triple.subject)); @@ -65,11 +65,18 @@ bool RDFVector::addTriple(const sempr::entity::Triple &triple) Soprano::Statement st(sub, pred, obj); - // but add anyway, necessary for RDFPropertyMap etc - triples_.push_back(triple); - //this->changed(); + bool isValid = st.isValid(); + + if (isValid) + { + if (replace) + removeTriple(triple); + + triples_.push_back(triple); + //this->changed(); + } - return st.isValid(); + return isValid; } bool RDFVector::removeTriple(const sempr::entity::Triple &triple) diff --git a/src/storage/DBObject.cpp b/src/storage/DBObject.cpp index 7e4f4fe..7fac279 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/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; } } From d9b28f6f6dac4aa93c3c948a71944d18cc832bae Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Thu, 6 Sep 2018 14:12:54 +0100 Subject: [PATCH 03/64] Add a RDFValue based variant of a Triple that is now be used in the RDFVector. Also the RDFVector could now be temporary. --- include/sempr/entity/RDFEntity.hpp | 2 +- include/sempr/entity/RDFValue.hpp | 46 +++++++++++++++++++++++++++++- include/sempr/entity/RDFVector.hpp | 22 +++++++------- include/sempr/entity/Triple.hpp | 8 +++--- src/entity/RDFDocument.cpp | 2 +- src/entity/RDFEntity.cpp | 12 ++++---- src/entity/RDFVector.cpp | 43 +++++++++++++++++----------- 7 files changed, 96 insertions(+), 39 deletions(-) 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/RDFValue.hpp b/include/sempr/entity/RDFValue.hpp index 3536607..2bb022c 100644 --- a/include/sempr/entity/RDFValue.hpp +++ b/include/sempr/entity/RDFValue.hpp @@ -238,7 +238,51 @@ template <> RDFValue& RDFValue::operator = (const RDFResource& other); // template <> RDFValue& RDFValue::operator = (const char& value); // done via overloading inside class. -typedef std::tuple RDFValueTriple; + +#pragma db value +struct RDFValueTriple +{ + RDFValue subject; + std::string predicate; + RDFValue object; + + RDFValueTriple() {} + + RDFValueTriple(const std::string& s, const std::string& p, const std::string& o) + { + subject = s; + predicate = p; + object = o; + } + + RDFValueTriple(const RDFValue& s, const std::string& p, const RDFValue& o) + : subject(s), predicate(p), object(o) + { + } +/* + std::string subject() const + { + return subject.toString(); + } + + std::string predicate() const + { + return predicate; + } + + std::string object() const + { + return object.toString(); + } +*/ + bool operator==(const RDFValueTriple& other) + { + return (subject == other.subject) && + (predicate == other.predicate) && + (object == other.object); + } +}; + }} diff --git a/include/sempr/entity/RDFVector.hpp b/include/sempr/entity/RDFVector.hpp index 97c604b..193aa4a 100644 --- a/include/sempr/entity/RDFVector.hpp +++ b/include/sempr/entity/RDFVector.hpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -12,9 +13,9 @@ namespace sempr { namespace entity { // custom triple-iterator for the vector class class RDFVectorIterator : public TripleIterator { friend class RDFVector; - std::vector::const_iterator vit_; + std::vector::const_iterator vit_; - RDFVectorIterator(std::vector::const_iterator it); + RDFVectorIterator(std::vector::const_iterator it); ~RDFVectorIterator(); const Triple operator * () const override; @@ -22,21 +23,20 @@ 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 replace = false); // if replace than an equal triple will be removed before, otherwise there a multiple entries possible - bool removeTriple(const Triple& triple); + void getTriples(std::vector& triples) const; + const RDFValueTriple& getTripleAt(const size_t index); + bool addTriple(const RDFValueTriple& triple, bool replace = false); // if replace than an equal triple will be removed before, otherwise there a multiple entries possible + bool removeTriple(const RDFValueTriple& triple); void removeTripleAt(const size_t index); void clear(); size_t size() const; @@ -44,9 +44,11 @@ class RDFVector : public RDFEntity { TripleIteratorWrapper begin() const override; TripleIteratorWrapper end() const override; + bool validity(const RDFValueTriple& triple) const; + protected: friend class odb::access; - std::vector triples_; + std::vector triples_; }; diff --git a/include/sempr/entity/Triple.hpp b/include/sempr/entity/Triple.hpp index 10235e1..0ed62c9 100644 --- a/include/sempr/entity/Triple.hpp +++ b/include/sempr/entity/Triple.hpp @@ -8,14 +8,14 @@ namespace sempr { namespace entity { #pragma db value -struct Triple { +struct Triple { //deprecated could be replaced by the RDFValueTriple as class with an subject(), predicate(), object() getter for the strings. ToDo! Triple() {} Triple(const RDFValueTriple& valueTriple) // allow implicit type convert from complex to simple triple { - subject = std::get<0>(valueTriple).toString(); - predicate = std::get<1>(valueTriple); - object = std::get<2>(valueTriple).toString(); + subject = valueTriple.subject.toString(); + predicate = valueTriple.predicate; + object = valueTriple.object.toString(); } Triple(const std::string& s, const std::string& p, const std::string& o) diff --git a/src/entity/RDFDocument.cpp b/src/entity/RDFDocument.cpp index d318a81..8989407 100644 --- a/src/entity/RDFDocument.cpp +++ b/src/entity/RDFDocument.cpp @@ -54,7 +54,7 @@ RDFDocument::Ptr RDFDocument::FromFile(const std::string &file) { // qDebug() << *statements; doc->addTriple( - Triple( (*statements).subject().toN3().toStdString(), + RDFValueTriple( (*statements).subject().toN3().toStdString(), (*statements).predicate().toN3().toStdString(), (*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/RDFVector.cpp b/src/entity/RDFVector.cpp index 69bc41a..7b56e82 100644 --- a/src/entity/RDFVector.cpp +++ b/src/entity/RDFVector.cpp @@ -5,7 +5,7 @@ namespace sempr { namespace entity { // iterator impl -RDFVectorIterator::RDFVectorIterator(std::vector::const_iterator it) +RDFVectorIterator::RDFVectorIterator(std::vector::const_iterator it) : vit_(it) { } @@ -34,38 +34,36 @@ 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()) { } -void RDFVector::getTriples(std::vector &triples) const +void RDFVector::getTriples(std::vector &triples) const { triples.insert(triples.end(), triples_.begin(), triples_.end()); } -const Triple& RDFVector::getTripleAt(const size_t index) +const RDFValueTriple& RDFVector::getTripleAt(const size_t index) { return triples_[index]; } -bool RDFVector::addTriple(const sempr::entity::Triple &triple, bool replace) +bool RDFVector::addTriple(const RDFValueTriple& triple, bool replace) { - // 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)); - - Soprano::Statement st(sub, pred, obj); - - bool isValid = st.isValid(); + bool isValid = validity(triple); if (isValid) { @@ -79,7 +77,20 @@ bool RDFVector::addTriple(const sempr::entity::Triple &triple, bool replace) return isValid; } -bool RDFVector::removeTriple(const sempr::entity::Triple &triple) +bool RDFVector::validity(const RDFValueTriple& triple) const +{ + Triple t = triple; + // check if the triple is valid! + 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); + + return st.isValid(); +} + +bool RDFVector::removeTriple(const RDFValueTriple& triple) { auto newEnd = std::remove(triples_.begin(), triples_.end(), triple); if (newEnd == triples_.end()) return false; From aa1d52098160f949c5674863cc6746fecce905be Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Wed, 12 Sep 2018 13:34:46 +0200 Subject: [PATCH 04/64] Remove RDFValueTriple Try --- include/sempr/entity/RDFValue.hpp | 46 ------------------------------ include/sempr/entity/RDFVector.hpp | 17 ++++++----- include/sempr/entity/Triple.hpp | 9 ------ src/entity/RDFDocument.cpp | 2 +- src/entity/RDFVector.cpp | 12 ++++---- 5 files changed, 15 insertions(+), 71 deletions(-) diff --git a/include/sempr/entity/RDFValue.hpp b/include/sempr/entity/RDFValue.hpp index 2bb022c..0a1e350 100644 --- a/include/sempr/entity/RDFValue.hpp +++ b/include/sempr/entity/RDFValue.hpp @@ -238,52 +238,6 @@ template <> RDFValue& RDFValue::operator = (const RDFResource& other); // template <> RDFValue& RDFValue::operator = (const char& value); // done via overloading inside class. - -#pragma db value -struct RDFValueTriple -{ - RDFValue subject; - std::string predicate; - RDFValue object; - - RDFValueTriple() {} - - RDFValueTriple(const std::string& s, const std::string& p, const std::string& o) - { - subject = s; - predicate = p; - object = o; - } - - RDFValueTriple(const RDFValue& s, const std::string& p, const RDFValue& o) - : subject(s), predicate(p), object(o) - { - } -/* - std::string subject() const - { - return subject.toString(); - } - - std::string predicate() const - { - return predicate; - } - - std::string object() const - { - return object.toString(); - } -*/ - bool operator==(const RDFValueTriple& other) - { - return (subject == other.subject) && - (predicate == other.predicate) && - (object == other.object); - } -}; - - }} #endif /* end of include guard: SEMPR_ENTITY_RDFVALUE_HPP_ */ diff --git a/include/sempr/entity/RDFVector.hpp b/include/sempr/entity/RDFVector.hpp index 193aa4a..ab56ddb 100644 --- a/include/sempr/entity/RDFVector.hpp +++ b/include/sempr/entity/RDFVector.hpp @@ -4,7 +4,6 @@ #include #include -#include #include @@ -13,9 +12,9 @@ namespace sempr { namespace entity { // custom triple-iterator for the vector class class RDFVectorIterator : public TripleIterator { friend class RDFVector; - std::vector::const_iterator vit_; + std::vector::const_iterator vit_; - RDFVectorIterator(std::vector::const_iterator it); + RDFVectorIterator(std::vector::const_iterator it); ~RDFVectorIterator(); const Triple operator * () const override; @@ -33,10 +32,10 @@ class RDFVector : public RDFEntity { RDFVector(const core::IDGenBase*, bool temporary = false); virtual ~RDFVector(){} - void getTriples(std::vector& triples) const; - const RDFValueTriple& getTripleAt(const size_t index); - bool addTriple(const RDFValueTriple& triple, bool replace = false); // if replace than an equal triple will be removed before, otherwise there a multiple entries possible - bool removeTriple(const RDFValueTriple& triple); + void getTriples(std::vector& triples) const; + const Triple& getTripleAt(const size_t index); + 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(); size_t size() const; @@ -44,11 +43,11 @@ class RDFVector : public RDFEntity { TripleIteratorWrapper begin() const override; TripleIteratorWrapper end() const override; - bool validity(const RDFValueTriple& triple) const; + bool validity(const Triple& triple) const; protected: friend class odb::access; - std::vector triples_; + std::vector triples_; }; diff --git a/include/sempr/entity/Triple.hpp b/include/sempr/entity/Triple.hpp index 0ed62c9..848c738 100644 --- a/include/sempr/entity/Triple.hpp +++ b/include/sempr/entity/Triple.hpp @@ -3,21 +3,12 @@ #define SEMPR_ENTITY_TRIPLE #include -#include - namespace sempr { namespace entity { #pragma db value struct Triple { //deprecated could be replaced by the RDFValueTriple as class with an subject(), predicate(), object() getter for the strings. ToDo! Triple() {} - Triple(const RDFValueTriple& valueTriple) // allow implicit type convert from complex to simple triple - { - subject = valueTriple.subject.toString(); - predicate = valueTriple.predicate; - object = valueTriple.object.toString(); - } - Triple(const std::string& s, const std::string& p, const std::string& o) : subject(s), predicate(p), object(o) { diff --git a/src/entity/RDFDocument.cpp b/src/entity/RDFDocument.cpp index 8989407..d318a81 100644 --- a/src/entity/RDFDocument.cpp +++ b/src/entity/RDFDocument.cpp @@ -54,7 +54,7 @@ RDFDocument::Ptr RDFDocument::FromFile(const std::string &file) { // qDebug() << *statements; doc->addTriple( - RDFValueTriple( (*statements).subject().toN3().toStdString(), + Triple( (*statements).subject().toN3().toStdString(), (*statements).predicate().toN3().toStdString(), (*statements).object().toN3().toStdString() ) ); diff --git a/src/entity/RDFVector.cpp b/src/entity/RDFVector.cpp index 7b56e82..098159d 100644 --- a/src/entity/RDFVector.cpp +++ b/src/entity/RDFVector.cpp @@ -5,7 +5,7 @@ namespace sempr { namespace entity { // iterator impl -RDFVectorIterator::RDFVectorIterator(std::vector::const_iterator it) +RDFVectorIterator::RDFVectorIterator(std::vector::const_iterator it) : vit_(it) { } @@ -51,17 +51,17 @@ RDFVector::RDFVector() : } -void RDFVector::getTriples(std::vector &triples) const +void RDFVector::getTriples(std::vector &triples) const { triples.insert(triples.end(), triples_.begin(), triples_.end()); } -const RDFValueTriple& RDFVector::getTripleAt(const size_t index) +const Triple& RDFVector::getTripleAt(const size_t index) { return triples_[index]; } -bool RDFVector::addTriple(const RDFValueTriple& triple, bool replace) +bool RDFVector::addTriple(const Triple& triple, bool replace) { bool isValid = validity(triple); @@ -77,7 +77,7 @@ bool RDFVector::addTriple(const RDFValueTriple& triple, bool replace) return isValid; } -bool RDFVector::validity(const RDFValueTriple& triple) const +bool RDFVector::validity(const Triple& triple) const { Triple t = triple; // check if the triple is valid! @@ -90,7 +90,7 @@ bool RDFVector::validity(const RDFValueTriple& triple) const return st.isValid(); } -bool RDFVector::removeTriple(const RDFValueTriple& triple) +bool RDFVector::removeTriple(const Triple& triple) { auto newEnd = std::remove(triples_.begin(), triples_.end(), triple); if (newEnd == triples_.end()) return false; From a62409748207732c53b63d0b13612e7bb44fe019 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 14 Sep 2018 11:28:15 +0200 Subject: [PATCH 05/64] isEqual check for all ReferenceSystems --- include/sempr/entity/spatial/reference/GlobalCS.hpp | 7 ------- include/sempr/entity/spatial/reference/LocalCS.hpp | 1 + .../spatial/reference/LocalTangentPlaneCS.hpp | 2 +- .../reference/MilitaryGridReferenceSystem.hpp | 2 +- .../entity/spatial/reference/SpatialReference.hpp | 6 ++++++ .../reference/UniversalPolarStereographicCS.hpp | 2 +- .../reference/UniversalTransverseMercatorCS.hpp | 2 +- src/entity/spatial/reference/GlobalCS.cpp | 9 --------- src/entity/spatial/reference/LocalCS.cpp | 13 +++++++++++++ .../spatial/reference/LocalTangentPlaneCS.cpp | 2 +- .../reference/MilitaryGridReferenceSystem.cpp | 2 +- src/entity/spatial/reference/SpatialReference.cpp | 11 +++++++++++ .../reference/UniversalPolarStereographicCS.cpp | 2 +- .../reference/UniversalTransverseMercatorCS.cpp | 2 +- 14 files changed, 39 insertions(+), 24 deletions(-) diff --git a/include/sempr/entity/spatial/reference/GlobalCS.hpp b/include/sempr/entity/spatial/reference/GlobalCS.hpp index 0ac177f..0693a78 100644 --- a/include/sempr/entity/spatial/reference/GlobalCS.hpp +++ b/include/sempr/entity/spatial/reference/GlobalCS.hpp @@ -28,13 +28,6 @@ class GlobalCS : public SpatialReference { //from this to other virtual FilterList to(const GlobalCS::Ptr other) const; - /** - * Check if this and the other global cs are equal. - * Note: This have to be override by derived class with attributes! - */ - virtual bool isEqual(const GlobalCS::Ptr other) const; - - protected: GlobalCS(); GlobalCS(const core::IDGenBase*); 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..071576c 100644 --- a/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp +++ b/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp @@ -21,7 +21,7 @@ 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; 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/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/src/entity/spatial/reference/GlobalCS.cpp b/src/entity/spatial/reference/GlobalCS.cpp index 049fd7a..9f238aa 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 diff --git a/src/entity/spatial/reference/LocalCS.cpp b/src/entity/spatial/reference/LocalCS.cpp index 239f9c8..2f8fa58 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: + return 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..4c424f8 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); 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/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); From 968dd6e8b035ee0d927ccbe0dd341c20c68e5565 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 14 Sep 2018 14:35:42 +0200 Subject: [PATCH 06/64] Fix isEqual for no parent of a local cs --- src/entity/spatial/reference/LocalCS.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity/spatial/reference/LocalCS.cpp b/src/entity/spatial/reference/LocalCS.cpp index 2f8fa58..0424e96 100644 --- a/src/entity/spatial/reference/LocalCS.cpp +++ b/src/entity/spatial/reference/LocalCS.cpp @@ -22,8 +22,8 @@ bool LocalCS::isEqual(const SpatialReference::Ptr other) const auto otherLocal = std::dynamic_pointer_cast(other); if(otherLocal) - { // check if origin is equal: - return parent_->isEqual(otherLocal->parent_) && transform_.isApprox(otherLocal->transform_); + { // 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 { From 4a25848b3084a983e6c2b8bcd47cdf756202560a Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 14 Sep 2018 15:37:22 +0200 Subject: [PATCH 07/64] SpatialIndex now for a specific root reference system. \n Multiple SpatialIndex for different reference systems possible --- include/sempr/processing/SpatialIndex.hpp | 25 ++-- src/processing/SpatialIndex.cpp | 157 ++++++++++++---------- src/query/SpatialIndexQuery.cpp | 4 +- test/main.cpp | 2 +- test/spatial_index_tests.cpp | 11 +- 5 files changed, 113 insertions(+), 86 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index f2291bb..27de617 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -36,6 +36,10 @@ 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. */ +/** + * Spatial Index for one specific root coordinate system. + * All entities that could not be tranformes to this coordinate system will be skipped! + */ class SpatialIndex : public Module< core::EntityEvent, core::EntityEvent, @@ -45,7 +49,7 @@ class SpatialIndex using Ptr = std::shared_ptr; std::string type() const override; - SpatialIndex(); + SpatialIndex(entity::SpatialReference::Ptr rootCS); /** Answer a SpatialIndexQuery @@ -58,25 +62,30 @@ class SpatialIndex 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? */ - typedef bg::model::point bPoint; - typedef bg::model::box bBox; - typedef std::pair bValue; + typedef bg::model::point bPoint; //ToDo rename it + typedef bg::model::box bBox; //ToDo rename it + typedef std::pair bValue; //ToDo rename it typedef bgi::rtree > RTree; private: /** - 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_; /** - 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; @@ -88,7 +97,7 @@ class SpatialIndex void removeGeo(entity::Geometry::Ptr geo); /** Create a pair of bounding-box and ptr */ - bValue createEntry(entity::Geometry::Ptr geo); + bValue createEntry(entity::Geometry::Ptr geo) const; //could throw an exception if there is no tranformation to the rootCS }; diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp index d862f00..7ae8055 100644 --- a/src/processing/SpatialIndex.cpp +++ b/src/processing/SpatialIndex.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -9,9 +10,9 @@ namespace sempr { namespace processing { -SpatialIndex::SpatialIndex() +SpatialIndex::SpatialIndex(entity::SpatialReference::Ptr rootCS) : + rootCS_(rootCS) { - // nothing to do } @@ -22,51 +23,49 @@ std::string SpatialIndex::type() const 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; + try + { + // create the AABB of the transformed query-volume. Create a transformed box. + auto searchEntry = createEntry(query->refGeo()); - 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; + bBox region = searchEntry.first; - default: - std::cout << "SpatialIndex: Mode " << query->mode() << " not implemented." << '\n'; + 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'; + } + + } + catch (const TransformException& ex) + { + //ref geom of the query is in a different cs. Not results. } std::transform( tmpResults.begin(), tmpResults.end(), @@ -130,12 +129,12 @@ void SpatialIndex::processChangedCS(entity::SpatialReference::Ptr cs) } -SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) +SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const { // get the 3d envelope of the geometry. - geos::geom::Coordinate min, max; - geo->findEnvelope(min, max); + geos::geom::Coordinate geoMin, geoMax; + geo->findEnvelope(geoMin, geoMax); // 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, @@ -146,34 +145,45 @@ SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) 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); + coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); + - geos::geom::MultiPoint* mp = geos::geom::GeometryFactory::getDefaultInstance()->createMultiPoint(cornerCoordinates); + entity::MultiPoint::Ptr mpEntity( new entity::MultiPoint() ); // Note: this wast IDs - recommended to use a factory in future - // transform the geometry - LocalTransformationFilter tf(geo->getCS()->transformationToRoot()); - mp->apply_rw(tf); + mpEntity->setCoordinates(cornerCoordinates); + mpEntity->setCS(geo->getCS()); + + // 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 + + /* // 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) + ); - // get the new envelope - EnvelopeFilter ef; - mp->apply_ro(ef); + */ - // destroy the multi point after there usage: - geos::geom::GeometryFactory::getDefaultInstance()->destroyGeometry(mp); + EnvelopeFilter ef; + mpEntity->getGeometry()->apply_ro(ef); // 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); } @@ -183,13 +193,20 @@ 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); + try + { + // 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); + } + catch (const TransformException& ex) + { + // this will skip the geometry if there is no transformation to the root cs of the spatial index + } // std::cout << "inserted " << geo->id() << " into spatial index. AABB: " // << "(" << entry.first.min_corner().get<0>() << ", " << diff --git a/src/query/SpatialIndexQuery.cpp b/src/query/SpatialIndexQuery.cpp index b3bac03..e59435d 100644 --- a/src/query/SpatialIndexQuery.cpp +++ b/src/query/SpatialIndexQuery.cpp @@ -34,8 +34,6 @@ void SpatialIndexQuery::setupRefGeo(const Eigen::Vector3d &lower, const Eigen::V { 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); @@ -49,6 +47,8 @@ void SpatialIndexQuery::setupRefGeo(const Eigen::Vector3d &lower, const Eigen::V corners->setCoordinates(cornerCoordinates); + corners->setCS(cs); + this->refGeo_ = corners; } diff --git a/test/main.cpp b/test/main.cpp index 5de1bd0..6d61fdd 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() ); + SpatialIndex::Ptr spatial( new SpatialIndex(std::make_shared()) ); sempr::core::IDGenerator::getInstance().setStrategy( std::unique_ptr( new sempr::core::IncrementalIDGeneration(storage) ) diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 5f09224..f70ee82 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -28,11 +28,12 @@ BOOST_AUTO_TEST_SUITE(spatial_index) { Core core; - SpatialIndex::Ptr index(new SpatialIndex()); + LocalCS::Ptr cs(new LocalCS()); + + SpatialIndex::Ptr index(new SpatialIndex(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); @@ -51,13 +52,13 @@ BOOST_AUTO_TEST_SUITE(spatial_index) BOOST_AUTO_TEST_CASE(spatial_index_complex) { Core core; - - SpatialIndex::Ptr index(new SpatialIndex()); - core.addModule(index); // add a spatial refernce LocalCS::Ptr cs(new LocalCS()); core.addEntity(cs); + + SpatialIndex::Ptr index(new SpatialIndex(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 From c9306ee805def4efd64749f7801a042298bc4411 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Sep 2018 09:52:35 +0200 Subject: [PATCH 08/64] First Version of SpatialConclusion --- include/sempr/processing/SpatialIndex.hpp | 12 +- include/sempr/query/SpatialIndexQuery.hpp | 7 +- src/processing/SpatialConclusion.cpp | 229 ++++++++++++++++++++++ 3 files changed, 242 insertions(+), 6 deletions(-) create mode 100644 src/processing/SpatialConclusion.cpp diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 27de617..85c7bd1 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -22,6 +22,9 @@ namespace sempr { namespace processing { namespace bg = boost::geometry; namespace bgi = boost::geometry::index; +template +class SpatialConclusion; + /** This class implements a spatial index for geometry-entities. It listens to events of Geometry @@ -62,12 +65,15 @@ class SpatialIndex 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? */ - typedef bg::model::point bPoint; //ToDo rename it - typedef bg::model::box bBox; //ToDo rename it - typedef std::pair bValue; //ToDo rename it + typedef bg::model::point bPoint; + typedef bg::model::box bBox; + typedef std::pair bValue; typedef bgi::rtree > RTree; private: + template + friend class SpatialConclusion; + /** * The actual R-Tree. */ diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index 66ce1ce..15d02a9 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -40,10 +40,11 @@ class SpatialIndexQuery : public Query, public core::OType { inside of sqlite3.h, which makes the preprocessor expand it here, and the compiler throwing an error pointing at NOT_WITHIN... */ + // negative constraints have to be odd! enum QueryType { - WITHIN = 0, NOTWITHIN, - CONTAINS, NOTCONTAINS, - INTERSECTS, NOTINTERSECTS + WITHIN = 0, NOTWITHIN, + CONTAINS = 2, NOTCONTAINS, + INTERSECTS = 4, NOTINTERSECTS }; diff --git a/src/processing/SpatialConclusion.cpp b/src/processing/SpatialConclusion.cpp new file mode 100644 index 0000000..4258663 --- /dev/null +++ b/src/processing/SpatialConclusion.cpp @@ -0,0 +1,229 @@ +#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()) { + casimportere 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); +}SpatialConclusion + +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); + } +} +*/ +}} From 2b4fcc596223f03ea6aad6119a0da86b644141e2 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Sep 2018 12:09:38 +0200 Subject: [PATCH 09/64] SpatialConclusion as SpatialIndex Extension --- .../sempr/processing/SpatialConclusion.hpp | 355 ++++++++++++++++++ include/sempr/processing/SpatialIndex.hpp | 4 +- 2 files changed, 357 insertions(+), 2 deletions(-) create mode 100644 include/sempr/processing/SpatialConclusion.hpp diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp new file mode 100644 index 0000000..8afcfdf --- /dev/null +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -0,0 +1,355 @@ +#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. + * + * @prefix geo: + * @prefix ogc: + * ogc:sfEquals + * ogc:sfDisjoint //b + * ogc:sfIntersects //b + * ogc:sfTouches //? + * ogc:sfWithin //b + * ogc:sfContains //b + * ogc:sfOverlaps //b + * ogc:sfCrosses //only for multipoint/polygon, multipoint/linestring, linestring/linestring, linestring/polygon, and linestring/multipolygon comparisons. + * + * @prefix spatial: + * spatial:north + * spatial:south + * spatial:west + * spatial:east + * + * Note: To fullfill the GeoSPARQL Entity the SpatialEntity have to be makred 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!) + * + * + * The SpatialEntity have to implement a geometry() method to get a geometry object pointer. + * + * The ODB header for the SpatialEntity (*_odb.h) have to be included before this module get included. + */ + + +template +class SpatialConclusion : public Module< core::EntityEvent, core::EntityEvent > +{ +public: + using Ptr = std::shared_ptr< SpatialConclusion >; + + // isGlobal is set if both geometries are transformed in the same global reference system + typedef std::function CheckBoxFunction; + typedef std::function CheckGeometryFunction; + + SpatialConclusion(const std::weak_ptr& spatialIndex) : + index_(spatialIndex) + { + globalRoot_ = false; + if (auto idx = index_.lock()) + { + auto globalTest = std::dynamic_pointer_cast(idx->rootCS_); + if(globalTest) + globalRoot_ = true; + } + + + 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 + { + // todo found and update the based enities + } + + void registerCheckFunction(const std::string& relationPredicate, const CheckBoxFunction& checker) + { + checkBoxFunctions_[relationPredicate] = checker; + } + + + +private: + std::weak_ptr index_; + + std::map spatialGeometry_; + + std::map rdfMap_; // temporary rdf vector for every entity (mapped by id) + + bool globalRoot_; + + std::map checkBoxFunctions_; + + + void removeEntity(const std::shared_ptr& entity) + { + rdfMap_[entity->id()]->removed(); // fire remove event + + removeGeometryLink(entity->id()); + + rdfMap_.erase(entity->id()); // remove rdf vector for the entity + removeBackRelation(entity->id()); // remove linked back relations ot his entity + } + + + void addEntity(const std::shared_ptr& entity, bool change = false) + { + if (indexedGeometry(entity->geometry())) + { + if (!change) + { // new Entity + rdfMap_[entity->id()] = entity::RDFVector::Ptr(new entity::RDFVector(true)); // not persistant Entity + rdfMap_[entity->id()]->created(); + } + else + { // changed Entity + rdfMap_[entity->id()]->clear(); + rdfMap_[entity->id()]->changed(); + } + + spatialGeometry_[entity->geometry()] = entity->id(); // build up geometry link + checkEntity(entity->id(), entity->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(); + } + } + + 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) + { + // 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(entity->id()); + removeBackRelation(entity->id()); + addEntity(entity, true); + } + + + void removeBackRelation(const std::string& id) + { + std::string objID = sempr::baseURI() + id; + + 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 == objID) + { + 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 selfBox = idx->geo2box_[self].first; + + std::set changedRDF; //set for all changed RDFVectors by this method + + // check every registered check function + for (auto checkIt = checkBoxFunctions_.begin(); checkIt != checkBoxFunctions_.end(); ++checkIt) + { + + for (auto other : idx->rtree_) + { + //check from self to others + bool selfRelated = checkIt->second(self, other.first, globalRoot_); + if (selfRelated) + { + // Build Triple: SelfId, Function predicate, OtherID + entity::Triple t(sempr::baseURI() + id, checkIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); + rdfMap_[id]->addTriple(t, true); + } + + //check from others to self + bool otherRelated = checkIt->second(other.first, self, globalRoot_); + if (otherRelated) + { + auto otherID = spatialGeometry_.at(other.second); + // Build Triple: OtherID, Function predicate, SelfId + entity::Triple t(sempr::baseURI() + otherID, checkIt->first, sempr::baseURI() + id); + rdfMap_[otherID]->addTriple(t, true); + changedRDF.insert(rdfMap_[otherID]); //mark vactor as changed + } + } + + } + + // notify changes of rdf vectors from others + for (auto rdfVector : changedRDF) + rdfVector->changed(); + } + + } + + void initDefaultChecker() + { + registerCheckFunction("spatial:north", checkNorthOf); + registerCheckFunction("spatial:south", checkSouthOf); + registerCheckFunction("spatial:east", checkEastOf); + registerCheckFunction("spatial:west", checkWestOf); + } + + + static bool check2D(const SpatialIndex::bBox& test) + { + double h = abs(test.min_corner().get<2>() - test.max_corner().get<2>()); + return h < 0.00001; // objects with a high less than 0.1mm + } + + static bool checkNorthOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + { + if (isGlobal) + { + return self.min_corner().get<0>() >= other.max_corner().get<0>(); // x coordinate is lat (only in WGS84) + } + else + { + return false; + } + } + + static bool checkSouthOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + { + if (isGlobal) + { + return self.max_corner().get<0>() <= other.min_corner().get<0>(); // x coordinate is lat (only in WGS84) + } + else + { + return false; + } + } + + static bool checkEastOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + { + if (isGlobal) + { + return self.min_corner().get<1>() >= other.max_corner().get<1>(); // y coordinate is lon (only in WGS84) + } + else + { + return false; + } + } + + static bool checkWestOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + { + if (isGlobal) + { + return self.max_corner().get<1>() <= other.min_corner().get<1>(); // y coordinate is lon (only in WGS84) + } + else + { + return false; + } + } + + + +}; + + +}} + +#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 85c7bd1..1d448df 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -61,11 +61,11 @@ class SpatialIndex /** Specify what is stored in the R-Tree: - boxes, made out of points, consisting of 3 floats, in cartesian space. + 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? */ - typedef bg::model::point bPoint; + typedef bg::model::point bPoint; typedef bg::model::box bBox; typedef std::pair bValue; typedef bgi::rtree > RTree; From 646620b13ca90223447806414b0b5ed2cedc40ab Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Sep 2018 14:12:04 +0200 Subject: [PATCH 10/64] R-Tree with cloned geometries --- include/sempr/processing/SpatialIndex.hpp | 5 ++++- src/processing/SpatialIndex.cpp | 26 ++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 1d448df..3ed9e23 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -67,7 +67,7 @@ class SpatialIndex */ typedef bg::model::point bPoint; typedef bg::model::box bBox; - typedef std::pair bValue; + typedef std::pair bValue; // pair af a bounding box and a translated geometry clone typedef bgi::rtree > RTree; private: @@ -104,6 +104,9 @@ class SpatialIndex /** Create a pair of bounding-box and ptr */ bValue createEntry(entity::Geometry::Ptr geo) const; //could throw an exception if there is no tranformation to the rootCS + entity::Geometry::Ptr findEntry(const bValue value) const; + + }; diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp index 7ae8055..f8f77a0 100644 --- a/src/processing/SpatialIndex.cpp +++ b/src/processing/SpatialIndex.cpp @@ -68,10 +68,11 @@ void SpatialIndex::process(query::SpatialIndexQuery::Ptr query) //ref geom of the query is in a different cs. Not results. } - std::transform( tmpResults.begin(), tmpResults.end(), - std::inserter(query->results, query->results.end()), - [](bValue tmp) { return tmp.second; } - ); + for (auto result : tmpResults) + { + query->results.insert(findEntry(result)); + } + } @@ -183,8 +184,12 @@ SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const bPoint(ef.getMin().x, ef.getMin().y, ef.getMin().z), bPoint(ef.getMax().x, ef.getMax().y, ef.getMax().z) ); + + // create a transformed copy of the geometry. + auto geom = geo->clone(); + geom->transformToCS(rootCS_); - return bValue(box, geo); + return bValue(box, geom); } @@ -242,4 +247,15 @@ void SpatialIndex::removeGeo(entity::Geometry::Ptr geo) } } +entity::Geometry::Ptr SpatialIndex::findEntry(const bValue value) const +{ + for (auto it = geo2box_.begin(); it != geo2box_.end(); ++it) + { + if (it->second.second == value.second) + return it->first; + } + + return nullptr; +} + }} From 5f837b5f6c60ba009dce75b141c393698ca77bf8 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Sep 2018 15:11:58 +0200 Subject: [PATCH 11/64] SpatialConclusion could also checks geometry and not only boxes --- .../sempr/processing/SpatialConclusion.hpp | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 8afcfdf..cfacb2e 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -121,6 +121,10 @@ class SpatialConclusion : public Module< core::EntityEvent, core: checkBoxFunctions_[relationPredicate] = checker; } + void registerCheckFunction(const std::string& relationPredicate, const CheckGeometryFunction& checker) + { + checkGeomFunctions_[relationPredicate] = checker; + } private: @@ -133,6 +137,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: bool globalRoot_; std::map checkBoxFunctions_; + std::map checkGeomFunctions_; void removeEntity(const std::shared_ptr& entity) @@ -246,28 +251,60 @@ class SpatialConclusion : public Module< core::EntityEvent, core: std::set changedRDF; //set for all changed RDFVectors by this method - // check every registered check function - for (auto checkIt = checkBoxFunctions_.begin(); checkIt != checkBoxFunctions_.end(); ++checkIt) + // check every registered box check function + for (auto checkBoxIt = checkBoxFunctions_.begin(); checkBoxIt != checkBoxFunctions_.end(); ++checkBoxIt) + { + + for (auto other : idx->rtree_) + { + //check from self to others + bool selfRelated = checkBoxIt->second(selfBox, other.first, globalRoot_); + if (selfRelated) + { + // Build Triple: SelfId, Function predicate, OtherID + entity::Triple t(sempr::baseURI() + id, checkBoxIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); + rdfMap_[id]->addTriple(t, true); + } + + //check from others to self + bool otherRelated = checkBoxIt->second(other.first, selfBox, globalRoot_); + if (otherRelated) + { + auto otherID = spatialGeometry_.at(other.second); + // Build Triple: OtherID, Function predicate, SelfId + entity::Triple t(sempr::baseURI() + otherID, checkBoxIt->first, sempr::baseURI() + id); + rdfMap_[otherID]->addTriple(t, true); + changedRDF.insert(rdfMap_[otherID]); //mark vactor as changed + } + } + + } + + + auto selfGeometry = idx->geo2box_[self].second; + + // check every registered geom check function + for (auto checkGeomIt = checkGeomFunctions_.begin(); checkGeomIt != checkGeomFunctions_.end(); ++checkGeomIt) { for (auto other : idx->rtree_) { //check from self to others - bool selfRelated = checkIt->second(self, other.first, globalRoot_); + bool selfRelated = checkGeomIt->second(selfGeometry, other.second, globalRoot_); if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t(sempr::baseURI() + id, checkIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); + entity::Triple t(sempr::baseURI() + id, checkGeomIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); rdfMap_[id]->addTriple(t, true); } //check from others to self - bool otherRelated = checkIt->second(other.first, self, globalRoot_); + bool otherRelated = checkGeomIt->second(other.second, selfGeometry, globalRoot_); if (otherRelated) { auto otherID = spatialGeometry_.at(other.second); // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t(sempr::baseURI() + otherID, checkIt->first, sempr::baseURI() + id); + entity::Triple t(sempr::baseURI() + otherID, checkGeomIt->first, sempr::baseURI() + id); rdfMap_[otherID]->addTriple(t, true); changedRDF.insert(rdfMap_[otherID]); //mark vactor as changed } From ce854706512e0d5b19d605dca61cda3fdfbf52ac Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Sep 2018 15:33:29 +0200 Subject: [PATCH 12/64] R-Tree only for query not iterating --- .../sempr/processing/SpatialConclusion.hpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index cfacb2e..0eea91b 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -255,22 +255,22 @@ class SpatialConclusion : public Module< core::EntityEvent, core: for (auto checkBoxIt = checkBoxFunctions_.begin(); checkBoxIt != checkBoxFunctions_.end(); ++checkBoxIt) { - for (auto other : idx->rtree_) + for (auto other : idx->geo2box_) { //check from self to others - bool selfRelated = checkBoxIt->second(selfBox, other.first, globalRoot_); + bool selfRelated = checkBoxIt->second(selfBox, other.second.first, globalRoot_); if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t(sempr::baseURI() + id, checkBoxIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); + entity::Triple t(sempr::baseURI() + id, checkBoxIt->first, sempr::baseURI() + spatialGeometry_.at(other.first)); rdfMap_[id]->addTriple(t, true); } //check from others to self - bool otherRelated = checkBoxIt->second(other.first, selfBox, globalRoot_); + bool otherRelated = checkBoxIt->second(other.second.first, selfBox, globalRoot_); if (otherRelated) { - auto otherID = spatialGeometry_.at(other.second); + auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId entity::Triple t(sempr::baseURI() + otherID, checkBoxIt->first, sempr::baseURI() + id); rdfMap_[otherID]->addTriple(t, true); @@ -287,22 +287,22 @@ class SpatialConclusion : public Module< core::EntityEvent, core: for (auto checkGeomIt = checkGeomFunctions_.begin(); checkGeomIt != checkGeomFunctions_.end(); ++checkGeomIt) { - for (auto other : idx->rtree_) + for (auto other : idx->geo2box_) { //check from self to others - bool selfRelated = checkGeomIt->second(selfGeometry, other.second, globalRoot_); + bool selfRelated = checkGeomIt->second(selfGeometry, other.second.second, globalRoot_); if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t(sempr::baseURI() + id, checkGeomIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); + entity::Triple t(sempr::baseURI() + id, checkGeomIt->first, sempr::baseURI() + spatialGeometry_.at(other.first)); rdfMap_[id]->addTriple(t, true); } //check from others to self - bool otherRelated = checkGeomIt->second(other.second, selfGeometry, globalRoot_); + bool otherRelated = checkGeomIt->second(other.second.second, selfGeometry, globalRoot_); if (otherRelated) { - auto otherID = spatialGeometry_.at(other.second); + auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId entity::Triple t(sempr::baseURI() + otherID, checkGeomIt->first, sempr::baseURI() + id); rdfMap_[otherID]->addTriple(t, true); From 183aeedc1f765002e331052d992677195210d323 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 18 Sep 2018 17:20:25 +0200 Subject: [PATCH 13/64] Fix 2D in SpatialIndex by assume a very high box --- include/sempr/processing/SpatialIndex.hpp | 2 ++ src/entity/spatial/filter/GeometryFilter.cpp | 2 +- src/processing/SpatialIndex.cpp | 18 ++++++++++++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 3ed9e23..415463b 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -70,6 +70,8 @@ class SpatialIndex typedef std::pair bValue; // pair af a bounding box and a translated geometry clone typedef bgi::rtree > RTree; + //const std::map& getGeoBoxes() const; + private: template friend class SpatialConclusion; 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/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp index f8f77a0..c405be3 100644 --- a/src/processing/SpatialIndex.cpp +++ b/src/processing/SpatialIndex.cpp @@ -132,11 +132,17 @@ void SpatialIndex::processChangedCS(entity::SpatialReference::Ptr cs) SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const { - - // get the 3d envelope of the geometry. + // get the 3D envelope of the geometry. geos::geom::Coordinate geoMin, geoMax; geo->findEnvelope(geoMin, geoMax); + // Fix for 2D. It will be a box with the max. possible height. + if (geoMin.z != geoMin.z) //NaN Check + geoMin.z = - std::numeric_limits::max(); // max double isnt valid for boost! + + if (geoMax.z != geoMax.z) //NaN Check + geoMax.z = + std::numeric_limits::max(); // max double isnt valid for boost! + // 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 @@ -155,7 +161,6 @@ SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); - entity::MultiPoint::Ptr mpEntity( new entity::MultiPoint() ); // Note: this wast IDs - recommended to use a factory in future mpEntity->setCoordinates(cornerCoordinates); @@ -246,7 +251,12 @@ void SpatialIndex::removeGeo(entity::Geometry::Ptr geo) geo2box_.erase(it); } } - +/* +const std::map& SpatialIndex::getGeoBoxes() const +{ + return geo2box_; +} +*/ entity::Geometry::Ptr SpatialIndex::findEntry(const bValue value) const { for (auto it = geo2box_.begin(); it != geo2box_.end(); ++it) From 7ed18376b1bf16fdb2d5cd259a8a4cd287d48993 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 18 Sep 2018 17:21:23 +0200 Subject: [PATCH 14/64] Add first tests for spatial conclusion --- test/CMakeLists.txt | 4 + test/spatial_conclusion_tests.cpp | 174 ++++++++++++++++++++++++++++++ test/spatial_index_tests.cpp | 2 + test/test_utils.hpp | 4 + 4 files changed, 184 insertions(+) create mode 100644 test/spatial_conclusion_tests.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ed61150..37e1cda 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/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp new file mode 100644 index 0000000..5b8a98c --- /dev/null +++ b/test/spatial_conclusion_tests.cpp @@ -0,0 +1,174 @@ +#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 getOsnaCoords() +{ + std::vector osnaCorner; + + osnaCorner.emplace_back(53.029056, 8.858612); + osnaCorner.emplace_back(52.302939, 8.034527); + osnaCorner.emplace_back(52.297650, 8.107368); + osnaCorner.emplace_back(52.245902, 8.087810); + osnaCorner.emplace_back(53.029056, 8.858612); + + 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 Nordpunkt + 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_cities) + { + Core core; + + GeodeticCS::Ptr globalCS(new GeodeticCS()); + + SpatialIndex::Ptr index(new SpatialIndex(globalCS)); + core.addModule(index); + + //SpatialConclusion::Ptr conclusion(new SpatialConclusion(index)); + //core.addModule(conclusion); + + //build up a quadrangle + Polygon::Ptr osna( new Polygon() ); + osna->setCoordinates(getOsnaCoords()); + osna->setCS(globalCS); + core.addEntity(osna); + + Polygon::Ptr bremen( new Polygon() ); + bremen->setCoordinates(getBremenCoords()); + bremen->setCS(globalCS); + core.addEntity(bremen); + + Polygon::Ptr nds( new Polygon() ); + nds->setCoordinates(getNDSCoords()); + nds->setCS(globalCS); + auto queryNDS = SpatialIndexQuery::withinBoxOf(nds); + + core.answerQuery(queryNDS); + BOOST_CHECK_EQUAL(queryNDS->results.size(), 2); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. + + /* + auto queryWithinBox = SpatialIndexQuery::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); + core.answerQuery(queryIntersecBox); + BOOST_CHECK_EQUAL(queryIntersecBox->results.size(), 1); + */ + + } + + 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 f70ee82..f2f906e 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -33,6 +33,8 @@ BOOST_AUTO_TEST_SUITE(spatial_index) SpatialIndex::Ptr index(new SpatialIndex(cs)); core.addModule(index); + + //build up a quadrangle MultiPoint::Ptr mp( new MultiPoint() ); mp->setGeometry(setupQuadrangle({{1, 1, 1}}, {{10, 10, 10}})); diff --git a/test/test_utils.hpp b/test/test_utils.hpp index 5c9bfc3..6a09a33 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -33,6 +33,9 @@ #include #include +#include +#include + // reference systems #include #include @@ -46,6 +49,7 @@ #include #include +#include using namespace sempr::core; using namespace sempr::storage; From 593402e2cb13571330ab0ebafb12e4d035febbce Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Thu, 20 Sep 2018 14:51:44 +0200 Subject: [PATCH 15/64] SpatialIndexQuery for 2D or 3D by template parameter and a box/geometry pair as reference --- .../sempr/processing/SpatialConclusion.hpp | 14 +- include/sempr/processing/SpatialIndex.hpp | 27 +-- include/sempr/query/SpatialIndexQuery.hpp | 211 +++++++++++++----- src/processing/SpatialIndex.cpp | 33 ++- src/query/SpatialIndexQuery.cpp | 131 ----------- test/main.cpp | 2 +- test/spatial_conclusion_tests.cpp | 6 +- test/spatial_index_tests.cpp | 8 +- 8 files changed, 204 insertions(+), 228 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 0eea91b..f1c6a37 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -70,7 +70,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: using Ptr = std::shared_ptr< SpatialConclusion >; // isGlobal is set if both geometries are transformed in the same global reference system - typedef std::function CheckBoxFunction; + typedef std::function CheckBoxFunction; typedef std::function CheckGeometryFunction; SpatialConclusion(const std::weak_ptr& spatialIndex) : @@ -179,6 +179,8 @@ class SpatialConclusion : public Module< core::EntityEvent, core: { return idx->geo2box_.find(geometry) != idx->geo2box_.end(); } + + return false; } bool removeGeometryLink(const std::string& id) @@ -328,13 +330,13 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } - static bool check2D(const SpatialIndex::bBox& test) + static bool check2D(const SpatialIndex::Box& test) { double h = abs(test.min_corner().get<2>() - test.max_corner().get<2>()); return h < 0.00001; // objects with a high less than 0.1mm } - static bool checkNorthOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + static bool checkNorthOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) { if (isGlobal) { @@ -346,7 +348,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkSouthOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + static bool checkSouthOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) { if (isGlobal) { @@ -358,7 +360,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkEastOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + static bool checkEastOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) { if (isGlobal) { @@ -370,7 +372,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkWestOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + static bool checkWestOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) { if (isGlobal) { diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 415463b..e72de5c 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -42,11 +42,14 @@ class SpatialConclusion; /** * 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. */ -class SpatialIndex - : public Module< core::EntityEvent, +//template< std::size_t Dim = 3 > +class SpatialIndex : + public Module< core::EntityEvent, core::EntityEvent, - query::SpatialIndexQuery > + query::SpatialIndexQuery<3> >, + SpatialIndexBase<3> { public: using Ptr = std::shared_ptr; @@ -57,18 +60,8 @@ class SpatialIndex /** Answer a SpatialIndexQuery */ - void process(query::SpatialIndexQuery::Ptr query) override; + void process(query::SpatialIndexQuery<3>::Ptr query) override; - /** - 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? - */ - typedef bg::model::point bPoint; - typedef bg::model::box bBox; - typedef std::pair bValue; // pair af a bounding box and a translated geometry clone - typedef bgi::rtree > RTree; //const std::map& getGeoBoxes() const; @@ -89,7 +82,7 @@ class SpatialIndex /** * A mapping of Geometry-->bValue for easier updates of the RTree */ - std::map geo2box_; + std::map geo2box_; /** @@ -105,8 +98,8 @@ class SpatialIndex void removeGeo(entity::Geometry::Ptr geo); /** Create a pair of bounding-box and ptr */ - bValue createEntry(entity::Geometry::Ptr geo) const; //could throw an exception if there is no tranformation to the rootCS - entity::Geometry::Ptr findEntry(const bValue value) const; + ValuePair createEntry(entity::Geometry::Ptr geo, bool query = false) const; //could throw an exception if there is no tranformation to the rootCS + entity::Geometry::Ptr findEntry(const ValuePair value) const; }; diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index 15d02a9..edb8380 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -3,10 +3,38 @@ #include #include +#include #include -#include #include +#include + +#include +#include +#include +#include + +// forward declaration of SpatialIndex // ToDo: Move this to a own Header File! +namespace sempr { namespace processing { + namespace bg = boost::geometry; + namespace bgi = boost::geometry::index; + + 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? + */ + 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; + }; +}} + namespace sempr { namespace query { @@ -23,12 +51,18 @@ 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 typename processing::SpatialIndexBase::ValuePair ValuePair; + typedef typename processing::SpatialIndexBase::Box Box; + typedef typename processing::SpatialIndexBase::Point Point; + typedef Eigen::Matrix EigenVector; + + using Ptr = std::shared_ptr< SpatialIndexQuery >; + ~SpatialIndexQuery() {} - std::string type() const override { return "SpatialIndexQuery"; } + std::string type() const override { return "SpatialIndexQuery" + std::to_string(dim) + "D"; } /** The set of geometries matching the criterium */ std::set results; @@ -45,76 +79,153 @@ class SpatialIndexQuery : public Query, public core::OType { WITHIN = 0, NOTWITHIN, CONTAINS = 2, NOTCONTAINS, INTERSECTS = 4, NOTINTERSECTS - }; + }; // ToDo rename it to SpatialQueryType and make a enum class out of it! + /** 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_; } /** returns the mode of the query: WITHIN, NOT_WITHIN, ... */ - QueryType mode() const; + QueryType mode() const { return qtype_; } + /** set the mode of the query */ - void mode(QueryType m); - /** inverts the mode: WITHIN <-> NOTWITHIN etc. */ - void invert(); + void mode(QueryType 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() + { + /* 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_ = (qtype_ % 2 == 0) ? QueryType(qtype_+1) : QueryType(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, SpatialIndexQuery::WITHIN); + } + + static SpatialIndexQuery::Ptr containsBoxOf(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createBoxQuery(geometry, SpatialIndexQuery::CONTAINS); + } + + static SpatialIndexQuery::Ptr intersectsBoxOf(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createBoxQuery(geometry, SpatialIndexQuery::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(); - */ - - - // TODO CONTAINS and INTERSECTS + static SpatialIndexQuery::Ptr withinBox(const EigenVector& lower, const EigenVector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialIndexQuery::WITHIN); + } + + static SpatialIndexQuery::Ptr containsBox( const EigenVector& lower, const EigenVector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialIndexQuery::CONTAINS); + } + + static SpatialIndexQuery::Ptr intersectsBox(const EigenVector& lower, const EigenVector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialIndexQuery::INTERSECTS); + } + + // TODO Disjoint and Overlaps + // TODO Query for a specific geometry relations not only Boxes! 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); - // 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); + // the actual type + QueryType qtype_; + + // the reference pair of bounding box and geometry + ValuePair refPair_; + // 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(); + SpatialIndexQuery() {} - // the actual type - QueryType qtype_; + void setupPair(const EigenVector& lower, const EigenVector& 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 EigenVector& min, const EigenVector& max) + { + return Box(toPoint(min), toPoint(max)); + } + + // helper: create query from geo or upper/lower and a type + static SpatialIndexQuery::Ptr createBoxQuery(entity::Geometry::Ptr geometry, QueryType type) + { + geos::geom::Coordinate min, max; + geometry->findEnvelope(min, max); + + auto lower = toVector(min); + auto upper = toVector(max); + + return createBoxQuery(lower, upper, geometry->getCS(), type); + } + + static SpatialIndexQuery::Ptr createBoxQuery(const EigenVector& min, const EigenVector& max, + entity::SpatialReference::Ptr cs, QueryType type) + { + if (!cs) return SpatialIndexQuery::Ptr(); + + SpatialIndexQuery::Ptr query(new SpatialIndexQuery()); + query->setupPair(min, max, nullptr, cs); + query->mode(type); + return query; + } + + static Point toPoint(const EigenVector& vec) + { + if (dim == 2) + { + return Point(vec.x(), vec.y()); + } + else if (dim == 3) + { + return Point(vec.x(), vec.y(), vec.z()); + } + return Point(); + } + + static EigenVector toVector(const geos::geom::Coordinate& coord) + { + EigenVector vec; + + if (dim >= 2) + { + vec.x() = coord.x; + vec.y() = coord.y; + } + if (dim >= 3) + { + vec.z() = coord.z; + } + + return vec; + } - // the reference geometry - entity::Geometry::Ptr refGeo_; }; + }} #endif /* end of include guard SEMPR_QUERY_SPATIALQUERY_HPP_ */ diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp index c405be3..1b0f1d9 100644 --- a/src/processing/SpatialIndex.cpp +++ b/src/processing/SpatialIndex.cpp @@ -21,18 +21,17 @@ std::string SpatialIndex::type() const return "SpatialIndex"; } -void SpatialIndex::process(query::SpatialIndexQuery::Ptr query) +void SpatialIndex::process(query::SpatialIndexQuery<3>::Ptr query) { - std::vector tmpResults; + std::vector tmpResults; try { - // create the AABB of the transformed query-volume. Create a transformed box. - auto searchEntry = createEntry(query->refGeo()); + // ToDo: Transform Box and Geom of the Pair into the root ref system. - bBox region = searchEntry.first; - - typedef query::SpatialIndexQuery::QueryType QueryType; + Box region = query->refBoxGeometryPair().first; + + typedef query::SpatialIndexQuery<3>::QueryType QueryType; switch (query->mode()) { case QueryType::WITHIN: @@ -130,18 +129,18 @@ void SpatialIndex::processChangedCS(entity::SpatialReference::Ptr cs) } -SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const +SpatialIndex::ValuePair SpatialIndex::createEntry(entity::Geometry::Ptr geo, bool query) const { // get the 3D envelope of the geometry. geos::geom::Coordinate geoMin, geoMax; geo->findEnvelope(geoMin, geoMax); - // Fix for 2D. It will be a box with the max. possible height. + // Fix for 2D. It will be a box with the max. possible height. // min -1 and max +1 for a query entry! if (geoMin.z != geoMin.z) //NaN Check - geoMin.z = - std::numeric_limits::max(); // max double isnt valid for boost! + geoMin.z = 0; // -std::numeric_limits::max(); // max double isnt valid for boost! if (geoMax.z != geoMax.z) //NaN Check - geoMax.z = + std::numeric_limits::max(); // max double isnt valid for boost! + geoMax.z = 0; // +std::numeric_limits::max(); // max double isnt valid for boost! // 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, @@ -185,16 +184,16 @@ SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const mpEntity->getGeometry()->apply_ro(ef); // 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) + Box box( + Point(ef.getMin().x, ef.getMin().y, ef.getMin().z), + Point(ef.getMax().x, ef.getMax().y, ef.getMax().z) ); // create a transformed copy of the geometry. auto geom = geo->clone(); geom->transformToCS(rootCS_); - return bValue(box, geom); + return ValuePair(box, geom); } @@ -207,7 +206,7 @@ void SpatialIndex::insertGeo(entity::Geometry::Ptr geo) try { // create a new entry - bValue entry = createEntry(geo); + ValuePair entry = createEntry(geo); // save it in our map geo2box_[geo] = entry; // and insert it into the RTree @@ -257,7 +256,7 @@ const std::map& SpatialIndex::getGeoBoxes() const return geo2box_; } */ -entity::Geometry::Ptr SpatialIndex::findEntry(const bValue value) const +entity::Geometry::Ptr SpatialIndex::findEntry(const ValuePair value) const { for (auto it = geo2box_.begin(); it != geo2box_.end(); ++it) { diff --git a/src/query/SpatialIndexQuery.cpp b/src/query/SpatialIndexQuery.cpp index e59435d..ac12860 100644 --- a/src/query/SpatialIndexQuery.cpp +++ b/src/query/SpatialIndexQuery.cpp @@ -1,136 +1,5 @@ #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()); - - 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); - - corners->setCS(cs); - - 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/test/main.cpp b/test/main.cpp index 6d61fdd..c021026 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -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 = SpatialIndexQuery<3>::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 index 5b8a98c..6b6b9c1 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -62,7 +62,7 @@ std::vector getNDSCoords() 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 Nordpunkt + 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); @@ -149,7 +149,9 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) Polygon::Ptr nds( new Polygon() ); nds->setCoordinates(getNDSCoords()); nds->setCS(globalCS); - auto queryNDS = SpatialIndexQuery::withinBoxOf(nds); + //auto queryNDS = SpatialIndexQuery<3>::containsBoxOf(nds); + //auto queryNDS = SpatialIndexQuery<3>::withinBoxOf(nds); + auto queryNDS = SpatialIndexQuery<3>::intersectsBoxOf(nds); core.answerQuery(queryNDS); BOOST_CHECK_EQUAL(queryNDS->results.size(), 2); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index f2f906e..14bde31 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -41,11 +41,11 @@ BOOST_AUTO_TEST_SUITE(spatial_index) mp->setCS(cs); core.addEntity(mp); - auto queryWithinBox = SpatialIndexQuery::withinBox(Eigen::Vector3d{0, 0, 0}, Eigen::Vector3d{10, 10 ,10}, cs); + auto queryWithinBox = SpatialIndexQuery<3>::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 = SpatialIndexQuery<3>::intersectsBox(Eigen::Vector3d{1, 1, 1}, Eigen::Vector3d{2, 2 ,2}, cs); core.answerQuery(queryIntersecBox); BOOST_CHECK_EQUAL(queryIntersecBox->results.size(), 1); @@ -102,7 +102,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()); @@ -113,7 +113,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) // intersects query->results.clear(); - query->mode(SpatialIndexQuery::INTERSECTS); + query->mode(SpatialIndexQuery<3>::INTERSECTS); core.answerQuery(query); BOOST_CHECK_EQUAL(query->results.size(), expected_intersects.size()); From 796867f7e014baf3ac6da7960f7b93c5c668a81a Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 21 Sep 2018 10:04:37 +0200 Subject: [PATCH 16/64] SpatialIndex separate for 2D and 3D --- .../sempr/processing/SpatialConclusion.hpp | 16 +- include/sempr/processing/SpatialIndex.hpp | 295 +++++++++++++++++- include/sempr/query/SpatialIndexQuery.hpp | 5 +- src/processing/SpatialIndex.cpp | 257 --------------- test/main.cpp | 4 +- test/spatial_conclusion_tests.cpp | 7 +- test/spatial_index_tests.cpp | 4 +- 7 files changed, 298 insertions(+), 290 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index f1c6a37..4ec3f33 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -70,10 +70,10 @@ class SpatialConclusion : public Module< core::EntityEvent, core: using Ptr = std::shared_ptr< SpatialConclusion >; // isGlobal is set if both geometries are transformed in the same global reference system - typedef std::function CheckBoxFunction; + typedef std::function::Box& self,const SpatialIndex<3>::Box& other, bool isGlobal)> CheckBoxFunction; typedef std::function CheckGeometryFunction; - SpatialConclusion(const std::weak_ptr& spatialIndex) : + SpatialConclusion(const std::weak_ptr< SpatialIndex<3> >& spatialIndex) : index_(spatialIndex) { globalRoot_ = false; @@ -128,7 +128,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: private: - std::weak_ptr index_; + std::weak_ptr< SpatialIndex<3> > index_; std::map spatialGeometry_; @@ -330,13 +330,13 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } - static bool check2D(const SpatialIndex::Box& test) + static bool check2D(const SpatialIndex<3>::Box& test) { double h = abs(test.min_corner().get<2>() - test.max_corner().get<2>()); return h < 0.00001; // objects with a high less than 0.1mm } - static bool checkNorthOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) + static bool checkNorthOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) { if (isGlobal) { @@ -348,7 +348,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkSouthOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) + static bool checkSouthOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) { if (isGlobal) { @@ -360,7 +360,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkEastOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) + static bool checkEastOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) { if (isGlobal) { @@ -372,7 +372,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkWestOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) + static bool checkWestOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) { if (isGlobal) { diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index e72de5c..6d93934 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -3,6 +3,9 @@ #include #include +#include + +#include #include #include @@ -16,6 +19,7 @@ #include #include +#include namespace sempr { namespace processing { @@ -44,26 +48,84 @@ class SpatialConclusion; * 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 > +template< std::size_t dim = 3 > class SpatialIndex : public Module< core::EntityEvent, core::EntityEvent, - query::SpatialIndexQuery<3> >, - SpatialIndexBase<3> + 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; + + using Ptr = std::shared_ptr< SpatialIndex >; + std::string type() const override { return "SpatialIndex" + std::to_string(dim) + "D"; } - SpatialIndex(entity::SpatialReference::Ptr rootCS); + SpatialIndex(entity::SpatialReference::Ptr rootCS) : + rootCS_(rootCS) + {} /** - Answer a SpatialIndexQuery + Answer a SpatialIndexQuery */ - void process(query::SpatialIndexQuery<3>::Ptr query) override; + void process(std::shared_ptr< query::SpatialIndexQuery > query) override + { + std::vector tmpResults; + + try + { + // ToDo: Transform Box and Geom of the Pair into the root ref system. + + Box region = query->refBoxGeometryPair().first; + + typedef query::SpatialIndexQuery<3>::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'; + } + + } + 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; + } + + + const std::map& getGeoBoxes() const { return geo2box_; } private: template @@ -88,22 +150,221 @@ class SpatialIndex : /** * 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; + + 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! -- skips 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 */ - ValuePair createEntry(entity::Geometry::Ptr geo, bool query = false) const; //could throw an exception if there is no tranformation to the rootCS - entity::Geometry::Ptr findEntry(const ValuePair value) const; + 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); +/* + 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! + + std::vector cornerCoordinates; + + if (dim == 2) + { + geos::geom::Coordinate coord; + coord = geos::geom::Coordinate(geoMin.x, geoMin.y); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMax.y); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMin.y); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMax.y); cornerCoordinates.push_back(coord); + } + + if (dim == 3) + { + geos::geom::Coordinate coord; + coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); + } + + entity::MultiPoint::Ptr mpEntity( new entity::MultiPoint() ); // Note: this wast IDs - recommended to use a factory in future + + mpEntity->setCoordinates(cornerCoordinates); + mpEntity->setCS(geo->getCS()); + + // 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 + + /* // 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; + mpEntity->getGeometry()->apply_ro(ef); + + // create the bBox out of bPoints. + Box box; + + if (dim == 2) + { + box = Box( + Point(ef.getMin().x, ef.getMin().y), + Point(ef.getMax().x, ef.getMax().y) + ); + } + if (dim == 3) + { + box = Box( + Point(ef.getMin().x, ef.getMin().y, ef.getMin().z), + Point(ef.getMax().x, ef.getMax().y, ef.getMax().z) + ); + } + + // 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; + } }; +typedef SpatialIndex<2> SpatialIndex2D; +typedef SpatialIndex<3> SpatialIndex3D; }} diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index edb8380..c4a001a 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -54,9 +54,10 @@ namespace sempr { namespace query { template class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery > { public: - typedef typename processing::SpatialIndexBase::ValuePair ValuePair; + typedef typename processing::SpatialIndexBase::Box Box; typedef typename processing::SpatialIndexBase::Point Point; + typedef typename processing::SpatialIndexBase::ValuePair ValuePair; typedef Eigen::Matrix EigenVector; using Ptr = std::shared_ptr< SpatialIndexQuery >; @@ -225,6 +226,8 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery SpatialIndexQuery2D; +typedef SpatialIndexQuery<3> SpatialIndexQuery3D; }} diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp index 1b0f1d9..3342364 100644 --- a/src/processing/SpatialIndex.cpp +++ b/src/processing/SpatialIndex.cpp @@ -6,265 +6,8 @@ #include -//#include namespace sempr { namespace processing { -SpatialIndex::SpatialIndex(entity::SpatialReference::Ptr rootCS) : - rootCS_(rootCS) -{ -} - - -std::string SpatialIndex::type() const -{ - return "SpatialIndex"; -} - -void SpatialIndex::process(query::SpatialIndexQuery<3>::Ptr query) -{ - std::vector tmpResults; - - try - { - // ToDo: Transform Box and Geom of the Pair into the root ref system. - - Box region = query->refBoxGeometryPair().first; - - typedef query::SpatialIndexQuery<3>::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'; - } - - } - 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)); - } - -} - - -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::ValuePair SpatialIndex::createEntry(entity::Geometry::Ptr geo, bool query) const -{ - // get the 3D envelope of the geometry. - geos::geom::Coordinate geoMin, geoMax; - geo->findEnvelope(geoMin, geoMax); - - // Fix for 2D. It will be a box with the max. possible height. // min -1 and max +1 for a query entry! - if (geoMin.z != geoMin.z) //NaN Check - geoMin.z = 0; // -std::numeric_limits::max(); // max double isnt valid for boost! - - if (geoMax.z != geoMax.z) //NaN Check - geoMax.z = 0; // +std::numeric_limits::max(); // max double isnt valid for boost! - - // 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(geoMin.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); - - entity::MultiPoint::Ptr mpEntity( new entity::MultiPoint() ); // Note: this wast IDs - recommended to use a factory in future - - mpEntity->setCoordinates(cornerCoordinates); - mpEntity->setCS(geo->getCS()); - - // 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 - - /* // 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; - mpEntity->getGeometry()->apply_ro(ef); - - // create the bBox out of bPoints. - Box box( - Point(ef.getMin().x, ef.getMin().y, ef.getMin().z), - Point(ef.getMax().x, ef.getMax().y, ef.getMax().z) - ); - - // create a transformed copy of the geometry. - auto geom = geo->clone(); - geom->transformToCS(rootCS_); - - return ValuePair(box, geom); -} - - -void SpatialIndex::insertGeo(entity::Geometry::Ptr geo) -{ - // sanity: it needs a geometry, and a spatial reference. - if (!geo->getGeometry() || !geo->getCS()) 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 - } - - // 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); - } -} -/* -const std::map& SpatialIndex::getGeoBoxes() const -{ - return geo2box_; -} -*/ -entity::Geometry::Ptr SpatialIndex::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; -} }} diff --git a/test/main.cpp b/test/main.cpp index c021026..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(std::make_shared()) ); + 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<3>::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 index 6b6b9c1..5a8520d 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -129,7 +129,7 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) GeodeticCS::Ptr globalCS(new GeodeticCS()); - SpatialIndex::Ptr index(new SpatialIndex(globalCS)); + SpatialIndex2D::Ptr index(new SpatialIndex2D(globalCS)); core.addModule(index); //SpatialConclusion::Ptr conclusion(new SpatialConclusion(index)); @@ -151,11 +151,12 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) nds->setCS(globalCS); //auto queryNDS = SpatialIndexQuery<3>::containsBoxOf(nds); //auto queryNDS = SpatialIndexQuery<3>::withinBoxOf(nds); - auto queryNDS = SpatialIndexQuery<3>::intersectsBoxOf(nds); + /* + auto queryNDS = SpatialIndexQuery2D::intersectsBoxOf(nds); core.answerQuery(queryNDS); BOOST_CHECK_EQUAL(queryNDS->results.size(), 2); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. - + */ /* auto queryWithinBox = SpatialIndexQuery::withinBox(Eigen::Vector3d{0, 0, 0}, Eigen::Vector3d{10, 10 ,10}, cs); core.answerQuery(queryWithinBox); diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 14bde31..056313d 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -30,7 +30,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) LocalCS::Ptr cs(new LocalCS()); - SpatialIndex::Ptr index(new SpatialIndex(cs)); + SpatialIndex<3>::Ptr index(new SpatialIndex<3>(cs)); core.addModule(index); @@ -59,7 +59,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) LocalCS::Ptr cs(new LocalCS()); core.addEntity(cs); - SpatialIndex::Ptr index(new SpatialIndex(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: From 17b7f3b75451feb5d4d36f8e8856c9e1fea0a80e Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 21 Sep 2018 11:15:32 +0200 Subject: [PATCH 17/64] Fix static assert from eigen in SpatialIndexQuery2D --- include/sempr/query/SpatialIndexQuery.hpp | 11 +++-- test/spatial_conclusion_tests.cpp | 56 ++++++++++++++++------- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index c4a001a..1e02aac 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -59,6 +59,7 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery::Point Point; typedef typename processing::SpatialIndexBase::ValuePair ValuePair; typedef Eigen::Matrix EigenVector; + using Ptr = std::shared_ptr< SpatialIndexQuery >; ~SpatialIndexQuery() {} @@ -198,11 +199,11 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery= 2) { - vec.x() = coord.x; - vec.y() = coord.y; + vec[0] = coord.x; + vec[1] = coord.y; } if (dim >= 3) { - vec.z() = coord.z; + vec[3] = coord.z; } return vec; diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 5a8520d..8b593af 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -20,6 +20,20 @@ geom::MultiPoint* setupQuadrangle(const std::array& min, const std::ar 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; @@ -28,7 +42,7 @@ std::vector getOsnaCoords() osnaCorner.emplace_back(52.302939, 8.034527); osnaCorner.emplace_back(52.297650, 8.107368); osnaCorner.emplace_back(52.245902, 8.087810); - osnaCorner.emplace_back(53.029056, 8.858612); + osnaCorner.emplace_back(53.029056, 8.858612); //close the ring return osnaCorner; } @@ -136,27 +150,37 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) //core.addModule(conclusion); //build up a quadrangle - Polygon::Ptr osna( new Polygon() ); - osna->setCoordinates(getOsnaCoords()); - osna->setCS(globalCS); - core.addEntity(osna); - - Polygon::Ptr bremen( new Polygon() ); - bremen->setCoordinates(getBremenCoords()); - bremen->setCS(globalCS); - core.addEntity(bremen); + { + Polygon::Ptr osna( new Polygon() ); + osna->setCoordinates(getOsnaCoords()); + osna->setCS(globalCS); + core.addEntity(osna); + } + + { + Polygon::Ptr bremen( new Polygon() ); + bremen->setCoordinates(getBremenCoords()); + bremen->setCS(globalCS); + core.addEntity(bremen); + } + + { + Polygon::Ptr vechta( new Polygon() ); + vechta->setCoordinates(getVechtaCoords()); + vechta->setCS(globalCS); + core.addEntity(vechta); + } Polygon::Ptr nds( new Polygon() ); nds->setCoordinates(getNDSCoords()); nds->setCS(globalCS); - //auto queryNDS = SpatialIndexQuery<3>::containsBoxOf(nds); - //auto queryNDS = SpatialIndexQuery<3>::withinBoxOf(nds); - /* - auto queryNDS = SpatialIndexQuery2D::intersectsBoxOf(nds); + //auto queryNDS = SpatialIndexQuery2D::containsBoxOf(nds); + auto queryNDS = SpatialIndexQuery2D::withinBoxOf(nds); + //auto queryNDS = SpatialIndexQuery2D::intersectsBoxOf(nds); core.answerQuery(queryNDS); - BOOST_CHECK_EQUAL(queryNDS->results.size(), 2); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. - */ + BOOST_CHECK_EQUAL(queryNDS->results.size(), 3); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. + /* auto queryWithinBox = SpatialIndexQuery::withinBox(Eigen::Vector3d{0, 0, 0}, Eigen::Vector3d{10, 10 ,10}, cs); core.answerQuery(queryWithinBox); From a6b2f9845715421caad5772cfc22ea6e349cc8b3 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 21 Sep 2018 15:17:00 +0200 Subject: [PATCH 18/64] Move type mapping and QueryType to SpatialIndexBase --- include/sempr/processing/SpatialIndex.hpp | 152 +++++++++--------- include/sempr/processing/SpatialIndexBase.hpp | 145 +++++++++++++++++ include/sempr/query/SpatialIndexQuery.hpp | 124 ++++---------- test/spatial_index_tests.cpp | 2 +- 4 files changed, 254 insertions(+), 169 deletions(-) create mode 100644 include/sempr/processing/SpatialIndexBase.hpp diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 6d93934..359c00f 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -14,6 +14,8 @@ #include +#include + #include // required for EntityEvent #include // required for EntityEvent @@ -77,38 +79,39 @@ class SpatialIndex : try { - // ToDo: Transform Box and Geom of the Pair into the root ref system. + // Transform Box and Geom of the Pair into the root ref system of the index. + auto transformedPair = transformToRoot(query->refBoxGeometryPair(), query->refCS()); - Box region = query->refBoxGeometryPair().first; + Box region = transformedPair.first; - typedef query::SpatialIndexQuery<3>::QueryType QueryType; + //typedef query::SpatialIndexQuery<3>::QueryType QueryType; switch (query->mode()) { - case QueryType::WITHIN: + case SpatialQueryType::WITHIN: rtree_.query(bgi::within(region), std::back_inserter(tmpResults)); break; - case QueryType::NOTWITHIN: + case SpatialQueryType::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: + case SpatialQueryType::CONTAINS: rtree_.query(bgi::contains(region), std::back_inserter(tmpResults)); break; - case QueryType::NOTCONTAINS: + case SpatialQueryType::NOTCONTAINS: rtree_.query(!bgi::contains(region), std::back_inserter(tmpResults)); break; - case QueryType::INTERSECTS: + case SpatialQueryType::INTERSECTS: rtree_.query(bgi::intersects(region), std::back_inserter(tmpResults)); break; - case QueryType::NOTINTERSECTS: + case SpatialQueryType::NOTINTERSECTS: rtree_.query(!bgi::intersects(region), std::back_inserter(tmpResults)); break; default: - std::cout << "SpatialIndex: Mode " << query->mode() << " not implemented." << '\n'; + std::cout << "SpatialIndex: Mode " << int(query->mode()) << " not implemented." << '\n'; } } @@ -124,7 +127,6 @@ class SpatialIndex : } - const std::map& getGeoBoxes() const { return geo2box_; } private: @@ -214,7 +216,7 @@ class SpatialIndex : // skip geometry with a wrong coordinate dimension if (geo->getGeometry()->getCoordinateDimension() != dim) { - std::cout << "2D Geometry in 3D Index! -- skips this!" << std::endl; + std::cout << "2D Geometry in 3D Index! -- skip this!" << std::endl; return; } @@ -264,10 +266,57 @@ class SpatialIndex : // 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; + } + + 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 @@ -276,40 +325,23 @@ class SpatialIndex : // 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! - - std::vector cornerCoordinates; - - if (dim == 2) - { - geos::geom::Coordinate coord; - coord = geos::geom::Coordinate(geoMin.x, geoMin.y); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMax.y); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMin.y); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMax.y); cornerCoordinates.push_back(coord); - } - - if (dim == 3) - { - geos::geom::Coordinate coord; - coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); - } 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(geo->getCS()); + 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 - /* // really strange happenings - this is different to the manually apply of the env filter!!! + 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); @@ -322,43 +354,11 @@ class SpatialIndex : */ EnvelopeFilter ef; - mpEntity->getGeometry()->apply_ro(ef); - - // create the bBox out of bPoints. - Box box; - - if (dim == 2) - { - box = Box( - Point(ef.getMin().x, ef.getMin().y), - Point(ef.getMax().x, ef.getMax().y) - ); - } - if (dim == 3) - { - box = Box( - Point(ef.getMin().x, ef.getMin().y, ef.getMin().z), - Point(ef.getMax().x, ef.getMax().y, ef.getMax().z) - ); - } + geomBox->getGeometry()->apply_ro(ef); - // 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; + auto min = this->toPoint(ef.getMin()); + auto max = this->toPoint(ef.getMax()); + return Box(min, max); } }; diff --git a/include/sempr/processing/SpatialIndexBase.hpp b/include/sempr/processing/SpatialIndexBase.hpp new file mode 100644 index 0000000..82e2ff9 --- /dev/null +++ b/include/sempr/processing/SpatialIndexBase.hpp @@ -0,0 +1,145 @@ +#ifndef SEMPR_PROCESSING_SPATIALINDEXBASE_HPP_ +#define SEMPR_PROCESSING_SPATIALINDEXBASE_HPP_ + +#include + +#include + +#include +#include +#include +#include + +#include // required for EntityEvent +#include // required for EntityEvent + + +namespace sempr { namespace processing { + +namespace bg = boost::geometry; +namespace bgi = boost::geometry::index; + +/* + 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... +*/ +// negative constraints have to be odd! +enum class SpatialQueryType { + WITHIN = 0, NOTWITHIN, + CONTAINS = 2, NOTCONTAINS, + INTERSECTS = 4, NOTINTERSECTS +}; + +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 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 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 1e02aac..0a8947c 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -2,6 +2,7 @@ #define SEMPR_QUERY_SPATIALQUERY_HPP_ #include +#include #include #include #include @@ -14,27 +15,6 @@ #include #include -// forward declaration of SpatialIndex // ToDo: Move this to a own Header File! -namespace sempr { namespace processing { - namespace bg = boost::geometry; - namespace bgi = boost::geometry::index; - - 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? - */ - 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; - }; -}} - namespace sempr { namespace query { @@ -54,12 +34,12 @@ namespace sempr { namespace query { template class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery > { public: - - typedef typename processing::SpatialIndexBase::Box Box; - typedef typename processing::SpatialIndexBase::Point Point; - typedef typename processing::SpatialIndexBase::ValuePair ValuePair; - typedef Eigen::Matrix EigenVector; - + 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; using Ptr = std::shared_ptr< SpatialIndexQuery >; ~SpatialIndexQuery() {} @@ -69,28 +49,16 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery 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... - */ - // negative constraints have to be odd! - enum QueryType { - WITHIN = 0, NOTWITHIN, - CONTAINS = 2, NOTCONTAINS, - INTERSECTS = 4, NOTINTERSECTS - }; // ToDo rename it to SpatialQueryType and make a enum class out of it! - /** 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 { return qtype_; } + SpatialQueryType mode() const { return qtype_; } /** set the mode of the query */ - void mode(QueryType m) { qtype_ = m; } + void mode(SpatialQueryType m) { qtype_ = m; } /** inverts the mode: WITHIN <-> NOTWITHIN etc. */ void invert() @@ -99,7 +67,7 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQueryreferenceCS_ = cs; } - Box createBox(const EigenVector& min, const EigenVector& max) + Box createBox(const Vector& min, const Vector& max) { - return Box(toPoint(min), toPoint(max)); + auto minP = SpatialIndexBase::toPoint(min); + auto maxP = SpatialIndexBase::toPoint(max); + return Box(minP, maxP); } // helper: create query from geo or upper/lower and a type - static SpatialIndexQuery::Ptr createBoxQuery(entity::Geometry::Ptr geometry, QueryType type) + static SpatialIndexQuery::Ptr createBoxQuery(entity::Geometry::Ptr geometry, SpatialQueryType type) { geos::geom::Coordinate min, max; geometry->findEnvelope(min, max); - auto lower = toVector(min); - auto upper = toVector(max); + auto lower = SpatialIndexBase::toEigen(min); + auto upper = SpatialIndexBase::toEigen(max); return createBoxQuery(lower, upper, geometry->getCS(), type); } - static SpatialIndexQuery::Ptr createBoxQuery(const EigenVector& min, const EigenVector& max, - entity::SpatialReference::Ptr cs, QueryType type) + static SpatialIndexQuery::Ptr createBoxQuery(const Vector& min, const Vector& max, + entity::SpatialReference::Ptr cs, SpatialQueryType type) { if (!cs) return SpatialIndexQuery::Ptr(); @@ -195,36 +165,6 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery= 2) - { - vec[0] = coord.x; - vec[1] = coord.y; - } - if (dim >= 3) - { - vec[3] = coord.z; - } - - return vec; - } - }; typedef SpatialIndexQuery<2> SpatialIndexQuery2D; diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 056313d..070d29b 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -113,7 +113,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) // intersects query->results.clear(); - query->mode(SpatialIndexQuery<3>::INTERSECTS); + query->mode(SpatialQueryType::INTERSECTS); core.answerQuery(query); BOOST_CHECK_EQUAL(query->results.size(), expected_intersects.size()); From 9619bd6304860630d96119f82205797316eb4f17 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 21 Sep 2018 15:56:16 +0200 Subject: [PATCH 19/64] Add support for Nearest, CoveredBy and Overlaps spatial queries. --- include/sempr/processing/SpatialIndex.hpp | 24 ++++++++++--- include/sempr/processing/SpatialIndexBase.hpp | 18 +++++----- include/sempr/query/SpatialIndexQuery.hpp | 34 +++++++++++++++++-- 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 359c00f..d589263 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -84,13 +84,15 @@ class SpatialIndex : Box region = transformedPair.first; - //typedef query::SpatialIndexQuery<3>::QueryType QueryType; 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::NOTWITHIN: + case SpatialQueryType::NOT_WITHIN: rtree_.query(!bgi::within(region), std::back_inserter(tmpResults)); break; @@ -99,16 +101,30 @@ class SpatialIndex : case SpatialQueryType::CONTAINS: rtree_.query(bgi::contains(region), std::back_inserter(tmpResults)); break; - case SpatialQueryType::NOTCONTAINS: + 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::NOTINTERSECTS: + 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." << '\n'; diff --git a/include/sempr/processing/SpatialIndexBase.hpp b/include/sempr/processing/SpatialIndexBase.hpp index 82e2ff9..efe521f 100644 --- a/include/sempr/processing/SpatialIndexBase.hpp +++ b/include/sempr/processing/SpatialIndexBase.hpp @@ -13,24 +13,22 @@ #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; -/* - 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... -*/ // negative constraints have to be odd! enum class SpatialQueryType { - WITHIN = 0, NOTWITHIN, - CONTAINS = 2, NOTCONTAINS, - INTERSECTS = 4, NOTINTERSECTS + NEAREST, + WITHIN = 2, NOT_WITHIN, + CONTAINS = 4, NOT_CONTAINS, + INTERSECTS = 6, NOT_INTERSECTS, + COVERED = 8, NOT_COVERED, + OVERLAPS = 10, NOT_OVERLAPS }; template diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index 0a8947c..521b11a 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -63,6 +63,7 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery 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). @@ -89,6 +90,21 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery Date: Fri, 21 Sep 2018 17:10:24 +0200 Subject: [PATCH 20/64] Allow Geometry based spatial queries (not Box based) for intersections, contains and within conditions. --- include/sempr/processing/SpatialIndex.hpp | 75 ++++++++++++++++++++++- include/sempr/query/SpatialIndexQuery.hpp | 37 +++++++++++ test/spatial_conclusion_tests.cpp | 10 --- test/spatial_index_tests.cpp | 71 ++++++++++++++++++++- 4 files changed, 178 insertions(+), 15 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index d589263..08f36c1 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -84,7 +84,8 @@ class SpatialIndex : Box region = transformedPair.first; - switch (query->mode()) { + 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; @@ -127,9 +128,13 @@ class SpatialIndex : break; default: - std::cout << "SpatialIndex: Mode " << int(query->mode()) << " not implemented." << '\n'; + 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) { @@ -307,6 +312,72 @@ class SpatialIndex : 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(); ++resultIt) + if (!ref.second->getGeometry()->within(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + + break; + } + case SpatialQueryType::NOT_WITHIN: + { + for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + if (ref.second->getGeometry()->within(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + + break; + } + + case SpatialQueryType::CONTAINS: + { + for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + if (!ref.second->getGeometry()->contains(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + + break; + } + case SpatialQueryType::NOT_CONTAINS: + { + for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + if (ref.second->getGeometry()->contains(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + + break; + } + + case SpatialQueryType::INTERSECTS: + { + for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + if (!ref.second->getGeometry()->intersects(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + + break; + } + case SpatialQueryType::NOT_INTERSECTS: + { + for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + if (ref.second->getGeometry()->intersects(resultIt->second->getGeometry())) + resultIt = results.erase(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; diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index 521b11a..1d7d87f 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -106,6 +106,26 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuerygetCS(); + 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 createBoxQuery(entity::Geometry::Ptr geometry, SpatialQueryType type) { diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 8b593af..6164e4e 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -180,16 +180,6 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) core.answerQuery(queryNDS); BOOST_CHECK_EQUAL(queryNDS->results.size(), 3); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. - - /* - auto queryWithinBox = SpatialIndexQuery::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); - core.answerQuery(queryIntersecBox); - BOOST_CHECK_EQUAL(queryIntersecBox->results.size(), 1); - */ } diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 070d29b..9f38989 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -30,7 +30,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) LocalCS::Ptr cs(new LocalCS()); - SpatialIndex<3>::Ptr index(new SpatialIndex<3>(cs)); + SpatialIndex3D::Ptr index(new SpatialIndex<3>(cs)); core.addModule(index); @@ -41,16 +41,81 @@ BOOST_AUTO_TEST_SUITE(spatial_index) mp->setCS(cs); core.addEntity(mp); - auto queryWithinBox = SpatialIndexQuery<3>::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<3>::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); } + /* + 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; + + 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(1, 10); coords.push_back(coord); + coord = geos::geom::Coordinate(5, 13); coords.push_back(coord); + coord = geos::geom::Coordinate(4, 8); coords.push_back(coord); + coord = geos::geom::Coordinate(5, 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(3.5, 8); coords.push_back(coord); + coord = geos::geom::Coordinate(7, 14); coords.push_back(coord); + coord = geos::geom::Coordinate(7, 0); coords.push_back(coord); + coord = geos::geom::Coordinate(3.5, 8); 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(), 1); // shall be 0. but geos believes that they are intersecting each other. + //ToDo: Check this in detail. Its possible that geos does not like concave polygons! + + } + BOOST_AUTO_TEST_CASE(spatial_index_complex) { Core core; From d27e690f60b54255a57182ee1cf6fa81be32e8a6 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 24 Sep 2018 13:08:30 +0200 Subject: [PATCH 21/64] Fix geometry check for Spatial Queries and update unit test --- include/sempr/processing/SpatialIndex.hpp | 27 +++++++++++++++++------ test/spatial_index_tests.cpp | 20 ++++++++--------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 08f36c1..380e027 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -322,51 +322,64 @@ class SpatialIndex : { case SpatialQueryType::WITHIN: { - for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + 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(); ++resultIt) + 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(); ++resultIt) + 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(); ++resultIt) + 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(); ++resultIt) + 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(); ++resultIt) + for (auto resultIt = results.begin(); resultIt != results.end();) if (ref.second->getGeometry()->intersects(resultIt->second->getGeometry())) resultIt = results.erase(resultIt); + else + ++resultIt; break; } diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 9f38989..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; @@ -80,10 +81,10 @@ BOOST_AUTO_TEST_SUITE(spatial_index) geos::geom::Coordinate coord; coord = geos::geom::Coordinate(1, 4); coords.push_back(coord); - coord = geos::geom::Coordinate(1, 10); coords.push_back(coord); - coord = geos::geom::Coordinate(5, 13); coords.push_back(coord); - coord = geos::geom::Coordinate(4, 8); coords.push_back(coord); - coord = geos::geom::Coordinate(5, 1); 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); @@ -96,10 +97,10 @@ BOOST_AUTO_TEST_SUITE(spatial_index) std::vector coords; geos::geom::Coordinate coord; - coord = geos::geom::Coordinate(3.5, 8); coords.push_back(coord); - coord = geos::geom::Coordinate(7, 14); coords.push_back(coord); - coord = geos::geom::Coordinate(7, 0); coords.push_back(coord); - coord = geos::geom::Coordinate(3.5, 8); coords.push_back(coord); // close + 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); } @@ -111,8 +112,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) auto queryIntersection = SpatialIndexQuery2D::intersects(right); core.answerQuery(queryIntersection); - BOOST_CHECK_EQUAL(queryIntersection->results.size(), 1); // shall be 0. but geos believes that they are intersecting each other. - //ToDo: Check this in detail. Its possible that geos does not like concave polygons! + BOOST_CHECK_EQUAL(queryIntersection->results.size(), 0); // shall be 0. } From 5a42d2c2a64dc682c87e15002ed92bce449b910d Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 24 Sep 2018 17:07:19 +0200 Subject: [PATCH 22/64] SpatialConclusion for 2D and 3D. Add SpatialThing as example entity. --- include/sempr/entity/example/SpatialThing.hpp | 36 +++ .../sempr/processing/SpatialConclusion.hpp | 148 ++++++------ include/sempr/processing/SpatialIndex.hpp | 4 +- src/entity/example/SpatialThing.cpp | 22 ++ src/processing/SpatialConclusion.cpp | 217 ------------------ test/spatial_conclusion_tests.cpp | 53 +++-- test/test_utils.hpp | 1 + 7 files changed, 161 insertions(+), 320 deletions(-) create mode 100644 include/sempr/entity/example/SpatialThing.hpp create mode 100644 src/entity/example/SpatialThing.cpp diff --git a/include/sempr/entity/example/SpatialThing.hpp b/include/sempr/entity/example/SpatialThing.hpp new file mode 100644 index 0000000..7c5aed9 --- /dev/null +++ b/include/sempr/entity/example/SpatialThing.hpp @@ -0,0 +1,36 @@ +#ifndef SEMPR_ENTITY_SPATIALTHING_H_ +#define SEMPR_ENTITY_SPATIALTHING_H_ + +#include +#include +#include + +#include + +namespace sempr { namespace entity { + + +#pragma db object +class SpatialThing : public Entity { + SEMPR_ENTITY +public: + using Ptr = std::shared_ptr; + + SpatialThing(); + SpatialThing(const core::IDGenBase*); + + Polygon::Ptr& geometry() { return polygon_; } + + std::string& type() { return type_; } + +protected: + friend class odb::access; + + std::string type_; + Polygon::Ptr polygon_; +}; + +}} + + +#endif /* end of include guard: SEMPR_ENTITY_SPATIALTHING_H_ */ diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 4ec3f33..cb9495a 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -29,14 +29,10 @@ namespace sempr { namespace processing { * * @prefix geo: * @prefix ogc: - * ogc:sfEquals - * ogc:sfDisjoint //b - * ogc:sfIntersects //b - * ogc:sfTouches //? - * ogc:sfWithin //b - * ogc:sfContains //b - * ogc:sfOverlaps //b - * ogc:sfCrosses //only for multipoint/polygon, multipoint/linestring, linestring/linestring, linestring/polygon, and linestring/multipolygon comparisons. + * ogc:sfIntersects + * ogc:sfWithin + * ogc:sfContains + * ogc:sfOverlaps * * @prefix spatial: * spatial:north @@ -44,7 +40,7 @@ namespace sempr { namespace processing { * spatial:west * spatial:east * - * Note: To fullfill the GeoSPARQL Entity the SpatialEntity have to be makred in RDF as SubClassOf ogc:SpatialObject and the depending geometry as ogc:Geometry + * Note: To fullfill the GeoSPARQL Entity 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 * @@ -55,7 +51,12 @@ namespace sempr { namespace processing { * below * on (like above but connected!) * under (like below but connected!) - * + * + * 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 object pointer. * @@ -63,17 +64,19 @@ namespace sempr { namespace processing { */ -template +template < std::size_t dim, class SpatialEntity> class SpatialConclusion : public Module< core::EntityEvent, core::EntityEvent > { public: - using Ptr = std::shared_ptr< SpatialConclusion >; + using Ptr = std::shared_ptr< SpatialConclusion >; + + typedef typename processing::SpatialIndexBase::ValuePair ValuePair; // isGlobal is set if both geometries are transformed in the same global reference system - typedef std::function::Box& self,const SpatialIndex<3>::Box& other, bool isGlobal)> CheckBoxFunction; - typedef std::function CheckGeometryFunction; + //typedef std::function::Box& self,const SpatialIndex::Box& other, bool isGlobal)> CheckBoxFunction; + typedef std::function CheckFunction; - SpatialConclusion(const std::weak_ptr< SpatialIndex<3> >& spatialIndex) : + SpatialConclusion(const std::weak_ptr< SpatialIndex >& spatialIndex) : index_(spatialIndex) { globalRoot_ = false; @@ -116,19 +119,19 @@ class SpatialConclusion : public Module< core::EntityEvent, core: // todo found and update the based enities } - void registerCheckFunction(const std::string& relationPredicate, const CheckBoxFunction& checker) + void registerCheckFunction(const std::string& relationPredicate, const CheckFunction& checker) { - checkBoxFunctions_[relationPredicate] = checker; + checkFunctions_[relationPredicate] = checker; } - void registerCheckFunction(const std::string& relationPredicate, const CheckGeometryFunction& checker) + entity::RDFVector::Ptr getConclusion(const std::shared_ptr& entity) { - checkGeomFunctions_[relationPredicate] = checker; + return rdfMap_.at(entity->id()); } private: - std::weak_ptr< SpatialIndex<3> > index_; + std::weak_ptr< SpatialIndex > index_; std::map spatialGeometry_; @@ -136,8 +139,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: bool globalRoot_; - std::map checkBoxFunctions_; - std::map checkGeomFunctions_; + std::map checkFunctions_; void removeEntity(const std::shared_ptr& entity) @@ -212,7 +214,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: void removeBackRelation(const std::string& id) { - std::string objID = sempr::baseURI() + id; + std::string objID = "<" + sempr::baseURI() + id + ">"; for (auto rdfIt = rdfMap_.begin(); rdfIt != rdfMap_.end(); ++rdfIt) { @@ -249,66 +251,38 @@ class SpatialConclusion : public Module< core::EntityEvent, core: auto idx = index_.lock(); if (idx) { - auto selfBox = idx->geo2box_[self].first; + 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 = checkBoxFunctions_.begin(); checkBoxIt != checkBoxFunctions_.end(); ++checkBoxIt) - { - - for (auto other : idx->geo2box_) - { - //check from self to others - bool selfRelated = checkBoxIt->second(selfBox, other.second.first, globalRoot_); - if (selfRelated) - { - // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t(sempr::baseURI() + id, checkBoxIt->first, sempr::baseURI() + spatialGeometry_.at(other.first)); - rdfMap_[id]->addTriple(t, true); - } - - //check from others to self - bool otherRelated = checkBoxIt->second(other.second.first, selfBox, globalRoot_); - if (otherRelated) - { - auto otherID = spatialGeometry_.at(other.first); - // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t(sempr::baseURI() + otherID, checkBoxIt->first, sempr::baseURI() + id); - rdfMap_[otherID]->addTriple(t, true); - changedRDF.insert(rdfMap_[otherID]); //mark vactor as changed - } - } - - } - - - auto selfGeometry = idx->geo2box_[self].second; - - // check every registered geom check function - for (auto checkGeomIt = checkGeomFunctions_.begin(); checkGeomIt != checkGeomFunctions_.end(); ++checkGeomIt) + for (auto checkBoxIt = checkFunctions_.begin(); checkBoxIt != checkFunctions_.end(); ++checkBoxIt) { for (auto other : idx->geo2box_) { //check from self to others - bool selfRelated = checkGeomIt->second(selfGeometry, other.second.second, globalRoot_); + bool selfRelated = checkBoxIt->second(selfPair, other.second, globalRoot_); if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t(sempr::baseURI() + id, checkGeomIt->first, sempr::baseURI() + spatialGeometry_.at(other.first)); + entity::Triple t( "<" + sempr::baseURI() + id + ">", + "<" + checkBoxIt->first + ">", + "<" + sempr::baseURI() + spatialGeometry_.at(other.first) + ">"); rdfMap_[id]->addTriple(t, true); } //check from others to self - bool otherRelated = checkGeomIt->second(other.second.second, selfGeometry, globalRoot_); + bool otherRelated = checkBoxIt->second(other.second, selfPair, globalRoot_); if (otherRelated) { auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t(sempr::baseURI() + otherID, checkGeomIt->first, sempr::baseURI() + id); + entity::Triple t( "<" + sempr::baseURI() + otherID+ ">", + "<" + checkBoxIt->first + ">", + "<" + sempr::baseURI() + id+ ">"); rdfMap_[otherID]->addTriple(t, true); - changedRDF.insert(rdfMap_[otherID]); //mark vactor as changed + changedRDF.insert(rdfMap_[otherID]); //mark vector as changed } } @@ -330,17 +304,17 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } - static bool check2D(const SpatialIndex<3>::Box& test) - { - double h = abs(test.min_corner().get<2>() - test.max_corner().get<2>()); - return h < 0.00001; // objects with a high less than 0.1mm - } - static bool checkNorthOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) + static bool checkNorthOf(const ValuePair& self, const ValuePair& other, bool isGlobal) { if (isGlobal) { - return self.min_corner().get<0>() >= other.max_corner().get<0>(); // x coordinate is lat (only in WGS84) + auto selfBox = self.first; + auto selfMinX = bg::get<0>(selfBox.min_corner()); + + auto otherBox = other.first; + auto otherMaxX = bg::get<0>(otherBox.max_corner()); + return selfMinX >= otherMaxX; // x coordinate is lat (only in WGS84) } else { @@ -348,11 +322,16 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkSouthOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) + static bool checkSouthOf(const ValuePair& self, const ValuePair& other, bool isGlobal) { if (isGlobal) { - return self.max_corner().get<0>() <= other.min_corner().get<0>(); // x coordinate is lat (only in WGS84) + auto selfBox = self.first; + auto selfMaxX = bg::get<0>(selfBox.max_corner()); + + auto otherBox = other.first; + auto otherMinX = bg::get<0>(otherBox.min_corner()); + return selfMaxX <= otherMinX; // x coordinate is lat (only in WGS84) } else { @@ -360,11 +339,16 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkEastOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) + static bool checkEastOf(const ValuePair& self, const ValuePair& other, bool isGlobal) { if (isGlobal) { - return self.min_corner().get<1>() >= other.max_corner().get<1>(); // y coordinate is lon (only in WGS84) + auto selfBox = self.first; + auto selfMinY = bg::get<1>(selfBox.min_corner()); + + auto otherBox = other.first; + auto otherMaxY = bg::get<1>(otherBox.max_corner()); + return selfMinY >= otherMaxY; // y coordinate is lon (only in WGS84) } else { @@ -372,11 +356,16 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkWestOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) + static bool checkWestOf(const ValuePair& self, const ValuePair& other, bool isGlobal) { if (isGlobal) { - return self.max_corner().get<1>() <= other.min_corner().get<1>(); // y coordinate is lon (only in WGS84) + auto selfBox = self.first; + auto selfMaxY = bg::get<1>(selfBox.max_corner()); + + auto otherBox = other.first; + auto otherMinY = bg::get<1>(otherBox.min_corner()); + return selfMaxY <= otherMinY; // y coordinate is lon (only in WGS84) } else { @@ -385,10 +374,15 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } - }; +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 380e027..f1135ec 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -28,7 +28,7 @@ namespace sempr { namespace processing { namespace bg = boost::geometry; namespace bgi = boost::geometry::index; -template +template < std::size_t dim, class SpatialEntity> class SpatialConclusion; @@ -151,7 +151,7 @@ class SpatialIndex : const std::map& getGeoBoxes() const { return geo2box_; } private: - template + template < std::size_t dimension, class SpatialEntity> friend class SpatialConclusion; /** diff --git a/src/entity/example/SpatialThing.cpp b/src/entity/example/SpatialThing.cpp new file mode 100644 index 0000000..68cf2f7 --- /dev/null +++ b/src/entity/example/SpatialThing.cpp @@ -0,0 +1,22 @@ +#include +#include + + +namespace sempr { namespace entity { + +SEMPR_ENTITY_SOURCE(SpatialThing) + +SpatialThing::SpatialThing(const core::IDGenBase* idgen) + : Entity(idgen) +{ + setDiscriminator(); +} + +SpatialThing::SpatialThing() : + SpatialThing(new core::IDGen()) +{ +} + + +} /* entity */ +} /* sempr */ diff --git a/src/processing/SpatialConclusion.cpp b/src/processing/SpatialConclusion.cpp index 4258663..8fb6e70 100644 --- a/src/processing/SpatialConclusion.cpp +++ b/src/processing/SpatialConclusion.cpp @@ -8,222 +8,5 @@ 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()) { - casimportere 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); -}SpatialConclusion - -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/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 6164e4e..8c2881b 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -38,11 +38,11 @@ std::vector getOsnaCoords() { std::vector osnaCorner; - osnaCorner.emplace_back(53.029056, 8.858612); + 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.245902, 8.087810); - osnaCorner.emplace_back(53.029056, 8.858612); //close the ring + osnaCorner.emplace_back(52.245900, 8.087793); + osnaCorner.emplace_back(52.245902, 8.087810); //close the ring return osnaCorner; } @@ -146,40 +146,45 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) SpatialIndex2D::Ptr index(new SpatialIndex2D(globalCS)); core.addModule(index); - //SpatialConclusion::Ptr conclusion(new SpatialConclusion(index)); - //core.addModule(conclusion); + SpatialConclusion2D::Ptr conclusion(new SpatialConclusion2D(index)); + core.addModule(conclusion); - //build up a quadrangle - { - Polygon::Ptr osna( new Polygon() ); - osna->setCoordinates(getOsnaCoords()); - osna->setCS(globalCS); - core.addEntity(osna); - } + SopranoModule::Ptr soprano(new SopranoModule()); + core.addModule(soprano); + SpatialThing::Ptr osna( new SpatialThing() ); { - Polygon::Ptr bremen( new Polygon() ); - bremen->setCoordinates(getBremenCoords()); - bremen->setCS(globalCS); - core.addEntity(bremen); + 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); + SpatialThing::Ptr bremen( new SpatialThing() ); { - Polygon::Ptr vechta( new Polygon() ); - vechta->setCoordinates(getVechtaCoords()); - vechta->setCS(globalCS); - core.addEntity(vechta); + 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 nds( new Polygon() ); nds->setCoordinates(getNDSCoords()); nds->setCS(globalCS); - //auto queryNDS = SpatialIndexQuery2D::containsBoxOf(nds); - auto queryNDS = SpatialIndexQuery2D::withinBoxOf(nds); - //auto queryNDS = SpatialIndexQuery2D::intersectsBoxOf(nds); + auto queryNDS = SpatialIndexQuery2D::intersects(nds); core.answerQuery(queryNDS); - BOOST_CHECK_EQUAL(queryNDS->results.size(), 3); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. + BOOST_CHECK_EQUAL(queryNDS->results.size(), 1); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. + + auto osnaContext = conclusion->getConclusion(osna); + + BOOST_CHECK_EQUAL(osnaContext->size(), 2); // Osna is in the south west of bremen - so there shall be two triple. } diff --git a/test/test_utils.hpp b/test/test_utils.hpp index 6a09a33..89e7196 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -35,6 +35,7 @@ #include #include +#include // reference systems #include From 747838b572f31e78950912e6eecd5dfa7aef6023 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Wed, 26 Sep 2018 11:32:20 +0200 Subject: [PATCH 23/64] Fix shortcut in RDF Triple and add test for a SpatialConclusion based SPARQL Query --- .../sempr/processing/SpatialConclusion.hpp | 31 +++++++++++++------ test/spatial_conclusion_tests.cpp | 24 ++++++++++++-- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index cb9495a..217c805 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -119,8 +119,11 @@ class SpatialConclusion : public Module< core::EntityEvent, core: // todo found and update the based enities } + // 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! void registerCheckFunction(const std::string& relationPredicate, const CheckFunction& checker) { + // ToDo: check that the pedicate is a complete uri and now shortcut! checkFunctions_[relationPredicate] = checker; } @@ -266,9 +269,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t( "<" + sempr::baseURI() + id + ">", - "<" + checkBoxIt->first + ">", - "<" + sempr::baseURI() + spatialGeometry_.at(other.first) + ">"); + entity::Triple t( toURI(id), + checkBoxIt->first, + toURI(spatialGeometry_.at(other.first)) ); rdfMap_[id]->addTriple(t, true); } @@ -278,9 +281,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: { auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t( "<" + sempr::baseURI() + otherID+ ">", - "<" + checkBoxIt->first + ">", - "<" + sempr::baseURI() + id+ ">"); + entity::Triple t( toURI(otherID), + checkBoxIt->first, + toURI(id) ); rdfMap_[otherID]->addTriple(t, true); changedRDF.insert(rdfMap_[otherID]); //mark vector as changed } @@ -295,16 +298,24 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } + // register the default set of check functions void initDefaultChecker() { - registerCheckFunction("spatial:north", checkNorthOf); - registerCheckFunction("spatial:south", checkSouthOf); - registerCheckFunction("spatial:east", checkEastOf); - registerCheckFunction("spatial:west", checkWestOf); + registerCheckFunction("", checkNorthOf); + registerCheckFunction("", checkSouthOf); + registerCheckFunction("", checkEastOf); + registerCheckFunction("", checkWestOf); } + std::string toURI(const std::string& id) + { + return "<" + sempr::baseURI() + id + ">"; + } + + //ToDo: Add checks for ogc:sfIntersects, ogc:sfWithin, ogc:sfContains, ogc:sfOverlaps + //ToDo: Checks the coordinate Systems for this conditions. For WGS84 and ENU/LTG the x axis points to the north. In ECEF its depends on the z axis and for a projection the y axis points to north and x to the east! static bool checkNorthOf(const ValuePair& self, const ValuePair& other, bool isGlobal) { if (isGlobal) diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 8c2881b..9d13d51 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -163,6 +163,17 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) } core.addEntity(osna); + SpatialThing::Ptr vechta( new SpatialThing() ); + { + 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); + SpatialThing::Ptr bremen( new SpatialThing() ); { Polygon::Ptr bremenPolygon( new Polygon() ); @@ -180,11 +191,18 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) auto queryNDS = SpatialIndexQuery2D::intersects(nds); core.answerQuery(queryNDS); - BOOST_CHECK_EQUAL(queryNDS->results.size(), 1); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. + 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. - auto osnaContext = conclusion->getConclusion(osna); + SPARQLQuery::Ptr query(new SPARQLQuery()); + query->prefixes["spatial"] = "http://jena.apache.org/spatial#"; + query->query = "SELECT ?o WHERE { ?o spatial:south " + sempr::baseURI()+ bremen->id() + " . }"; + core.answerQuery(query); - BOOST_CHECK_EQUAL(osnaContext->size(), 2); // Osna is in the south west of bremen - so there shall be two triple. + BOOST_CHECK_EQUAL(query->results.size(), 2); // Vechta and Osnabrück are in the south of Bremen. } From 6c34475eaac36a1a60b19c7d50413935d7965af3 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Wed, 26 Sep 2018 12:26:22 +0200 Subject: [PATCH 24/64] check URI and RDF Shortcut in SpatialConclusion --- include/sempr/core/RDF.hpp | 103 +++++++++--------- .../sempr/processing/SpatialConclusion.hpp | 59 +++++++--- src/core/RDF.cpp | 20 ++++ test/spatial_conclusion_tests.cpp | 12 +- 4 files changed, 129 insertions(+), 65 deletions(-) diff --git a/include/sempr/core/RDF.hpp b/include/sempr/core/RDF.hpp index b99e380..813476c 100644 --- a/include/sempr/core/RDF.hpp +++ b/include/sempr/core/RDF.hpp @@ -7,63 +7,66 @@ namespace sempr { const std::string& baseURI(); + const std::string buildURI(const std::string& id); + const std::string extractID(const std::string& uri); + 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 { + # 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 { + # 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/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 217c805..4213a46 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -121,10 +121,16 @@ class SpatialConclusion : public Module< core::EntityEvent, core: // 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! - void registerCheckFunction(const std::string& relationPredicate, const CheckFunction& checker) + bool registerCheckFunction(const std::string& relationPredicate, const CheckFunction& checker) { - // ToDo: check that the pedicate is a complete uri and now shortcut! - checkFunctions_[relationPredicate] = checker; + if (checkURI(relationPredicate)) + { + checkFunctions_[relationPredicate] = checker; + return true; + } else + { + return false; + } } entity::RDFVector::Ptr getConclusion(const std::shared_ptr& entity) @@ -217,7 +223,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: void removeBackRelation(const std::string& id) { - std::string objID = "<" + sempr::baseURI() + id + ">"; + std::string objURI = sempr::buildURI(id); for (auto rdfIt = rdfMap_.begin(); rdfIt != rdfMap_.end(); ++rdfIt) { @@ -226,7 +232,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: for (auto tIt = rdfIt->second->begin(); tIt != rdfIt->second->end(); ++tIt) { // search for object id in back linked rfd triple - if ((*tIt).object == objID) + if ((*tIt).object == objURI) { toRemove.emplace_back(*tIt); } @@ -269,9 +275,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t( toURI(id), + entity::Triple t( sempr::buildURI(id), checkBoxIt->first, - toURI(spatialGeometry_.at(other.first)) ); + sempr::buildURI(spatialGeometry_.at(other.first)) ); rdfMap_[id]->addTriple(t, true); } @@ -281,9 +287,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: { auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t( toURI(otherID), + entity::Triple t( sempr::buildURI(otherID), checkBoxIt->first, - toURI(id) ); + sempr::buildURI(id) ); rdfMap_[otherID]->addTriple(t, true); changedRDF.insert(rdfMap_[otherID]); //mark vector as changed } @@ -298,6 +304,36 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } + 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() { @@ -307,11 +343,6 @@ class SpatialConclusion : public Module< core::EntityEvent, core: registerCheckFunction("", checkWestOf); } - std::string toURI(const std::string& id) - { - return "<" + sempr::baseURI() + id + ">"; - } - //ToDo: Add checks for ogc:sfIntersects, ogc:sfWithin, ogc:sfContains, ogc:sfOverlaps diff --git a/src/core/RDF.cpp b/src/core/RDF.cpp index 0240110..ed401ae 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) { + return std::string( "<" + baseURI() + id + ">" ); + } + + /** + Extract the ID of a given sempr URI + */ + const std::string extractID(const std::string& uri) { + 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/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 9d13d51..703cdc9 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -137,6 +137,16 @@ std::vector getNDSCoords() BOOST_AUTO_TEST_SUITE(spatial_conclusion) std::string dbfile = "test_spatial_conclusion_sqlite.db"; + BOOST_AUTO_TEST_CASE(spatial_conclusion_id_uri) + { + SpatialThing::Ptr farfaraway( new SpatialThing() ); + + auto uri = sempr::buildURI(farfaraway->id()); + auto id = sempr::extractID(uri); + + BOOST_CHECK_EQUAL(id, farfaraway->id()); + } + BOOST_AUTO_TEST_CASE(spatial_conclusion_cities) { Core core; @@ -199,7 +209,7 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) SPARQLQuery::Ptr query(new SPARQLQuery()); query->prefixes["spatial"] = "http://jena.apache.org/spatial#"; - query->query = "SELECT ?o WHERE { ?o spatial:south " + sempr::baseURI()+ bremen->id() + " . }"; + query->query = "SELECT ?o WHERE { ?o spatial:south " + sempr::buildURI(bremen->id()) + " . }"; core.answerQuery(query); BOOST_CHECK_EQUAL(query->results.size(), 2); // Vechta and Osnabrück are in the south of Bremen. From dfb98c962ad240c95dd8e2185ae2c11fd0b414ac Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Thu, 4 Oct 2018 15:01:56 +0200 Subject: [PATCH 25/64] Add generic cardinal direction checker for WGS84 --- .../sempr/processing/SpatialConclusion.hpp | 116 +++++------------- 1 file changed, 34 insertions(+), 82 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 4213a46..50309b8 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -40,7 +40,7 @@ namespace sempr { namespace processing { * spatial:west * spatial:east * - * Note: To fullfill the GeoSPARQL Entity the SpatialEntity have to be marked in RDF as SubClassOf ogc:SpatialObject and the depending geometry as ogc:Geometry + * Note: To fullfill the GeoSPARQL 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 * @@ -51,6 +51,7 @@ namespace sempr { namespace processing { * below * on (like above but connected!) * under (like below but connected!) + * perpendicular * * Relations in discussion: * ogc:sfEquals @@ -74,20 +75,11 @@ class SpatialConclusion : public Module< core::EntityEvent, core: // 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; + typedef std::function CheckFunction; SpatialConclusion(const std::weak_ptr< SpatialIndex >& spatialIndex) : index_(spatialIndex) { - globalRoot_ = false; - if (auto idx = index_.lock()) - { - auto globalTest = std::dynamic_pointer_cast(idx->rootCS_); - if(globalTest) - globalRoot_ = true; - } - - initDefaultChecker(); }; @@ -146,8 +138,6 @@ class SpatialConclusion : public Module< core::EntityEvent, core: std::map rdfMap_; // temporary rdf vector for every entity (mapped by id) - bool globalRoot_; - std::map checkFunctions_; @@ -250,7 +240,6 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } - } @@ -271,7 +260,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: for (auto other : idx->geo2box_) { //check from self to others - bool selfRelated = checkBoxIt->second(selfPair, other.second, globalRoot_); + bool selfRelated = checkBoxIt->second(selfPair, other.second, index_.lock()->rootCS_); if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID @@ -282,14 +271,14 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } //check from others to self - bool otherRelated = checkBoxIt->second(other.second, selfPair, globalRoot_); + 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), checkBoxIt->first, - sempr::buildURI(id) ); + sempr::buildURI(id) ); rdfMap_[otherID]->addTriple(t, true); changedRDF.insert(rdfMap_[otherID]); //mark vector as changed } @@ -330,90 +319,53 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } catch (const std::out_of_range& oor) { return false; } - + return isShortcut; } // register the default set of check functions void initDefaultChecker() { - registerCheckFunction("", checkNorthOf); - registerCheckFunction("", checkSouthOf); - registerCheckFunction("", checkEastOf); - registerCheckFunction("", checkWestOf); + registerCheckFunction("", directionCheck<0>); + registerCheckFunction("", directionCheck<1>); + registerCheckFunction("", directionCheck<2>); + registerCheckFunction("", directionCheck<3>); } //ToDo: Add checks for ogc:sfIntersects, ogc:sfWithin, ogc:sfContains, ogc:sfOverlaps - - //ToDo: Checks the coordinate Systems for this conditions. For WGS84 and ENU/LTG the x axis points to the north. In ECEF its depends on the z axis and for a projection the y axis points to north and x to the east! - static bool checkNorthOf(const ValuePair& self, const ValuePair& other, bool isGlobal) + // A generic direction check function for north east south west relations. The direction is equal to the GlobalCS direction enum. + template + static bool directionCheck(const ValuePair& self, const ValuePair& other, const entity::SpatialReference::Ptr& ref) { - if (isGlobal) - { - auto selfBox = self.first; - auto selfMinX = bg::get<0>(selfBox.min_corner()); + auto globalRef = std::dynamic_pointer_cast(ref); - auto otherBox = other.first; - auto otherMaxX = bg::get<0>(otherBox.max_corner()); - return selfMinX >= otherMaxX; // x coordinate is lat (only in WGS84) - } - else - { - return false; - } - } + const std::size_t axis = direction % 2; // quick hack to get the dim of the direction for wgs84. - static bool checkSouthOf(const ValuePair& self, const ValuePair& other, bool isGlobal) - { - if (isGlobal) + if (globalRef) { - auto selfBox = self.first; - auto selfMaxX = bg::get<0>(selfBox.max_corner()); + bool positive = direction <= 1; // North and East are the positive directions - auto otherBox = other.first; - auto otherMinX = bg::get<0>(otherBox.min_corner()); - return selfMaxX <= otherMinX; // x coordinate is lat (only in WGS84) - } - else - { - return false; - } - } - - static bool checkEastOf(const ValuePair& self, const ValuePair& other, bool isGlobal) - { - if (isGlobal) - { auto selfBox = self.first; - auto selfMinY = bg::get<1>(selfBox.min_corner()); - auto otherBox = other.first; - auto otherMaxY = bg::get<1>(otherBox.max_corner()); - return selfMinY >= otherMaxY; // y coordinate is lon (only in WGS84) - } - else - { - return false; - } - } - - static bool checkWestOf(const ValuePair& self, const ValuePair& other, bool isGlobal) - { - if (isGlobal) - { - auto selfBox = self.first; - auto selfMaxY = bg::get<1>(selfBox.max_corner()); - auto otherBox = other.first; - auto otherMinY = bg::get<1>(otherBox.min_corner()); - return selfMaxY <= otherMinY; // y coordinate is lon (only in WGS84) - } - else - { - return false; + if (positive) + { + auto selfMin = bg::get(selfBox.min_corner()); + auto otherMax = bg::get(otherBox.max_corner()); + return selfMin >= otherMax; + } + else + { + auto selfMax = bg::get(selfBox.max_corner()); + auto otherMin = bg::get(otherBox.min_corner()); + return selfMax <= otherMin; + } } + + return false; } + //ToDo: Checks the coordinate Systems for this conditions. For WGS84 the x axis points to the north. In ECEF its depends on the z axis and for a projection and ENU/LTG the y axis points to north and x to the east! }; @@ -425,6 +377,6 @@ template using SpatialConclusion3D = SpatialConclusion<3, SpatialEntity>; - }} +}} #endif /* end of include guard SEMPR_PROCESSING_SPATIAL_CONCLUSION_HPP_ */ From 3b170a565b84c21d5a3a2f97d69e9ae0746a891c Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Thu, 4 Oct 2018 17:38:06 +0200 Subject: [PATCH 26/64] DirectionCheck now supportes different global coordinate systems --- .../spatial/reference/EarthCenteredCS.hpp | 2 + .../entity/spatial/reference/GeodeticCS.hpp | 2 + .../entity/spatial/reference/GlobalCS.hpp | 23 ++++++++---- .../spatial/reference/LocalTangentPlaneCS.hpp | 2 + .../entity/spatial/reference/ProjectionCS.hpp | 1 + .../sempr/processing/SpatialConclusion.hpp | 37 +++++++++++++------ include/sempr/processing/SpatialIndexBase.hpp | 10 +++++ .../spatial/reference/EarthCenteredCS.cpp | 15 ++++++++ src/entity/spatial/reference/GeodeticCS.cpp | 13 +++++++ src/entity/spatial/reference/GlobalCS.cpp | 5 +++ .../spatial/reference/LocalTangentPlaneCS.cpp | 16 ++++++++ src/entity/spatial/reference/ProjectionCS.cpp | 15 ++++++++ 12 files changed, 122 insertions(+), 19 deletions(-) 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 0693a78..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,6 +26,21 @@ class GlobalCS : public SpatialReference { //from this to other virtual FilterList to(const GlobalCS::Ptr other) const; + enum CardinalDirection + { + NORTH, + EAST, + SOUTH, + WEST + }; + + /** + * @brief Find the dimension that representate the cardinal direction. + * + * @return std::size_t The dimension (X = 0, Y = 1, Z = 2) + */ + virtual std::size_t directionDimension(const CardinalDirection& direction) const; + protected: GlobalCS(); GlobalCS(const core::IDGenBase*); @@ -52,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/LocalTangentPlaneCS.hpp b/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp index 071576c..ef90ff4 100644 --- a/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp +++ b/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp @@ -23,6 +23,8 @@ class LocalTangentPlaneCS : public GeocentricCS { 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/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/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 50309b8..110060f 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -72,6 +72,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: using Ptr = std::shared_ptr< SpatialConclusion >; 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; @@ -326,24 +327,24 @@ class SpatialConclusion : public Module< core::EntityEvent, core: // register the default set of check functions void initDefaultChecker() { - registerCheckFunction("", directionCheck<0>); - registerCheckFunction("", directionCheck<1>); - registerCheckFunction("", directionCheck<2>); - registerCheckFunction("", directionCheck<3>); + registerCheckFunction("", directionCheck); + registerCheckFunction("", directionCheck); + registerCheckFunction("", directionCheck); + registerCheckFunction("", directionCheck); } //ToDo: Add checks for ogc:sfIntersects, ogc:sfWithin, ogc:sfContains, ogc:sfOverlaps - // A generic direction check function for north east south west relations. The direction is equal to the GlobalCS direction enum. - template + // 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); - const std::size_t axis = direction % 2; // quick hack to get the dim of the direction for wgs84. - 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; @@ -351,14 +352,14 @@ class SpatialConclusion : public Module< core::EntityEvent, core: if (positive) { - auto selfMin = bg::get(selfBox.min_corner()); - auto otherMax = bg::get(otherBox.max_corner()); + auto selfMin = getMin(selfBox, axis); + auto otherMax = getMax(otherBox, axis); return selfMin >= otherMax; } else { - auto selfMax = bg::get(selfBox.max_corner()); - auto otherMin = bg::get(otherBox.min_corner()); + auto selfMax = getMax(selfBox, axis); + auto otherMin = getMin(otherBox, axis); return selfMax <= otherMin; } } @@ -367,6 +368,18 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } //ToDo: Checks the coordinate Systems for this conditions. For WGS84 the x axis points to the north. In ECEF its depends on the z axis and for a projection and ENU/LTG the y axis points to north and x to the east! + 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]; + } + }; diff --git a/include/sempr/processing/SpatialIndexBase.hpp b/include/sempr/processing/SpatialIndexBase.hpp index efe521f..b9c12a9 100644 --- a/include/sempr/processing/SpatialIndexBase.hpp +++ b/include/sempr/processing/SpatialIndexBase.hpp @@ -74,6 +74,11 @@ struct SpatialIndexBase<2> 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; @@ -119,6 +124,11 @@ struct SpatialIndexBase<3> 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; 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 9f238aa..aa1a4e8 100644 --- a/src/entity/spatial/reference/GlobalCS.cpp +++ b/src/entity/spatial/reference/GlobalCS.cpp @@ -59,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/LocalTangentPlaneCS.cpp b/src/entity/spatial/reference/LocalTangentPlaneCS.cpp index 4c424f8..3447b3f 100644 --- a/src/entity/spatial/reference/LocalTangentPlaneCS.cpp +++ b/src/entity/spatial/reference/LocalTangentPlaneCS.cpp @@ -54,6 +54,22 @@ bool LocalTangentPlaneCS::isEqual(const SpatialReference::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/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) { From 5d6a513ec571af4c30ac7008fcafe0d26dae5d3d Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 9 Oct 2018 16:37:49 +0200 Subject: [PATCH 27/64] Add removeProperty for SemanticEntity --- include/sempr/entity/SemanticEntity.hpp | 37 +++++++++++++++++++++++-- src/entity/SemanticEntity.cpp | 16 +++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/include/sempr/entity/SemanticEntity.hpp b/include/sempr/entity/SemanticEntity.hpp index 41d3add..b048229 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,6 +252,8 @@ class SemanticEntity : public RDFEntity { #pragma db transient std::vector properties_; + std::vector::iterator findProperty(const RegisteredPropertyBase& prop); + protected: SemanticEntity(const core::IDGenBase*); SemanticEntity(); @@ -268,6 +272,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 +287,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 +301,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 +322,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/src/entity/SemanticEntity.cpp b/src/entity/SemanticEntity.cpp index 401da52..74f8a25 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) @@ -98,6 +105,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 { From a50f627d98fb85a5182fb99cadf01d598846c87d Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 9 Oct 2018 16:39:34 +0200 Subject: [PATCH 28/64] Rename and move SpatialThing to GeometricObject --- include/sempr/entity/GeometricObject.hpp | 57 ++++++++++++++++ include/sempr/entity/example/SpatialThing.hpp | 36 ---------- .../sempr/processing/SpatialConclusion.hpp | 65 ++++++++++++++++++- src/entity/GeometricObject.cpp | 36 ++++++++++ src/entity/example/SpatialThing.cpp | 22 ------- test/spatial_conclusion_tests.cpp | 16 ++--- test/test_utils.hpp | 2 +- 7 files changed, 164 insertions(+), 70 deletions(-) create mode 100644 include/sempr/entity/GeometricObject.hpp delete mode 100644 include/sempr/entity/example/SpatialThing.hpp create mode 100644 src/entity/GeometricObject.cpp delete mode 100644 src/entity/example/SpatialThing.cpp diff --git a/include/sempr/entity/GeometricObject.hpp b/include/sempr/entity/GeometricObject.hpp new file mode 100644 index 0000000..93c73d6 --- /dev/null +++ b/include/sempr/entity/GeometricObject.hpp @@ -0,0 +1,57 @@ +#ifndef SEMPR_ENTITY_GEOMETRICOBJECT_H_ +#define SEMPR_ENTITY_GEOMETRICOBJECT_H_ + +#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(); //ToDo Allow temporary!! + GeometricObject(const core::IDGenBase*); + + 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) { updateProperty(type_, "rdf:type", 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::vector related(const std::string& predicate = ""); + +private: + friend class odb::access; + + Geometry::Ptr geometry_; + + std::string type_; +/* + template + void updateProperty(T& ref, const std::string predicate, const T& value) + { + removeProperty(predicate, value); // will remove the first equal one! + + ref = value; + + registerProperty(predicate, value); + + changed(); + }*/ +}; + +}} + + +#endif /* end of include guard: SEMPR_ENTITY_GEOMETRICOBJECT_H_ */ diff --git a/include/sempr/entity/example/SpatialThing.hpp b/include/sempr/entity/example/SpatialThing.hpp deleted file mode 100644 index 7c5aed9..0000000 --- a/include/sempr/entity/example/SpatialThing.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef SEMPR_ENTITY_SPATIALTHING_H_ -#define SEMPR_ENTITY_SPATIALTHING_H_ - -#include -#include -#include - -#include - -namespace sempr { namespace entity { - - -#pragma db object -class SpatialThing : public Entity { - SEMPR_ENTITY -public: - using Ptr = std::shared_ptr; - - SpatialThing(); - SpatialThing(const core::IDGenBase*); - - Polygon::Ptr& geometry() { return polygon_; } - - std::string& type() { return type_; } - -protected: - friend class odb::access; - - std::string type_; - Polygon::Ptr polygon_; -}; - -}} - - -#endif /* end of include guard: SEMPR_ENTITY_SPATIALTHING_H_ */ diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 110060f..58cd17d 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -260,6 +260,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: 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) @@ -331,9 +334,13 @@ class SpatialConclusion : public Module< core::EntityEvent, core: registerCheckFunction("", directionCheck); registerCheckFunction("", directionCheck); registerCheckFunction("", directionCheck); - } - //ToDo: Add checks for ogc:sfIntersects, ogc:sfWithin, ogc:sfContains, ogc:sfOverlaps + 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. @@ -366,7 +373,59 @@ class SpatialConclusion : public Module< core::EntityEvent, core: return false; } - //ToDo: Checks the coordinate Systems for this conditions. For WGS84 the x axis points to the north. In ECEF its depends on the z axis and for a projection and ENU/LTG the y axis points to north and x to the east! + + 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) { diff --git a/src/entity/GeometricObject.cpp b/src/entity/GeometricObject.cpp new file mode 100644 index 0000000..5bf6551 --- /dev/null +++ b/src/entity/GeometricObject.cpp @@ -0,0 +1,36 @@ +#include +#include + + +namespace sempr { namespace entity { + +SEMPR_ENTITY_SOURCE(GeometricObject) + +GeometricObject::GeometricObject(const core::IDGenBase* idgen) + : SemanticEntity(idgen) +{ + setDiscriminator(); + + registerProperty("rdfs:subClassOf", ""); +} + +GeometricObject::GeometricObject() : + GeometricObject(new core::IDGen()) +{ +} + +void GeometricObject::geometry(const Geometry::Ptr geometry) +{ + if (geometry_) + removeProperty(geometry_->id(), "rdfs:subClassOf", ""); + + geometry_ = geometry; + + if (geometry_) // cover nullptr set case + registerProperty(geometry_->id(), "rdfs:subClassOf", ""); + + changed(); +} + +} /* entity */ +} /* sempr */ diff --git a/src/entity/example/SpatialThing.cpp b/src/entity/example/SpatialThing.cpp deleted file mode 100644 index 68cf2f7..0000000 --- a/src/entity/example/SpatialThing.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include - - -namespace sempr { namespace entity { - -SEMPR_ENTITY_SOURCE(SpatialThing) - -SpatialThing::SpatialThing(const core::IDGenBase* idgen) - : Entity(idgen) -{ - setDiscriminator(); -} - -SpatialThing::SpatialThing() : - SpatialThing(new core::IDGen()) -{ -} - - -} /* entity */ -} /* sempr */ diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 703cdc9..88546cd 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -139,7 +139,7 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) BOOST_AUTO_TEST_CASE(spatial_conclusion_id_uri) { - SpatialThing::Ptr farfaraway( new SpatialThing() ); + GeometricObject::Ptr farfaraway( new GeometricObject() ); auto uri = sempr::buildURI(farfaraway->id()); auto id = sempr::extractID(uri); @@ -156,42 +156,42 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) SpatialIndex2D::Ptr index(new SpatialIndex2D(globalCS)); core.addModule(index); - SpatialConclusion2D::Ptr conclusion(new SpatialConclusion2D(index)); + SpatialConclusion2D::Ptr conclusion(new SpatialConclusion2D(index)); core.addModule(conclusion); SopranoModule::Ptr soprano(new SopranoModule()); core.addModule(soprano); - SpatialThing::Ptr osna( new SpatialThing() ); + 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; + osna->geometry(osnaPolygon); } core.addEntity(osna); - SpatialThing::Ptr vechta( new SpatialThing() ); + 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; + vechta->geometry(vechtaPolygon); } core.addEntity(vechta); - SpatialThing::Ptr bremen( new SpatialThing() ); + 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; + bremen->geometry(bremenPolygon); } core.addEntity(bremen); diff --git a/test/test_utils.hpp b/test/test_utils.hpp index 89e7196..4911fe2 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -35,7 +35,7 @@ #include #include -#include +#include // reference systems #include From 565de926d046dacbbc793570cefc320833e79c9c Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 15 Oct 2018 14:19:15 +0200 Subject: [PATCH 29/64] GeometricObject and SemanticEntity may be temporary and clean up the branch --- include/sempr/core/RDF.hpp | 7 +- include/sempr/entity/GeometricObject.hpp | 24 ++--- include/sempr/entity/SemanticEntity.hpp | 4 +- .../sempr/processing/SpatialConclusion.hpp | 87 ++++++++++++++----- include/sempr/processing/SpatialIndex.hpp | 2 +- src/core/RDF.cpp | 10 +-- src/entity/GeometricObject.cpp | 50 +++++++++-- src/entity/SemanticEntity.cpp | 17 ++-- src/processing/SpatialConclusion.cpp | 12 --- src/processing/SpatialIndex.cpp | 13 --- src/query/SpatialIndexQuery.cpp | 5 -- test/spatial_conclusion_tests.cpp | 32 +++++-- 12 files changed, 164 insertions(+), 99 deletions(-) delete mode 100644 src/processing/SpatialConclusion.cpp delete mode 100644 src/processing/SpatialIndex.cpp delete mode 100644 src/query/SpatialIndexQuery.cpp diff --git a/include/sempr/core/RDF.hpp b/include/sempr/core/RDF.hpp index 813476c..e9f2b74 100644 --- a/include/sempr/core/RDF.hpp +++ b/include/sempr/core/RDF.hpp @@ -3,12 +3,13 @@ #include #include +#include namespace sempr { const std::string& baseURI(); - const std::string buildURI(const std::string& id); - const std::string extractID(const std::string& uri); + 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 { @@ -22,6 +23,7 @@ namespace sempr { } namespace rdfs { + const std::string& baseURI(); # define RDFS(name) const std::string& (name)(); RDFS(baseURI) RDFS(Resource) @@ -34,6 +36,7 @@ namespace sempr { } namespace owl { + const std::string& baseURI(); # define OWL(name) const std::string& (name)(); OWL(baseURI) OWL(Class) diff --git a/include/sempr/entity/GeometricObject.hpp b/include/sempr/entity/GeometricObject.hpp index 93c73d6..316ad72 100644 --- a/include/sempr/entity/GeometricObject.hpp +++ b/include/sempr/entity/GeometricObject.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -17,19 +18,20 @@ class GeometricObject : public SemanticEntity { public: using Ptr = std::shared_ptr; - GeometricObject(); //ToDo Allow temporary!! - GeometricObject(const core::IDGenBase*); + 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) { updateProperty(type_, "rdf:type", type); } + void type(const std::string& type); - //void registerProperty(const std::string& predicate, const std::string& object); + 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::vector related(const std::string& predicate = ""); + std::set related(const std::string& predicate = ""); private: friend class odb::access; @@ -37,18 +39,6 @@ class GeometricObject : public SemanticEntity { Geometry::Ptr geometry_; std::string type_; -/* - template - void updateProperty(T& ref, const std::string predicate, const T& value) - { - removeProperty(predicate, value); // will remove the first equal one! - - ref = value; - - registerProperty(predicate, value); - - changed(); - }*/ }; }} diff --git a/include/sempr/entity/SemanticEntity.hpp b/include/sempr/entity/SemanticEntity.hpp index b048229..e58685d 100644 --- a/include/sempr/entity/SemanticEntity.hpp +++ b/include/sempr/entity/SemanticEntity.hpp @@ -255,8 +255,10 @@ class SemanticEntity : public RDFEntity { 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: diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 58cd17d..0ceddf1 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -109,7 +109,24 @@ class SpatialConclusion : public Module< core::EntityEvent, core: void process(core::EntityEvent::Ptr refEvent) override { - // todo found and update the based enities + 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; + } } // register a new check function for a given predicate like north or east. @@ -144,35 +161,56 @@ class SpatialConclusion : public Module< core::EntityEvent, core: void removeEntity(const std::shared_ptr& entity) { - rdfMap_[entity->id()]->removed(); // fire remove event + removeEntity(entity->id()); + } + + void removeEntity(const std::string& id) + { + rdfMap_[id]->removed(); // fire remove event - removeGeometryLink(entity->id()); + removeGeometryLink(id); - rdfMap_.erase(entity->id()); // remove rdf vector for the entity - removeBackRelation(entity->id()); // remove linked back relations ot his entity + 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) { - if (indexedGeometry(entity->geometry())) + 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_[entity->id()] = entity::RDFVector::Ptr(new entity::RDFVector(true)); // not persistant Entity - rdfMap_[entity->id()]->created(); + rdfMap_[id] = entity::RDFVector::Ptr(new entity::RDFVector(true)); // not persistant Entity + rdfMap_[id]->created(); } else { // changed Entity - rdfMap_[entity->id()]->clear(); - rdfMap_[entity->id()]->changed(); + rdfMap_[id]->clear(); + rdfMap_[id]->changed(); } - spatialGeometry_[entity->geometry()] = entity->id(); // build up geometry link - checkEntity(entity->id(), entity->geometry()); + spatialGeometry_[geometry] = id; // build up geometry link + checkEntity(id, geometry); } //otherwise skip this entity! - } bool indexedGeometry(const std::shared_ptr& geometry) @@ -200,21 +238,24 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } 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(entity->id()); - removeBackRelation(entity->id()); - addEntity(entity, true); + removeGeometryLink(id); + removeBackRelation(id); + addEntity(geometry, id, true); } - void removeBackRelation(const std::string& id) { - std::string objURI = sempr::buildURI(id); + std::string objURI = sempr::buildURI(id, sempr::baseURI()); for (auto rdfIt = rdfMap_.begin(); rdfIt != rdfMap_.end(); ++rdfIt) { @@ -268,9 +309,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t( sempr::buildURI(id), + entity::Triple t( sempr::buildURI(id, sempr::baseURI()), checkBoxIt->first, - sempr::buildURI(spatialGeometry_.at(other.first)) ); + sempr::buildURI(spatialGeometry_.at(other.first), sempr::baseURI()) ); rdfMap_[id]->addTriple(t, true); } @@ -280,9 +321,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: { auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t( sempr::buildURI(otherID), + entity::Triple t( sempr::buildURI(otherID, sempr::baseURI()), checkBoxIt->first, - sempr::buildURI(id) ); + sempr::buildURI(id, sempr::baseURI()) ); rdfMap_[otherID]->addTriple(t, true); changedRDF.insert(rdfMap_[otherID]); //mark vector as changed } diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index f1135ec..76d5dc9 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -195,7 +195,7 @@ class SpatialIndex : void process(core::EntityEvent::Ptr refEvent) override { - typedef core::EntityEvent::EventType EType; + typedef core::EntityEvent::EventType EType; //Will ony be could if the spatial reference itself is changed switch (refEvent->what()) { diff --git a/src/core/RDF.cpp b/src/core/RDF.cpp index ed401ae..15deca7 100644 --- a/src/core/RDF.cpp +++ b/src/core/RDF.cpp @@ -19,17 +19,17 @@ namespace sempr { /** Build a URI based on the sempr::baseURI and the given string in "" format. */ - const std::string buildURI(const std::string& id) { - return std::string( "<" + baseURI() + id + ">" ); + 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) { - auto pos = uri.find(baseURI()); + 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); + auto id = uri.substr(pos + baseURI.length(), uri.length()-pos-baseURI.length()-1); return id; } else diff --git a/src/entity/GeometricObject.cpp b/src/entity/GeometricObject.cpp index 5bf6551..ab24129 100644 --- a/src/entity/GeometricObject.cpp +++ b/src/entity/GeometricObject.cpp @@ -6,31 +6,65 @@ namespace sempr { namespace entity { SEMPR_ENTITY_SOURCE(GeometricObject) -GeometricObject::GeometricObject(const core::IDGenBase* idgen) - : SemanticEntity(idgen) +GeometricObject::GeometricObject() : + GeometricObject(new core::IDGen()) { - setDiscriminator(); +} - registerProperty("rdfs:subClassOf", ""); +GeometricObject::GeometricObject(bool temporary) : + GeometricObject(new core::IDGen(), temporary) +{ } -GeometricObject::GeometricObject() : - GeometricObject(new core::IDGen()) +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_) - removeProperty(geometry_->id(), "rdfs:subClassOf", ""); + SemanticEntity::removeProperty(sempr::buildURI(geometry_->id(), sempr::baseURI()), sempr::buildURI("subClassOf", sempr::core::rdfs::baseURI()), ""); geometry_ = geometry; if (geometry_) // cover nullptr set case - registerProperty(geometry_->id(), "rdfs:subClassOf", ""); + SemanticEntity::registerProperty(sempr::buildURI(geometry_->id(), sempr::baseURI()), sempr::buildURI("subClassOf", sempr::core::rdfs::baseURI()), ""); + + changed(); +} + +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_); changed(); } +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/SemanticEntity.cpp b/src/entity/SemanticEntity.cpp index 74f8a25..94833e6 100644 --- a/src/entity/SemanticEntity.cpp +++ b/src/entity/SemanticEntity.cpp @@ -86,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_) diff --git a/src/processing/SpatialConclusion.cpp b/src/processing/SpatialConclusion.cpp deleted file mode 100644 index 8fb6e70..0000000 --- a/src/processing/SpatialConclusion.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include -#include - -#include - - -namespace sempr { namespace processing { - - -}} diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp deleted file mode 100644 index 3342364..0000000 --- a/src/processing/SpatialIndex.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include -#include -#include -#include - -#include - - -namespace sempr { namespace processing { - - -}} diff --git a/src/query/SpatialIndexQuery.cpp b/src/query/SpatialIndexQuery.cpp deleted file mode 100644 index ac12860..0000000 --- a/src/query/SpatialIndexQuery.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include - -namespace sempr { namespace query { - -}} diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 88546cd..e8a7257 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -141,8 +141,8 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) { GeometricObject::Ptr farfaraway( new GeometricObject() ); - auto uri = sempr::buildURI(farfaraway->id()); - auto id = sempr::extractID(uri); + auto uri = sempr::buildURI(farfaraway->id(), sempr::baseURI()); + auto id = sempr::extractID(uri, sempr::baseURI()); BOOST_CHECK_EQUAL(id, farfaraway->id()); } @@ -195,10 +195,10 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) } core.addEntity(bremen); - Polygon::Ptr nds( new Polygon() ); - nds->setCoordinates(getNDSCoords()); - nds->setCS(globalCS); - auto queryNDS = SpatialIndexQuery2D::intersects(nds); + 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. @@ -209,11 +209,29 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) 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()) + " . }"; + 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) From d62c20148cf0c31b78e56792c2ac7ab15ce34e8f Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 16 Oct 2018 15:07:52 +0200 Subject: [PATCH 30/64] GeometricObject will not notify about changes of himself --- include/sempr/processing/SpatialConclusion.hpp | 2 +- include/sempr/processing/SpatialIndex.hpp | 2 +- src/entity/GeometricObject.cpp | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 0ceddf1..3dc1c9a 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -109,7 +109,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: void process(core::EntityEvent::Ptr refEvent) override { - typedef core::EntityEvent::EventType EType; //Will ony be could if the spatial reference itself is changed + typedef core::EntityEvent::EventType EType; // will ony be called if the spatial reference itself is changed switch (refEvent->what()) { diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 76d5dc9..31535f5 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -195,7 +195,7 @@ class SpatialIndex : void process(core::EntityEvent::Ptr refEvent) override { - typedef core::EntityEvent::EventType EType; //Will ony be could if the spatial reference itself is changed + typedef core::EntityEvent::EventType EType; // will ony be could if the spatial reference itself is changed switch (refEvent->what()) { diff --git a/src/entity/GeometricObject.cpp b/src/entity/GeometricObject.cpp index ab24129..3c66134 100644 --- a/src/entity/GeometricObject.cpp +++ b/src/entity/GeometricObject.cpp @@ -33,8 +33,6 @@ void GeometricObject::geometry(const Geometry::Ptr geometry) if (geometry_) // cover nullptr set case SemanticEntity::registerProperty(sempr::buildURI(geometry_->id(), sempr::baseURI()), sempr::buildURI("subClassOf", sempr::core::rdfs::baseURI()), ""); - - changed(); } void GeometricObject::type(const std::string& type) @@ -44,8 +42,6 @@ void GeometricObject::type(const std::string& type) type_ = type; SemanticEntity::registerProperty(sempr::buildURI("type", sempr::core::rdf::baseURI()), type_); - - changed(); } void GeometricObject::registerProperty(const std::string& predicate, const std::string& object) From 7ce6435360743409f25d8728c32ee7c9bb424263 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 23 Oct 2018 11:45:56 +0200 Subject: [PATCH 31/64] Update SpatialConclusion comments --- include/sempr/processing/SpatialConclusion.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 3dc1c9a..b6dc92b 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -26,6 +26,7 @@ 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: @@ -40,7 +41,7 @@ namespace sempr { namespace processing { * spatial:west * spatial:east * - * Note: To fullfill the GeoSPARQL the SpatialEntity have to be marked in RDF as SubClassOf ogc:SpatialObject and the depending geometry as ogc:Geometry + * 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 * @@ -59,17 +60,17 @@ namespace sempr { namespace processing { * 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 object pointer. + * 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; From 273ccd25fd1e4790e7855872766d27c724839779 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Thu, 6 Sep 2018 10:42:38 +0100 Subject: [PATCH 32/64] Add a RDFValue based Triple implementation and remove document from triple struct. This will close #33 --- include/sempr/entity/RDFPropertyMap.hpp | 2 +- include/sempr/entity/RDFValue.hpp | 5 +++++ include/sempr/entity/RDFVector.hpp | 9 +++------ include/sempr/entity/Triple.hpp | 21 +++++++++++---------- src/entity/RDFDocument.cpp | 3 +-- src/entity/RDFPropertyMap.cpp | 1 - src/entity/RDFVector.cpp | 17 ++++++++++++----- src/storage/DBObject.cpp | 2 +- test/SemanticEntity_tests.cpp | 2 +- 9 files changed, 35 insertions(+), 27 deletions(-) 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..3536607 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 @@ -235,6 +238,8 @@ template <> RDFValue& RDFValue::operator = (const RDFResource& other); // template <> RDFValue& RDFValue::operator = (const char& value); // done via overloading inside class. +typedef std::tuple RDFValueTriple; + }} #endif /* end of include guard: SEMPR_ENTITY_RDFVALUE_HPP_ */ diff --git a/include/sempr/entity/RDFVector.hpp b/include/sempr/entity/RDFVector.hpp index cda78e3..4c614ce 100644 --- a/include/sempr/entity/RDFVector.hpp +++ b/include/sempr/entity/RDFVector.hpp @@ -2,13 +2,8 @@ #define SEMPR_ENTITY_RDFVECTOR_H_ #include -#include -#include -#include #include -#include - #include namespace sempr { namespace entity { @@ -39,7 +34,7 @@ class RDFVector : public RDFEntity { 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(); @@ -53,6 +48,8 @@ class RDFVector : public RDFEntity { std::vector triples_; }; + + }} #endif /* end of include guard: SEMPR_ENTITY_RDFVECTOR_H_ */ diff --git a/include/sempr/entity/Triple.hpp b/include/sempr/entity/Triple.hpp index 80e4e95..10235e1 100644 --- a/include/sempr/entity/Triple.hpp +++ b/include/sempr/entity/Triple.hpp @@ -3,6 +3,7 @@ #define SEMPR_ENTITY_TRIPLE #include +#include namespace sempr { namespace entity { @@ -10,31 +11,31 @@ namespace sempr { namespace entity { 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 RDFValueTriple& valueTriple) // allow implicit type convert from complex to simple triple { + subject = std::get<0>(valueTriple).toString(); + predicate = std::get<1>(valueTriple); + object = std::get<2>(valueTriple).toString(); } - 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) + Triple(const std::string& s, const std::string& p, const std::string& o) + : 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/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/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..69bc41a 100644 --- a/src/entity/RDFVector.cpp +++ b/src/entity/RDFVector.cpp @@ -56,7 +56,7 @@ const Triple& RDFVector::getTripleAt(const size_t index) return triples_[index]; } -bool RDFVector::addTriple(const sempr::entity::Triple &triple) +bool RDFVector::addTriple(const sempr::entity::Triple &triple, bool replace) { // check if the triple is valid! auto sub = Soprano::Node::fromN3(QString::fromStdString(triple.subject)); @@ -65,11 +65,18 @@ bool RDFVector::addTriple(const sempr::entity::Triple &triple) Soprano::Statement st(sub, pred, obj); - // but add anyway, necessary for RDFPropertyMap etc - triples_.push_back(triple); - //this->changed(); + bool isValid = st.isValid(); + + if (isValid) + { + if (replace) + removeTriple(triple); + + triples_.push_back(triple); + //this->changed(); + } - return st.isValid(); + return isValid; } bool RDFVector::removeTriple(const sempr::entity::Triple &triple) 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/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; } } From 74f66ea8929da4ac87a855bc983b4b9ec320f682 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Thu, 6 Sep 2018 14:12:54 +0100 Subject: [PATCH 33/64] Add a RDFValue based variant of a Triple that is now be used in the RDFVector. Also the RDFVector could now be temporary. --- include/sempr/entity/RDFEntity.hpp | 2 +- include/sempr/entity/RDFValue.hpp | 46 +++++++++++++++++++++++++++++- include/sempr/entity/RDFVector.hpp | 22 +++++++------- include/sempr/entity/Triple.hpp | 8 +++--- src/entity/RDFDocument.cpp | 2 +- src/entity/RDFEntity.cpp | 12 ++++---- src/entity/RDFVector.cpp | 43 +++++++++++++++++----------- 7 files changed, 96 insertions(+), 39 deletions(-) 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/RDFValue.hpp b/include/sempr/entity/RDFValue.hpp index 3536607..2bb022c 100644 --- a/include/sempr/entity/RDFValue.hpp +++ b/include/sempr/entity/RDFValue.hpp @@ -238,7 +238,51 @@ template <> RDFValue& RDFValue::operator = (const RDFResource& other); // template <> RDFValue& RDFValue::operator = (const char& value); // done via overloading inside class. -typedef std::tuple RDFValueTriple; + +#pragma db value +struct RDFValueTriple +{ + RDFValue subject; + std::string predicate; + RDFValue object; + + RDFValueTriple() {} + + RDFValueTriple(const std::string& s, const std::string& p, const std::string& o) + { + subject = s; + predicate = p; + object = o; + } + + RDFValueTriple(const RDFValue& s, const std::string& p, const RDFValue& o) + : subject(s), predicate(p), object(o) + { + } +/* + std::string subject() const + { + return subject.toString(); + } + + std::string predicate() const + { + return predicate; + } + + std::string object() const + { + return object.toString(); + } +*/ + bool operator==(const RDFValueTriple& other) + { + return (subject == other.subject) && + (predicate == other.predicate) && + (object == other.object); + } +}; + }} diff --git a/include/sempr/entity/RDFVector.hpp b/include/sempr/entity/RDFVector.hpp index 4c614ce..7c63c42 100644 --- a/include/sempr/entity/RDFVector.hpp +++ b/include/sempr/entity/RDFVector.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -11,9 +12,9 @@ namespace sempr { namespace entity { // custom triple-iterator for the vector class class RDFVectorIterator : public TripleIterator { friend class RDFVector; - std::vector::const_iterator vit_; + std::vector::const_iterator vit_; - RDFVectorIterator(std::vector::const_iterator it); + RDFVectorIterator(std::vector::const_iterator it); ~RDFVectorIterator(); const Triple operator * () const override; @@ -21,21 +22,20 @@ 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 replace = false); // if replace than an equal triple will be removed before, otherwise there a multiple entries possible - bool removeTriple(const Triple& triple); + void getTriples(std::vector& triples) const; + const RDFValueTriple& getTripleAt(const size_t index); + bool addTriple(const RDFValueTriple& triple, bool replace = false); // if replace than an equal triple will be removed before, otherwise there a multiple entries possible + bool removeTriple(const RDFValueTriple& triple); void removeTripleAt(const size_t index); void clear(); size_t size() const; @@ -43,9 +43,11 @@ class RDFVector : public RDFEntity { TripleIteratorWrapper begin() const override; TripleIteratorWrapper end() const override; + bool validity(const RDFValueTriple& triple) const; + protected: friend class odb::access; - std::vector triples_; + std::vector triples_; }; diff --git a/include/sempr/entity/Triple.hpp b/include/sempr/entity/Triple.hpp index 10235e1..0ed62c9 100644 --- a/include/sempr/entity/Triple.hpp +++ b/include/sempr/entity/Triple.hpp @@ -8,14 +8,14 @@ namespace sempr { namespace entity { #pragma db value -struct Triple { +struct Triple { //deprecated could be replaced by the RDFValueTriple as class with an subject(), predicate(), object() getter for the strings. ToDo! Triple() {} Triple(const RDFValueTriple& valueTriple) // allow implicit type convert from complex to simple triple { - subject = std::get<0>(valueTriple).toString(); - predicate = std::get<1>(valueTriple); - object = std::get<2>(valueTriple).toString(); + subject = valueTriple.subject.toString(); + predicate = valueTriple.predicate; + object = valueTriple.object.toString(); } Triple(const std::string& s, const std::string& p, const std::string& o) diff --git a/src/entity/RDFDocument.cpp b/src/entity/RDFDocument.cpp index d318a81..8989407 100644 --- a/src/entity/RDFDocument.cpp +++ b/src/entity/RDFDocument.cpp @@ -54,7 +54,7 @@ RDFDocument::Ptr RDFDocument::FromFile(const std::string &file) { // qDebug() << *statements; doc->addTriple( - Triple( (*statements).subject().toN3().toStdString(), + RDFValueTriple( (*statements).subject().toN3().toStdString(), (*statements).predicate().toN3().toStdString(), (*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/RDFVector.cpp b/src/entity/RDFVector.cpp index 69bc41a..7b56e82 100644 --- a/src/entity/RDFVector.cpp +++ b/src/entity/RDFVector.cpp @@ -5,7 +5,7 @@ namespace sempr { namespace entity { // iterator impl -RDFVectorIterator::RDFVectorIterator(std::vector::const_iterator it) +RDFVectorIterator::RDFVectorIterator(std::vector::const_iterator it) : vit_(it) { } @@ -34,38 +34,36 @@ 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()) { } -void RDFVector::getTriples(std::vector &triples) const +void RDFVector::getTriples(std::vector &triples) const { triples.insert(triples.end(), triples_.begin(), triples_.end()); } -const Triple& RDFVector::getTripleAt(const size_t index) +const RDFValueTriple& RDFVector::getTripleAt(const size_t index) { return triples_[index]; } -bool RDFVector::addTriple(const sempr::entity::Triple &triple, bool replace) +bool RDFVector::addTriple(const RDFValueTriple& triple, bool replace) { - // 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)); - - Soprano::Statement st(sub, pred, obj); - - bool isValid = st.isValid(); + bool isValid = validity(triple); if (isValid) { @@ -79,7 +77,20 @@ bool RDFVector::addTriple(const sempr::entity::Triple &triple, bool replace) return isValid; } -bool RDFVector::removeTriple(const sempr::entity::Triple &triple) +bool RDFVector::validity(const RDFValueTriple& triple) const +{ + Triple t = triple; + // check if the triple is valid! + 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); + + return st.isValid(); +} + +bool RDFVector::removeTriple(const RDFValueTriple& triple) { auto newEnd = std::remove(triples_.begin(), triples_.end(), triple); if (newEnd == triples_.end()) return false; From 6b2e5bdcb78e1c9a76ade6004e6f70384eba3b32 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Wed, 12 Sep 2018 13:34:46 +0200 Subject: [PATCH 34/64] Remove RDFValueTriple Try --- include/sempr/entity/RDFValue.hpp | 46 ------------------------------ include/sempr/entity/RDFVector.hpp | 17 ++++++----- include/sempr/entity/Triple.hpp | 9 ------ src/entity/RDFDocument.cpp | 2 +- src/entity/RDFVector.cpp | 12 ++++---- 5 files changed, 15 insertions(+), 71 deletions(-) diff --git a/include/sempr/entity/RDFValue.hpp b/include/sempr/entity/RDFValue.hpp index 2bb022c..0a1e350 100644 --- a/include/sempr/entity/RDFValue.hpp +++ b/include/sempr/entity/RDFValue.hpp @@ -238,52 +238,6 @@ template <> RDFValue& RDFValue::operator = (const RDFResource& other); // template <> RDFValue& RDFValue::operator = (const char& value); // done via overloading inside class. - -#pragma db value -struct RDFValueTriple -{ - RDFValue subject; - std::string predicate; - RDFValue object; - - RDFValueTriple() {} - - RDFValueTriple(const std::string& s, const std::string& p, const std::string& o) - { - subject = s; - predicate = p; - object = o; - } - - RDFValueTriple(const RDFValue& s, const std::string& p, const RDFValue& o) - : subject(s), predicate(p), object(o) - { - } -/* - std::string subject() const - { - return subject.toString(); - } - - std::string predicate() const - { - return predicate; - } - - std::string object() const - { - return object.toString(); - } -*/ - bool operator==(const RDFValueTriple& other) - { - return (subject == other.subject) && - (predicate == other.predicate) && - (object == other.object); - } -}; - - }} #endif /* end of include guard: SEMPR_ENTITY_RDFVALUE_HPP_ */ diff --git a/include/sempr/entity/RDFVector.hpp b/include/sempr/entity/RDFVector.hpp index 7c63c42..f86e0b8 100644 --- a/include/sempr/entity/RDFVector.hpp +++ b/include/sempr/entity/RDFVector.hpp @@ -3,7 +3,6 @@ #include #include -#include #include @@ -12,9 +11,9 @@ namespace sempr { namespace entity { // custom triple-iterator for the vector class class RDFVectorIterator : public TripleIterator { friend class RDFVector; - std::vector::const_iterator vit_; + std::vector::const_iterator vit_; - RDFVectorIterator(std::vector::const_iterator it); + RDFVectorIterator(std::vector::const_iterator it); ~RDFVectorIterator(); const Triple operator * () const override; @@ -32,10 +31,10 @@ class RDFVector : public RDFEntity { RDFVector(const core::IDGenBase*, bool temporary = false); virtual ~RDFVector(){} - void getTriples(std::vector& triples) const; - const RDFValueTriple& getTripleAt(const size_t index); - bool addTriple(const RDFValueTriple& triple, bool replace = false); // if replace than an equal triple will be removed before, otherwise there a multiple entries possible - bool removeTriple(const RDFValueTriple& triple); + void getTriples(std::vector& triples) const; + const Triple& getTripleAt(const size_t index); + 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(); size_t size() const; @@ -43,11 +42,11 @@ class RDFVector : public RDFEntity { TripleIteratorWrapper begin() const override; TripleIteratorWrapper end() const override; - bool validity(const RDFValueTriple& triple) const; + bool validity(const Triple& triple) const; protected: friend class odb::access; - std::vector triples_; + std::vector triples_; }; diff --git a/include/sempr/entity/Triple.hpp b/include/sempr/entity/Triple.hpp index 0ed62c9..848c738 100644 --- a/include/sempr/entity/Triple.hpp +++ b/include/sempr/entity/Triple.hpp @@ -3,21 +3,12 @@ #define SEMPR_ENTITY_TRIPLE #include -#include - namespace sempr { namespace entity { #pragma db value struct Triple { //deprecated could be replaced by the RDFValueTriple as class with an subject(), predicate(), object() getter for the strings. ToDo! Triple() {} - Triple(const RDFValueTriple& valueTriple) // allow implicit type convert from complex to simple triple - { - subject = valueTriple.subject.toString(); - predicate = valueTriple.predicate; - object = valueTriple.object.toString(); - } - Triple(const std::string& s, const std::string& p, const std::string& o) : subject(s), predicate(p), object(o) { diff --git a/src/entity/RDFDocument.cpp b/src/entity/RDFDocument.cpp index 8989407..d318a81 100644 --- a/src/entity/RDFDocument.cpp +++ b/src/entity/RDFDocument.cpp @@ -54,7 +54,7 @@ RDFDocument::Ptr RDFDocument::FromFile(const std::string &file) { // qDebug() << *statements; doc->addTriple( - RDFValueTriple( (*statements).subject().toN3().toStdString(), + Triple( (*statements).subject().toN3().toStdString(), (*statements).predicate().toN3().toStdString(), (*statements).object().toN3().toStdString() ) ); diff --git a/src/entity/RDFVector.cpp b/src/entity/RDFVector.cpp index 7b56e82..098159d 100644 --- a/src/entity/RDFVector.cpp +++ b/src/entity/RDFVector.cpp @@ -5,7 +5,7 @@ namespace sempr { namespace entity { // iterator impl -RDFVectorIterator::RDFVectorIterator(std::vector::const_iterator it) +RDFVectorIterator::RDFVectorIterator(std::vector::const_iterator it) : vit_(it) { } @@ -51,17 +51,17 @@ RDFVector::RDFVector() : } -void RDFVector::getTriples(std::vector &triples) const +void RDFVector::getTriples(std::vector &triples) const { triples.insert(triples.end(), triples_.begin(), triples_.end()); } -const RDFValueTriple& RDFVector::getTripleAt(const size_t index) +const Triple& RDFVector::getTripleAt(const size_t index) { return triples_[index]; } -bool RDFVector::addTriple(const RDFValueTriple& triple, bool replace) +bool RDFVector::addTriple(const Triple& triple, bool replace) { bool isValid = validity(triple); @@ -77,7 +77,7 @@ bool RDFVector::addTriple(const RDFValueTriple& triple, bool replace) return isValid; } -bool RDFVector::validity(const RDFValueTriple& triple) const +bool RDFVector::validity(const Triple& triple) const { Triple t = triple; // check if the triple is valid! @@ -90,7 +90,7 @@ bool RDFVector::validity(const RDFValueTriple& triple) const return st.isValid(); } -bool RDFVector::removeTriple(const RDFValueTriple& triple) +bool RDFVector::removeTriple(const Triple& triple) { auto newEnd = std::remove(triples_.begin(), triples_.end(), triple); if (newEnd == triples_.end()) return false; From 553989749498f8ff33f523c246b95631b7ed3c34 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 14 Sep 2018 11:28:15 +0200 Subject: [PATCH 35/64] isEqual check for all ReferenceSystems --- include/sempr/entity/spatial/reference/GlobalCS.hpp | 7 ------- include/sempr/entity/spatial/reference/LocalCS.hpp | 1 + .../spatial/reference/LocalTangentPlaneCS.hpp | 2 +- .../reference/MilitaryGridReferenceSystem.hpp | 2 +- .../entity/spatial/reference/SpatialReference.hpp | 6 ++++++ .../reference/UniversalPolarStereographicCS.hpp | 2 +- .../reference/UniversalTransverseMercatorCS.hpp | 2 +- src/entity/spatial/reference/GlobalCS.cpp | 9 --------- src/entity/spatial/reference/LocalCS.cpp | 13 +++++++++++++ .../spatial/reference/LocalTangentPlaneCS.cpp | 2 +- .../reference/MilitaryGridReferenceSystem.cpp | 2 +- src/entity/spatial/reference/SpatialReference.cpp | 11 +++++++++++ .../reference/UniversalPolarStereographicCS.cpp | 2 +- .../reference/UniversalTransverseMercatorCS.cpp | 2 +- 14 files changed, 39 insertions(+), 24 deletions(-) diff --git a/include/sempr/entity/spatial/reference/GlobalCS.hpp b/include/sempr/entity/spatial/reference/GlobalCS.hpp index 0ac177f..0693a78 100644 --- a/include/sempr/entity/spatial/reference/GlobalCS.hpp +++ b/include/sempr/entity/spatial/reference/GlobalCS.hpp @@ -28,13 +28,6 @@ class GlobalCS : public SpatialReference { //from this to other virtual FilterList to(const GlobalCS::Ptr other) const; - /** - * Check if this and the other global cs are equal. - * Note: This have to be override by derived class with attributes! - */ - virtual bool isEqual(const GlobalCS::Ptr other) const; - - protected: GlobalCS(); GlobalCS(const core::IDGenBase*); 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..071576c 100644 --- a/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp +++ b/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp @@ -21,7 +21,7 @@ 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; 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/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/src/entity/spatial/reference/GlobalCS.cpp b/src/entity/spatial/reference/GlobalCS.cpp index 049fd7a..9f238aa 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 diff --git a/src/entity/spatial/reference/LocalCS.cpp b/src/entity/spatial/reference/LocalCS.cpp index 239f9c8..2f8fa58 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: + return 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..4c424f8 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); 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/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); From 600f577cca7a20e648d4160df2e2d1fbfc9e10d4 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 14 Sep 2018 14:35:42 +0200 Subject: [PATCH 36/64] Fix isEqual for no parent of a local cs --- src/entity/spatial/reference/LocalCS.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity/spatial/reference/LocalCS.cpp b/src/entity/spatial/reference/LocalCS.cpp index 2f8fa58..0424e96 100644 --- a/src/entity/spatial/reference/LocalCS.cpp +++ b/src/entity/spatial/reference/LocalCS.cpp @@ -22,8 +22,8 @@ bool LocalCS::isEqual(const SpatialReference::Ptr other) const auto otherLocal = std::dynamic_pointer_cast(other); if(otherLocal) - { // check if origin is equal: - return parent_->isEqual(otherLocal->parent_) && transform_.isApprox(otherLocal->transform_); + { // 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 { From e3122d801d1bb9d73855c82df4554644ae98cf92 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 14 Sep 2018 15:37:22 +0200 Subject: [PATCH 37/64] SpatialIndex now for a specific root reference system. \n Multiple SpatialIndex for different reference systems possible --- include/sempr/processing/SpatialIndex.hpp | 25 ++-- src/processing/SpatialIndex.cpp | 157 ++++++++++++---------- src/query/SpatialIndexQuery.cpp | 4 +- test/main.cpp | 2 +- test/spatial_index_tests.cpp | 11 +- 5 files changed, 113 insertions(+), 86 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index f2291bb..27de617 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -36,6 +36,10 @@ 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. */ +/** + * Spatial Index for one specific root coordinate system. + * All entities that could not be tranformes to this coordinate system will be skipped! + */ class SpatialIndex : public Module< core::EntityEvent, core::EntityEvent, @@ -45,7 +49,7 @@ class SpatialIndex using Ptr = std::shared_ptr; std::string type() const override; - SpatialIndex(); + SpatialIndex(entity::SpatialReference::Ptr rootCS); /** Answer a SpatialIndexQuery @@ -58,25 +62,30 @@ class SpatialIndex 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? */ - typedef bg::model::point bPoint; - typedef bg::model::box bBox; - typedef std::pair bValue; + typedef bg::model::point bPoint; //ToDo rename it + typedef bg::model::box bBox; //ToDo rename it + typedef std::pair bValue; //ToDo rename it typedef bgi::rtree > RTree; private: /** - 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_; /** - 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; @@ -88,7 +97,7 @@ class SpatialIndex void removeGeo(entity::Geometry::Ptr geo); /** Create a pair of bounding-box and ptr */ - bValue createEntry(entity::Geometry::Ptr geo); + bValue createEntry(entity::Geometry::Ptr geo) const; //could throw an exception if there is no tranformation to the rootCS }; diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp index d862f00..7ae8055 100644 --- a/src/processing/SpatialIndex.cpp +++ b/src/processing/SpatialIndex.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -9,9 +10,9 @@ namespace sempr { namespace processing { -SpatialIndex::SpatialIndex() +SpatialIndex::SpatialIndex(entity::SpatialReference::Ptr rootCS) : + rootCS_(rootCS) { - // nothing to do } @@ -22,51 +23,49 @@ std::string SpatialIndex::type() const 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; + try + { + // create the AABB of the transformed query-volume. Create a transformed box. + auto searchEntry = createEntry(query->refGeo()); - 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; + bBox region = searchEntry.first; - default: - std::cout << "SpatialIndex: Mode " << query->mode() << " not implemented." << '\n'; + 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'; + } + + } + catch (const TransformException& ex) + { + //ref geom of the query is in a different cs. Not results. } std::transform( tmpResults.begin(), tmpResults.end(), @@ -130,12 +129,12 @@ void SpatialIndex::processChangedCS(entity::SpatialReference::Ptr cs) } -SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) +SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const { // get the 3d envelope of the geometry. - geos::geom::Coordinate min, max; - geo->findEnvelope(min, max); + geos::geom::Coordinate geoMin, geoMax; + geo->findEnvelope(geoMin, geoMax); // 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, @@ -146,34 +145,45 @@ SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) 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); + coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); + - geos::geom::MultiPoint* mp = geos::geom::GeometryFactory::getDefaultInstance()->createMultiPoint(cornerCoordinates); + entity::MultiPoint::Ptr mpEntity( new entity::MultiPoint() ); // Note: this wast IDs - recommended to use a factory in future - // transform the geometry - LocalTransformationFilter tf(geo->getCS()->transformationToRoot()); - mp->apply_rw(tf); + mpEntity->setCoordinates(cornerCoordinates); + mpEntity->setCS(geo->getCS()); + + // 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 + + /* // 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) + ); - // get the new envelope - EnvelopeFilter ef; - mp->apply_ro(ef); + */ - // destroy the multi point after there usage: - geos::geom::GeometryFactory::getDefaultInstance()->destroyGeometry(mp); + EnvelopeFilter ef; + mpEntity->getGeometry()->apply_ro(ef); // 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); } @@ -183,13 +193,20 @@ 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); + try + { + // 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); + } + catch (const TransformException& ex) + { + // this will skip the geometry if there is no transformation to the root cs of the spatial index + } // std::cout << "inserted " << geo->id() << " into spatial index. AABB: " // << "(" << entry.first.min_corner().get<0>() << ", " << diff --git a/src/query/SpatialIndexQuery.cpp b/src/query/SpatialIndexQuery.cpp index b3bac03..e59435d 100644 --- a/src/query/SpatialIndexQuery.cpp +++ b/src/query/SpatialIndexQuery.cpp @@ -34,8 +34,6 @@ void SpatialIndexQuery::setupRefGeo(const Eigen::Vector3d &lower, const Eigen::V { 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); @@ -49,6 +47,8 @@ void SpatialIndexQuery::setupRefGeo(const Eigen::Vector3d &lower, const Eigen::V corners->setCoordinates(cornerCoordinates); + corners->setCS(cs); + this->refGeo_ = corners; } diff --git a/test/main.cpp b/test/main.cpp index 5de1bd0..6d61fdd 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() ); + SpatialIndex::Ptr spatial( new SpatialIndex(std::make_shared()) ); sempr::core::IDGenerator::getInstance().setStrategy( std::unique_ptr( new sempr::core::IncrementalIDGeneration(storage) ) diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 5f09224..f70ee82 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -28,11 +28,12 @@ BOOST_AUTO_TEST_SUITE(spatial_index) { Core core; - SpatialIndex::Ptr index(new SpatialIndex()); + LocalCS::Ptr cs(new LocalCS()); + + SpatialIndex::Ptr index(new SpatialIndex(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); @@ -51,13 +52,13 @@ BOOST_AUTO_TEST_SUITE(spatial_index) BOOST_AUTO_TEST_CASE(spatial_index_complex) { Core core; - - SpatialIndex::Ptr index(new SpatialIndex()); - core.addModule(index); // add a spatial refernce LocalCS::Ptr cs(new LocalCS()); core.addEntity(cs); + + SpatialIndex::Ptr index(new SpatialIndex(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 From 272d61b832e92575acaf521dc5b28ba3dcee716a Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Sep 2018 09:52:35 +0200 Subject: [PATCH 38/64] First Version of SpatialConclusion --- include/sempr/processing/SpatialIndex.hpp | 12 +- include/sempr/query/SpatialIndexQuery.hpp | 7 +- src/processing/SpatialConclusion.cpp | 229 ++++++++++++++++++++++ 3 files changed, 242 insertions(+), 6 deletions(-) create mode 100644 src/processing/SpatialConclusion.cpp diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 27de617..85c7bd1 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -22,6 +22,9 @@ namespace sempr { namespace processing { namespace bg = boost::geometry; namespace bgi = boost::geometry::index; +template +class SpatialConclusion; + /** This class implements a spatial index for geometry-entities. It listens to events of Geometry @@ -62,12 +65,15 @@ class SpatialIndex 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? */ - typedef bg::model::point bPoint; //ToDo rename it - typedef bg::model::box bBox; //ToDo rename it - typedef std::pair bValue; //ToDo rename it + typedef bg::model::point bPoint; + typedef bg::model::box bBox; + typedef std::pair bValue; typedef bgi::rtree > RTree; private: + template + friend class SpatialConclusion; + /** * The actual R-Tree. */ diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index 66ce1ce..15d02a9 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -40,10 +40,11 @@ class SpatialIndexQuery : public Query, public core::OType { inside of sqlite3.h, which makes the preprocessor expand it here, and the compiler throwing an error pointing at NOT_WITHIN... */ + // negative constraints have to be odd! enum QueryType { - WITHIN = 0, NOTWITHIN, - CONTAINS, NOTCONTAINS, - INTERSECTS, NOTINTERSECTS + WITHIN = 0, NOTWITHIN, + CONTAINS = 2, NOTCONTAINS, + INTERSECTS = 4, NOTINTERSECTS }; diff --git a/src/processing/SpatialConclusion.cpp b/src/processing/SpatialConclusion.cpp new file mode 100644 index 0000000..4258663 --- /dev/null +++ b/src/processing/SpatialConclusion.cpp @@ -0,0 +1,229 @@ +#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()) { + casimportere 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); +}SpatialConclusion + +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); + } +} +*/ +}} From db9256aeef45c390e5c1ee32b11913d8dc9d856e Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Sep 2018 12:09:38 +0200 Subject: [PATCH 39/64] SpatialConclusion as SpatialIndex Extension --- .../sempr/processing/SpatialConclusion.hpp | 355 ++++++++++++++++++ include/sempr/processing/SpatialIndex.hpp | 4 +- 2 files changed, 357 insertions(+), 2 deletions(-) create mode 100644 include/sempr/processing/SpatialConclusion.hpp diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp new file mode 100644 index 0000000..8afcfdf --- /dev/null +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -0,0 +1,355 @@ +#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. + * + * @prefix geo: + * @prefix ogc: + * ogc:sfEquals + * ogc:sfDisjoint //b + * ogc:sfIntersects //b + * ogc:sfTouches //? + * ogc:sfWithin //b + * ogc:sfContains //b + * ogc:sfOverlaps //b + * ogc:sfCrosses //only for multipoint/polygon, multipoint/linestring, linestring/linestring, linestring/polygon, and linestring/multipolygon comparisons. + * + * @prefix spatial: + * spatial:north + * spatial:south + * spatial:west + * spatial:east + * + * Note: To fullfill the GeoSPARQL Entity the SpatialEntity have to be makred 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!) + * + * + * The SpatialEntity have to implement a geometry() method to get a geometry object pointer. + * + * The ODB header for the SpatialEntity (*_odb.h) have to be included before this module get included. + */ + + +template +class SpatialConclusion : public Module< core::EntityEvent, core::EntityEvent > +{ +public: + using Ptr = std::shared_ptr< SpatialConclusion >; + + // isGlobal is set if both geometries are transformed in the same global reference system + typedef std::function CheckBoxFunction; + typedef std::function CheckGeometryFunction; + + SpatialConclusion(const std::weak_ptr& spatialIndex) : + index_(spatialIndex) + { + globalRoot_ = false; + if (auto idx = index_.lock()) + { + auto globalTest = std::dynamic_pointer_cast(idx->rootCS_); + if(globalTest) + globalRoot_ = true; + } + + + 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 + { + // todo found and update the based enities + } + + void registerCheckFunction(const std::string& relationPredicate, const CheckBoxFunction& checker) + { + checkBoxFunctions_[relationPredicate] = checker; + } + + + +private: + std::weak_ptr index_; + + std::map spatialGeometry_; + + std::map rdfMap_; // temporary rdf vector for every entity (mapped by id) + + bool globalRoot_; + + std::map checkBoxFunctions_; + + + void removeEntity(const std::shared_ptr& entity) + { + rdfMap_[entity->id()]->removed(); // fire remove event + + removeGeometryLink(entity->id()); + + rdfMap_.erase(entity->id()); // remove rdf vector for the entity + removeBackRelation(entity->id()); // remove linked back relations ot his entity + } + + + void addEntity(const std::shared_ptr& entity, bool change = false) + { + if (indexedGeometry(entity->geometry())) + { + if (!change) + { // new Entity + rdfMap_[entity->id()] = entity::RDFVector::Ptr(new entity::RDFVector(true)); // not persistant Entity + rdfMap_[entity->id()]->created(); + } + else + { // changed Entity + rdfMap_[entity->id()]->clear(); + rdfMap_[entity->id()]->changed(); + } + + spatialGeometry_[entity->geometry()] = entity->id(); // build up geometry link + checkEntity(entity->id(), entity->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(); + } + } + + 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) + { + // 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(entity->id()); + removeBackRelation(entity->id()); + addEntity(entity, true); + } + + + void removeBackRelation(const std::string& id) + { + std::string objID = sempr::baseURI() + id; + + 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 == objID) + { + 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 selfBox = idx->geo2box_[self].first; + + std::set changedRDF; //set for all changed RDFVectors by this method + + // check every registered check function + for (auto checkIt = checkBoxFunctions_.begin(); checkIt != checkBoxFunctions_.end(); ++checkIt) + { + + for (auto other : idx->rtree_) + { + //check from self to others + bool selfRelated = checkIt->second(self, other.first, globalRoot_); + if (selfRelated) + { + // Build Triple: SelfId, Function predicate, OtherID + entity::Triple t(sempr::baseURI() + id, checkIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); + rdfMap_[id]->addTriple(t, true); + } + + //check from others to self + bool otherRelated = checkIt->second(other.first, self, globalRoot_); + if (otherRelated) + { + auto otherID = spatialGeometry_.at(other.second); + // Build Triple: OtherID, Function predicate, SelfId + entity::Triple t(sempr::baseURI() + otherID, checkIt->first, sempr::baseURI() + id); + rdfMap_[otherID]->addTriple(t, true); + changedRDF.insert(rdfMap_[otherID]); //mark vactor as changed + } + } + + } + + // notify changes of rdf vectors from others + for (auto rdfVector : changedRDF) + rdfVector->changed(); + } + + } + + void initDefaultChecker() + { + registerCheckFunction("spatial:north", checkNorthOf); + registerCheckFunction("spatial:south", checkSouthOf); + registerCheckFunction("spatial:east", checkEastOf); + registerCheckFunction("spatial:west", checkWestOf); + } + + + static bool check2D(const SpatialIndex::bBox& test) + { + double h = abs(test.min_corner().get<2>() - test.max_corner().get<2>()); + return h < 0.00001; // objects with a high less than 0.1mm + } + + static bool checkNorthOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + { + if (isGlobal) + { + return self.min_corner().get<0>() >= other.max_corner().get<0>(); // x coordinate is lat (only in WGS84) + } + else + { + return false; + } + } + + static bool checkSouthOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + { + if (isGlobal) + { + return self.max_corner().get<0>() <= other.min_corner().get<0>(); // x coordinate is lat (only in WGS84) + } + else + { + return false; + } + } + + static bool checkEastOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + { + if (isGlobal) + { + return self.min_corner().get<1>() >= other.max_corner().get<1>(); // y coordinate is lon (only in WGS84) + } + else + { + return false; + } + } + + static bool checkWestOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + { + if (isGlobal) + { + return self.max_corner().get<1>() <= other.min_corner().get<1>(); // y coordinate is lon (only in WGS84) + } + else + { + return false; + } + } + + + +}; + + +}} + +#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 85c7bd1..1d448df 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -61,11 +61,11 @@ class SpatialIndex /** Specify what is stored in the R-Tree: - boxes, made out of points, consisting of 3 floats, in cartesian space. + 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? */ - typedef bg::model::point bPoint; + typedef bg::model::point bPoint; typedef bg::model::box bBox; typedef std::pair bValue; typedef bgi::rtree > RTree; From 379760496c496d1ca609935852062fa728f6cef0 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Sep 2018 14:12:04 +0200 Subject: [PATCH 40/64] R-Tree with cloned geometries --- include/sempr/processing/SpatialIndex.hpp | 5 ++++- src/processing/SpatialIndex.cpp | 26 ++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 1d448df..3ed9e23 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -67,7 +67,7 @@ class SpatialIndex */ typedef bg::model::point bPoint; typedef bg::model::box bBox; - typedef std::pair bValue; + typedef std::pair bValue; // pair af a bounding box and a translated geometry clone typedef bgi::rtree > RTree; private: @@ -104,6 +104,9 @@ class SpatialIndex /** Create a pair of bounding-box and ptr */ bValue createEntry(entity::Geometry::Ptr geo) const; //could throw an exception if there is no tranformation to the rootCS + entity::Geometry::Ptr findEntry(const bValue value) const; + + }; diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp index 7ae8055..f8f77a0 100644 --- a/src/processing/SpatialIndex.cpp +++ b/src/processing/SpatialIndex.cpp @@ -68,10 +68,11 @@ void SpatialIndex::process(query::SpatialIndexQuery::Ptr query) //ref geom of the query is in a different cs. Not results. } - std::transform( tmpResults.begin(), tmpResults.end(), - std::inserter(query->results, query->results.end()), - [](bValue tmp) { return tmp.second; } - ); + for (auto result : tmpResults) + { + query->results.insert(findEntry(result)); + } + } @@ -183,8 +184,12 @@ SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const bPoint(ef.getMin().x, ef.getMin().y, ef.getMin().z), bPoint(ef.getMax().x, ef.getMax().y, ef.getMax().z) ); + + // create a transformed copy of the geometry. + auto geom = geo->clone(); + geom->transformToCS(rootCS_); - return bValue(box, geo); + return bValue(box, geom); } @@ -242,4 +247,15 @@ void SpatialIndex::removeGeo(entity::Geometry::Ptr geo) } } +entity::Geometry::Ptr SpatialIndex::findEntry(const bValue value) const +{ + for (auto it = geo2box_.begin(); it != geo2box_.end(); ++it) + { + if (it->second.second == value.second) + return it->first; + } + + return nullptr; +} + }} From f9c1e601d5cef5a3ee373353c2855102695cc9e8 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Sep 2018 15:11:58 +0200 Subject: [PATCH 41/64] SpatialConclusion could also checks geometry and not only boxes --- .../sempr/processing/SpatialConclusion.hpp | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 8afcfdf..cfacb2e 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -121,6 +121,10 @@ class SpatialConclusion : public Module< core::EntityEvent, core: checkBoxFunctions_[relationPredicate] = checker; } + void registerCheckFunction(const std::string& relationPredicate, const CheckGeometryFunction& checker) + { + checkGeomFunctions_[relationPredicate] = checker; + } private: @@ -133,6 +137,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: bool globalRoot_; std::map checkBoxFunctions_; + std::map checkGeomFunctions_; void removeEntity(const std::shared_ptr& entity) @@ -246,28 +251,60 @@ class SpatialConclusion : public Module< core::EntityEvent, core: std::set changedRDF; //set for all changed RDFVectors by this method - // check every registered check function - for (auto checkIt = checkBoxFunctions_.begin(); checkIt != checkBoxFunctions_.end(); ++checkIt) + // check every registered box check function + for (auto checkBoxIt = checkBoxFunctions_.begin(); checkBoxIt != checkBoxFunctions_.end(); ++checkBoxIt) + { + + for (auto other : idx->rtree_) + { + //check from self to others + bool selfRelated = checkBoxIt->second(selfBox, other.first, globalRoot_); + if (selfRelated) + { + // Build Triple: SelfId, Function predicate, OtherID + entity::Triple t(sempr::baseURI() + id, checkBoxIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); + rdfMap_[id]->addTriple(t, true); + } + + //check from others to self + bool otherRelated = checkBoxIt->second(other.first, selfBox, globalRoot_); + if (otherRelated) + { + auto otherID = spatialGeometry_.at(other.second); + // Build Triple: OtherID, Function predicate, SelfId + entity::Triple t(sempr::baseURI() + otherID, checkBoxIt->first, sempr::baseURI() + id); + rdfMap_[otherID]->addTriple(t, true); + changedRDF.insert(rdfMap_[otherID]); //mark vactor as changed + } + } + + } + + + auto selfGeometry = idx->geo2box_[self].second; + + // check every registered geom check function + for (auto checkGeomIt = checkGeomFunctions_.begin(); checkGeomIt != checkGeomFunctions_.end(); ++checkGeomIt) { for (auto other : idx->rtree_) { //check from self to others - bool selfRelated = checkIt->second(self, other.first, globalRoot_); + bool selfRelated = checkGeomIt->second(selfGeometry, other.second, globalRoot_); if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t(sempr::baseURI() + id, checkIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); + entity::Triple t(sempr::baseURI() + id, checkGeomIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); rdfMap_[id]->addTriple(t, true); } //check from others to self - bool otherRelated = checkIt->second(other.first, self, globalRoot_); + bool otherRelated = checkGeomIt->second(other.second, selfGeometry, globalRoot_); if (otherRelated) { auto otherID = spatialGeometry_.at(other.second); // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t(sempr::baseURI() + otherID, checkIt->first, sempr::baseURI() + id); + entity::Triple t(sempr::baseURI() + otherID, checkGeomIt->first, sempr::baseURI() + id); rdfMap_[otherID]->addTriple(t, true); changedRDF.insert(rdfMap_[otherID]); //mark vactor as changed } From 743f31c62e42f3d82fe6ae27a6cffd9323931d13 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Sep 2018 15:33:29 +0200 Subject: [PATCH 42/64] R-Tree only for query not iterating --- .../sempr/processing/SpatialConclusion.hpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index cfacb2e..0eea91b 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -255,22 +255,22 @@ class SpatialConclusion : public Module< core::EntityEvent, core: for (auto checkBoxIt = checkBoxFunctions_.begin(); checkBoxIt != checkBoxFunctions_.end(); ++checkBoxIt) { - for (auto other : idx->rtree_) + for (auto other : idx->geo2box_) { //check from self to others - bool selfRelated = checkBoxIt->second(selfBox, other.first, globalRoot_); + bool selfRelated = checkBoxIt->second(selfBox, other.second.first, globalRoot_); if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t(sempr::baseURI() + id, checkBoxIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); + entity::Triple t(sempr::baseURI() + id, checkBoxIt->first, sempr::baseURI() + spatialGeometry_.at(other.first)); rdfMap_[id]->addTriple(t, true); } //check from others to self - bool otherRelated = checkBoxIt->second(other.first, selfBox, globalRoot_); + bool otherRelated = checkBoxIt->second(other.second.first, selfBox, globalRoot_); if (otherRelated) { - auto otherID = spatialGeometry_.at(other.second); + auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId entity::Triple t(sempr::baseURI() + otherID, checkBoxIt->first, sempr::baseURI() + id); rdfMap_[otherID]->addTriple(t, true); @@ -287,22 +287,22 @@ class SpatialConclusion : public Module< core::EntityEvent, core: for (auto checkGeomIt = checkGeomFunctions_.begin(); checkGeomIt != checkGeomFunctions_.end(); ++checkGeomIt) { - for (auto other : idx->rtree_) + for (auto other : idx->geo2box_) { //check from self to others - bool selfRelated = checkGeomIt->second(selfGeometry, other.second, globalRoot_); + bool selfRelated = checkGeomIt->second(selfGeometry, other.second.second, globalRoot_); if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t(sempr::baseURI() + id, checkGeomIt->first, sempr::baseURI() + spatialGeometry_.at(other.second)); + entity::Triple t(sempr::baseURI() + id, checkGeomIt->first, sempr::baseURI() + spatialGeometry_.at(other.first)); rdfMap_[id]->addTriple(t, true); } //check from others to self - bool otherRelated = checkGeomIt->second(other.second, selfGeometry, globalRoot_); + bool otherRelated = checkGeomIt->second(other.second.second, selfGeometry, globalRoot_); if (otherRelated) { - auto otherID = spatialGeometry_.at(other.second); + auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId entity::Triple t(sempr::baseURI() + otherID, checkGeomIt->first, sempr::baseURI() + id); rdfMap_[otherID]->addTriple(t, true); From 073e3353f7355f4b7e50a309f89ebae1db33a842 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 18 Sep 2018 17:20:25 +0200 Subject: [PATCH 43/64] Fix 2D in SpatialIndex by assume a very high box --- include/sempr/processing/SpatialIndex.hpp | 2 ++ src/entity/spatial/filter/GeometryFilter.cpp | 2 +- src/processing/SpatialIndex.cpp | 18 ++++++++++++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 3ed9e23..415463b 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -70,6 +70,8 @@ class SpatialIndex typedef std::pair bValue; // pair af a bounding box and a translated geometry clone typedef bgi::rtree > RTree; + //const std::map& getGeoBoxes() const; + private: template friend class SpatialConclusion; 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/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp index f8f77a0..c405be3 100644 --- a/src/processing/SpatialIndex.cpp +++ b/src/processing/SpatialIndex.cpp @@ -132,11 +132,17 @@ void SpatialIndex::processChangedCS(entity::SpatialReference::Ptr cs) SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const { - - // get the 3d envelope of the geometry. + // get the 3D envelope of the geometry. geos::geom::Coordinate geoMin, geoMax; geo->findEnvelope(geoMin, geoMax); + // Fix for 2D. It will be a box with the max. possible height. + if (geoMin.z != geoMin.z) //NaN Check + geoMin.z = - std::numeric_limits::max(); // max double isnt valid for boost! + + if (geoMax.z != geoMax.z) //NaN Check + geoMax.z = + std::numeric_limits::max(); // max double isnt valid for boost! + // 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 @@ -155,7 +161,6 @@ SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); - entity::MultiPoint::Ptr mpEntity( new entity::MultiPoint() ); // Note: this wast IDs - recommended to use a factory in future mpEntity->setCoordinates(cornerCoordinates); @@ -246,7 +251,12 @@ void SpatialIndex::removeGeo(entity::Geometry::Ptr geo) geo2box_.erase(it); } } - +/* +const std::map& SpatialIndex::getGeoBoxes() const +{ + return geo2box_; +} +*/ entity::Geometry::Ptr SpatialIndex::findEntry(const bValue value) const { for (auto it = geo2box_.begin(); it != geo2box_.end(); ++it) From e5cf03eb2c03f8ecb5e756d7f2de653dcdb2668d Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 18 Sep 2018 17:21:23 +0200 Subject: [PATCH 44/64] Add first tests for spatial conclusion --- test/CMakeLists.txt | 4 + test/spatial_conclusion_tests.cpp | 174 ++++++++++++++++++++++++++++++ test/spatial_index_tests.cpp | 2 + test/test_utils.hpp | 5 + 4 files changed, 185 insertions(+) create mode 100644 test/spatial_conclusion_tests.cpp 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/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp new file mode 100644 index 0000000..5b8a98c --- /dev/null +++ b/test/spatial_conclusion_tests.cpp @@ -0,0 +1,174 @@ +#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 getOsnaCoords() +{ + std::vector osnaCorner; + + osnaCorner.emplace_back(53.029056, 8.858612); + osnaCorner.emplace_back(52.302939, 8.034527); + osnaCorner.emplace_back(52.297650, 8.107368); + osnaCorner.emplace_back(52.245902, 8.087810); + osnaCorner.emplace_back(53.029056, 8.858612); + + 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 Nordpunkt + 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_cities) + { + Core core; + + GeodeticCS::Ptr globalCS(new GeodeticCS()); + + SpatialIndex::Ptr index(new SpatialIndex(globalCS)); + core.addModule(index); + + //SpatialConclusion::Ptr conclusion(new SpatialConclusion(index)); + //core.addModule(conclusion); + + //build up a quadrangle + Polygon::Ptr osna( new Polygon() ); + osna->setCoordinates(getOsnaCoords()); + osna->setCS(globalCS); + core.addEntity(osna); + + Polygon::Ptr bremen( new Polygon() ); + bremen->setCoordinates(getBremenCoords()); + bremen->setCS(globalCS); + core.addEntity(bremen); + + Polygon::Ptr nds( new Polygon() ); + nds->setCoordinates(getNDSCoords()); + nds->setCS(globalCS); + auto queryNDS = SpatialIndexQuery::withinBoxOf(nds); + + core.answerQuery(queryNDS); + BOOST_CHECK_EQUAL(queryNDS->results.size(), 2); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. + + /* + auto queryWithinBox = SpatialIndexQuery::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); + core.answerQuery(queryIntersecBox); + BOOST_CHECK_EQUAL(queryIntersecBox->results.size(), 1); + */ + + } + + 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 f70ee82..f2f906e 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -33,6 +33,8 @@ BOOST_AUTO_TEST_SUITE(spatial_index) SpatialIndex::Ptr index(new SpatialIndex(cs)); core.addModule(index); + + //build up a quadrangle MultiPoint::Ptr mp( new MultiPoint() ); mp->setGeometry(setupQuadrangle({{1, 1, 1}}, {{10, 10, 10}})); diff --git a/test/test_utils.hpp b/test/test_utils.hpp index 8df8f93..1dbbc70 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -35,6 +35,10 @@ #include +#include +#include + + // reference systems #include #include @@ -48,6 +52,7 @@ #include #include +#include using namespace sempr::core; using namespace sempr::storage; From c26e1c0fb68cf14f2991d40a3e6a3a1f8ebcc352 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Thu, 20 Sep 2018 14:51:44 +0200 Subject: [PATCH 45/64] SpatialIndexQuery for 2D or 3D by template parameter and a box/geometry pair as reference --- .../sempr/processing/SpatialConclusion.hpp | 14 +- include/sempr/processing/SpatialIndex.hpp | 27 +-- include/sempr/query/SpatialIndexQuery.hpp | 211 +++++++++++++----- src/processing/SpatialIndex.cpp | 33 ++- src/query/SpatialIndexQuery.cpp | 131 ----------- test/main.cpp | 2 +- test/spatial_conclusion_tests.cpp | 6 +- test/spatial_index_tests.cpp | 8 +- 8 files changed, 204 insertions(+), 228 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 0eea91b..f1c6a37 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -70,7 +70,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: using Ptr = std::shared_ptr< SpatialConclusion >; // isGlobal is set if both geometries are transformed in the same global reference system - typedef std::function CheckBoxFunction; + typedef std::function CheckBoxFunction; typedef std::function CheckGeometryFunction; SpatialConclusion(const std::weak_ptr& spatialIndex) : @@ -179,6 +179,8 @@ class SpatialConclusion : public Module< core::EntityEvent, core: { return idx->geo2box_.find(geometry) != idx->geo2box_.end(); } + + return false; } bool removeGeometryLink(const std::string& id) @@ -328,13 +330,13 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } - static bool check2D(const SpatialIndex::bBox& test) + static bool check2D(const SpatialIndex::Box& test) { double h = abs(test.min_corner().get<2>() - test.max_corner().get<2>()); return h < 0.00001; // objects with a high less than 0.1mm } - static bool checkNorthOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + static bool checkNorthOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) { if (isGlobal) { @@ -346,7 +348,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkSouthOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + static bool checkSouthOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) { if (isGlobal) { @@ -358,7 +360,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkEastOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + static bool checkEastOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) { if (isGlobal) { @@ -370,7 +372,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkWestOf(const SpatialIndex::bBox& self, const SpatialIndex::bBox& other, bool isGlobal) + static bool checkWestOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) { if (isGlobal) { diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 415463b..e72de5c 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -42,11 +42,14 @@ class SpatialConclusion; /** * 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. */ -class SpatialIndex - : public Module< core::EntityEvent, +//template< std::size_t Dim = 3 > +class SpatialIndex : + public Module< core::EntityEvent, core::EntityEvent, - query::SpatialIndexQuery > + query::SpatialIndexQuery<3> >, + SpatialIndexBase<3> { public: using Ptr = std::shared_ptr; @@ -57,18 +60,8 @@ class SpatialIndex /** Answer a SpatialIndexQuery */ - void process(query::SpatialIndexQuery::Ptr query) override; + void process(query::SpatialIndexQuery<3>::Ptr query) override; - /** - 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? - */ - typedef bg::model::point bPoint; - typedef bg::model::box bBox; - typedef std::pair bValue; // pair af a bounding box and a translated geometry clone - typedef bgi::rtree > RTree; //const std::map& getGeoBoxes() const; @@ -89,7 +82,7 @@ class SpatialIndex /** * A mapping of Geometry-->bValue for easier updates of the RTree */ - std::map geo2box_; + std::map geo2box_; /** @@ -105,8 +98,8 @@ class SpatialIndex void removeGeo(entity::Geometry::Ptr geo); /** Create a pair of bounding-box and ptr */ - bValue createEntry(entity::Geometry::Ptr geo) const; //could throw an exception if there is no tranformation to the rootCS - entity::Geometry::Ptr findEntry(const bValue value) const; + ValuePair createEntry(entity::Geometry::Ptr geo, bool query = false) const; //could throw an exception if there is no tranformation to the rootCS + entity::Geometry::Ptr findEntry(const ValuePair value) const; }; diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index 15d02a9..edb8380 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -3,10 +3,38 @@ #include #include +#include #include -#include #include +#include + +#include +#include +#include +#include + +// forward declaration of SpatialIndex // ToDo: Move this to a own Header File! +namespace sempr { namespace processing { + namespace bg = boost::geometry; + namespace bgi = boost::geometry::index; + + 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? + */ + 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; + }; +}} + namespace sempr { namespace query { @@ -23,12 +51,18 @@ 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 typename processing::SpatialIndexBase::ValuePair ValuePair; + typedef typename processing::SpatialIndexBase::Box Box; + typedef typename processing::SpatialIndexBase::Point Point; + typedef Eigen::Matrix EigenVector; + + using Ptr = std::shared_ptr< SpatialIndexQuery >; + ~SpatialIndexQuery() {} - std::string type() const override { return "SpatialIndexQuery"; } + std::string type() const override { return "SpatialIndexQuery" + std::to_string(dim) + "D"; } /** The set of geometries matching the criterium */ std::set results; @@ -45,76 +79,153 @@ class SpatialIndexQuery : public Query, public core::OType { WITHIN = 0, NOTWITHIN, CONTAINS = 2, NOTCONTAINS, INTERSECTS = 4, NOTINTERSECTS - }; + }; // ToDo rename it to SpatialQueryType and make a enum class out of it! + /** 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_; } /** returns the mode of the query: WITHIN, NOT_WITHIN, ... */ - QueryType mode() const; + QueryType mode() const { return qtype_; } + /** set the mode of the query */ - void mode(QueryType m); - /** inverts the mode: WITHIN <-> NOTWITHIN etc. */ - void invert(); + void mode(QueryType 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() + { + /* 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_ = (qtype_ % 2 == 0) ? QueryType(qtype_+1) : QueryType(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, SpatialIndexQuery::WITHIN); + } + + static SpatialIndexQuery::Ptr containsBoxOf(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createBoxQuery(geometry, SpatialIndexQuery::CONTAINS); + } + + static SpatialIndexQuery::Ptr intersectsBoxOf(entity::Geometry::Ptr geometry) + { + return SpatialIndexQuery::createBoxQuery(geometry, SpatialIndexQuery::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(); - */ - - - // TODO CONTAINS and INTERSECTS + static SpatialIndexQuery::Ptr withinBox(const EigenVector& lower, const EigenVector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialIndexQuery::WITHIN); + } + + static SpatialIndexQuery::Ptr containsBox( const EigenVector& lower, const EigenVector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialIndexQuery::CONTAINS); + } + + static SpatialIndexQuery::Ptr intersectsBox(const EigenVector& lower, const EigenVector& upper, entity::SpatialReference::Ptr cs) + { + return SpatialIndexQuery::createBoxQuery(lower, upper, cs, SpatialIndexQuery::INTERSECTS); + } + + // TODO Disjoint and Overlaps + // TODO Query for a specific geometry relations not only Boxes! 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); - // 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); + // the actual type + QueryType qtype_; + + // the reference pair of bounding box and geometry + ValuePair refPair_; + // 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(); + SpatialIndexQuery() {} - // the actual type - QueryType qtype_; + void setupPair(const EigenVector& lower, const EigenVector& 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 EigenVector& min, const EigenVector& max) + { + return Box(toPoint(min), toPoint(max)); + } + + // helper: create query from geo or upper/lower and a type + static SpatialIndexQuery::Ptr createBoxQuery(entity::Geometry::Ptr geometry, QueryType type) + { + geos::geom::Coordinate min, max; + geometry->findEnvelope(min, max); + + auto lower = toVector(min); + auto upper = toVector(max); + + return createBoxQuery(lower, upper, geometry->getCS(), type); + } + + static SpatialIndexQuery::Ptr createBoxQuery(const EigenVector& min, const EigenVector& max, + entity::SpatialReference::Ptr cs, QueryType type) + { + if (!cs) return SpatialIndexQuery::Ptr(); + + SpatialIndexQuery::Ptr query(new SpatialIndexQuery()); + query->setupPair(min, max, nullptr, cs); + query->mode(type); + return query; + } + + static Point toPoint(const EigenVector& vec) + { + if (dim == 2) + { + return Point(vec.x(), vec.y()); + } + else if (dim == 3) + { + return Point(vec.x(), vec.y(), vec.z()); + } + return Point(); + } + + static EigenVector toVector(const geos::geom::Coordinate& coord) + { + EigenVector vec; + + if (dim >= 2) + { + vec.x() = coord.x; + vec.y() = coord.y; + } + if (dim >= 3) + { + vec.z() = coord.z; + } + + return vec; + } - // the reference geometry - entity::Geometry::Ptr refGeo_; }; + }} #endif /* end of include guard SEMPR_QUERY_SPATIALQUERY_HPP_ */ diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp index c405be3..1b0f1d9 100644 --- a/src/processing/SpatialIndex.cpp +++ b/src/processing/SpatialIndex.cpp @@ -21,18 +21,17 @@ std::string SpatialIndex::type() const return "SpatialIndex"; } -void SpatialIndex::process(query::SpatialIndexQuery::Ptr query) +void SpatialIndex::process(query::SpatialIndexQuery<3>::Ptr query) { - std::vector tmpResults; + std::vector tmpResults; try { - // create the AABB of the transformed query-volume. Create a transformed box. - auto searchEntry = createEntry(query->refGeo()); + // ToDo: Transform Box and Geom of the Pair into the root ref system. - bBox region = searchEntry.first; - - typedef query::SpatialIndexQuery::QueryType QueryType; + Box region = query->refBoxGeometryPair().first; + + typedef query::SpatialIndexQuery<3>::QueryType QueryType; switch (query->mode()) { case QueryType::WITHIN: @@ -130,18 +129,18 @@ void SpatialIndex::processChangedCS(entity::SpatialReference::Ptr cs) } -SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const +SpatialIndex::ValuePair SpatialIndex::createEntry(entity::Geometry::Ptr geo, bool query) const { // get the 3D envelope of the geometry. geos::geom::Coordinate geoMin, geoMax; geo->findEnvelope(geoMin, geoMax); - // Fix for 2D. It will be a box with the max. possible height. + // Fix for 2D. It will be a box with the max. possible height. // min -1 and max +1 for a query entry! if (geoMin.z != geoMin.z) //NaN Check - geoMin.z = - std::numeric_limits::max(); // max double isnt valid for boost! + geoMin.z = 0; // -std::numeric_limits::max(); // max double isnt valid for boost! if (geoMax.z != geoMax.z) //NaN Check - geoMax.z = + std::numeric_limits::max(); // max double isnt valid for boost! + geoMax.z = 0; // +std::numeric_limits::max(); // max double isnt valid for boost! // 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, @@ -185,16 +184,16 @@ SpatialIndex::bValue SpatialIndex::createEntry(entity::Geometry::Ptr geo) const mpEntity->getGeometry()->apply_ro(ef); // 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) + Box box( + Point(ef.getMin().x, ef.getMin().y, ef.getMin().z), + Point(ef.getMax().x, ef.getMax().y, ef.getMax().z) ); // create a transformed copy of the geometry. auto geom = geo->clone(); geom->transformToCS(rootCS_); - return bValue(box, geom); + return ValuePair(box, geom); } @@ -207,7 +206,7 @@ void SpatialIndex::insertGeo(entity::Geometry::Ptr geo) try { // create a new entry - bValue entry = createEntry(geo); + ValuePair entry = createEntry(geo); // save it in our map geo2box_[geo] = entry; // and insert it into the RTree @@ -257,7 +256,7 @@ const std::map& SpatialIndex::getGeoBoxes() const return geo2box_; } */ -entity::Geometry::Ptr SpatialIndex::findEntry(const bValue value) const +entity::Geometry::Ptr SpatialIndex::findEntry(const ValuePair value) const { for (auto it = geo2box_.begin(); it != geo2box_.end(); ++it) { diff --git a/src/query/SpatialIndexQuery.cpp b/src/query/SpatialIndexQuery.cpp index e59435d..ac12860 100644 --- a/src/query/SpatialIndexQuery.cpp +++ b/src/query/SpatialIndexQuery.cpp @@ -1,136 +1,5 @@ #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()); - - 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); - - corners->setCS(cs); - - 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/test/main.cpp b/test/main.cpp index 6d61fdd..c021026 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -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 = SpatialIndexQuery<3>::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 index 5b8a98c..6b6b9c1 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -62,7 +62,7 @@ std::vector getNDSCoords() 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 Nordpunkt + 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); @@ -149,7 +149,9 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) Polygon::Ptr nds( new Polygon() ); nds->setCoordinates(getNDSCoords()); nds->setCS(globalCS); - auto queryNDS = SpatialIndexQuery::withinBoxOf(nds); + //auto queryNDS = SpatialIndexQuery<3>::containsBoxOf(nds); + //auto queryNDS = SpatialIndexQuery<3>::withinBoxOf(nds); + auto queryNDS = SpatialIndexQuery<3>::intersectsBoxOf(nds); core.answerQuery(queryNDS); BOOST_CHECK_EQUAL(queryNDS->results.size(), 2); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index f2f906e..14bde31 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -41,11 +41,11 @@ BOOST_AUTO_TEST_SUITE(spatial_index) mp->setCS(cs); core.addEntity(mp); - auto queryWithinBox = SpatialIndexQuery::withinBox(Eigen::Vector3d{0, 0, 0}, Eigen::Vector3d{10, 10 ,10}, cs); + auto queryWithinBox = SpatialIndexQuery<3>::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 = SpatialIndexQuery<3>::intersectsBox(Eigen::Vector3d{1, 1, 1}, Eigen::Vector3d{2, 2 ,2}, cs); core.answerQuery(queryIntersecBox); BOOST_CHECK_EQUAL(queryIntersecBox->results.size(), 1); @@ -102,7 +102,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()); @@ -113,7 +113,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) // intersects query->results.clear(); - query->mode(SpatialIndexQuery::INTERSECTS); + query->mode(SpatialIndexQuery<3>::INTERSECTS); core.answerQuery(query); BOOST_CHECK_EQUAL(query->results.size(), expected_intersects.size()); From 0d4c7b5704b8a78dfc78075963bdbba183573e8c Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 21 Sep 2018 10:04:37 +0200 Subject: [PATCH 46/64] SpatialIndex separate for 2D and 3D --- .../sempr/processing/SpatialConclusion.hpp | 16 +- include/sempr/processing/SpatialIndex.hpp | 295 +++++++++++++++++- include/sempr/query/SpatialIndexQuery.hpp | 5 +- src/processing/SpatialIndex.cpp | 257 --------------- test/main.cpp | 4 +- test/spatial_conclusion_tests.cpp | 7 +- test/spatial_index_tests.cpp | 4 +- 7 files changed, 298 insertions(+), 290 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index f1c6a37..4ec3f33 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -70,10 +70,10 @@ class SpatialConclusion : public Module< core::EntityEvent, core: using Ptr = std::shared_ptr< SpatialConclusion >; // isGlobal is set if both geometries are transformed in the same global reference system - typedef std::function CheckBoxFunction; + typedef std::function::Box& self,const SpatialIndex<3>::Box& other, bool isGlobal)> CheckBoxFunction; typedef std::function CheckGeometryFunction; - SpatialConclusion(const std::weak_ptr& spatialIndex) : + SpatialConclusion(const std::weak_ptr< SpatialIndex<3> >& spatialIndex) : index_(spatialIndex) { globalRoot_ = false; @@ -128,7 +128,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: private: - std::weak_ptr index_; + std::weak_ptr< SpatialIndex<3> > index_; std::map spatialGeometry_; @@ -330,13 +330,13 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } - static bool check2D(const SpatialIndex::Box& test) + static bool check2D(const SpatialIndex<3>::Box& test) { double h = abs(test.min_corner().get<2>() - test.max_corner().get<2>()); return h < 0.00001; // objects with a high less than 0.1mm } - static bool checkNorthOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) + static bool checkNorthOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) { if (isGlobal) { @@ -348,7 +348,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkSouthOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) + static bool checkSouthOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) { if (isGlobal) { @@ -360,7 +360,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkEastOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) + static bool checkEastOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) { if (isGlobal) { @@ -372,7 +372,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkWestOf(const SpatialIndex::Box& self, const SpatialIndex::Box& other, bool isGlobal) + static bool checkWestOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) { if (isGlobal) { diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index e72de5c..6d93934 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -3,6 +3,9 @@ #include #include +#include + +#include #include #include @@ -16,6 +19,7 @@ #include #include +#include namespace sempr { namespace processing { @@ -44,26 +48,84 @@ class SpatialConclusion; * 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 > +template< std::size_t dim = 3 > class SpatialIndex : public Module< core::EntityEvent, core::EntityEvent, - query::SpatialIndexQuery<3> >, - SpatialIndexBase<3> + 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; + + using Ptr = std::shared_ptr< SpatialIndex >; + std::string type() const override { return "SpatialIndex" + std::to_string(dim) + "D"; } - SpatialIndex(entity::SpatialReference::Ptr rootCS); + SpatialIndex(entity::SpatialReference::Ptr rootCS) : + rootCS_(rootCS) + {} /** - Answer a SpatialIndexQuery + Answer a SpatialIndexQuery */ - void process(query::SpatialIndexQuery<3>::Ptr query) override; + void process(std::shared_ptr< query::SpatialIndexQuery > query) override + { + std::vector tmpResults; + + try + { + // ToDo: Transform Box and Geom of the Pair into the root ref system. + + Box region = query->refBoxGeometryPair().first; + + typedef query::SpatialIndexQuery<3>::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'; + } + + } + 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; + } + + + const std::map& getGeoBoxes() const { return geo2box_; } private: template @@ -88,22 +150,221 @@ class SpatialIndex : /** * 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; + + 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! -- skips 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 */ - ValuePair createEntry(entity::Geometry::Ptr geo, bool query = false) const; //could throw an exception if there is no tranformation to the rootCS - entity::Geometry::Ptr findEntry(const ValuePair value) const; + 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); +/* + 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! + + std::vector cornerCoordinates; + + if (dim == 2) + { + geos::geom::Coordinate coord; + coord = geos::geom::Coordinate(geoMin.x, geoMin.y); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMax.y); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMin.y); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMax.y); cornerCoordinates.push_back(coord); + } + + if (dim == 3) + { + geos::geom::Coordinate coord; + coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); + coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); + } + + entity::MultiPoint::Ptr mpEntity( new entity::MultiPoint() ); // Note: this wast IDs - recommended to use a factory in future + + mpEntity->setCoordinates(cornerCoordinates); + mpEntity->setCS(geo->getCS()); + + // 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 + + /* // 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; + mpEntity->getGeometry()->apply_ro(ef); + + // create the bBox out of bPoints. + Box box; + + if (dim == 2) + { + box = Box( + Point(ef.getMin().x, ef.getMin().y), + Point(ef.getMax().x, ef.getMax().y) + ); + } + if (dim == 3) + { + box = Box( + Point(ef.getMin().x, ef.getMin().y, ef.getMin().z), + Point(ef.getMax().x, ef.getMax().y, ef.getMax().z) + ); + } + + // 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; + } }; +typedef SpatialIndex<2> SpatialIndex2D; +typedef SpatialIndex<3> SpatialIndex3D; }} diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index edb8380..c4a001a 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -54,9 +54,10 @@ namespace sempr { namespace query { template class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery > { public: - typedef typename processing::SpatialIndexBase::ValuePair ValuePair; + typedef typename processing::SpatialIndexBase::Box Box; typedef typename processing::SpatialIndexBase::Point Point; + typedef typename processing::SpatialIndexBase::ValuePair ValuePair; typedef Eigen::Matrix EigenVector; using Ptr = std::shared_ptr< SpatialIndexQuery >; @@ -225,6 +226,8 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery SpatialIndexQuery2D; +typedef SpatialIndexQuery<3> SpatialIndexQuery3D; }} diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp index 1b0f1d9..3342364 100644 --- a/src/processing/SpatialIndex.cpp +++ b/src/processing/SpatialIndex.cpp @@ -6,265 +6,8 @@ #include -//#include namespace sempr { namespace processing { -SpatialIndex::SpatialIndex(entity::SpatialReference::Ptr rootCS) : - rootCS_(rootCS) -{ -} - - -std::string SpatialIndex::type() const -{ - return "SpatialIndex"; -} - -void SpatialIndex::process(query::SpatialIndexQuery<3>::Ptr query) -{ - std::vector tmpResults; - - try - { - // ToDo: Transform Box and Geom of the Pair into the root ref system. - - Box region = query->refBoxGeometryPair().first; - - typedef query::SpatialIndexQuery<3>::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'; - } - - } - 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)); - } - -} - - -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::ValuePair SpatialIndex::createEntry(entity::Geometry::Ptr geo, bool query) const -{ - // get the 3D envelope of the geometry. - geos::geom::Coordinate geoMin, geoMax; - geo->findEnvelope(geoMin, geoMax); - - // Fix for 2D. It will be a box with the max. possible height. // min -1 and max +1 for a query entry! - if (geoMin.z != geoMin.z) //NaN Check - geoMin.z = 0; // -std::numeric_limits::max(); // max double isnt valid for boost! - - if (geoMax.z != geoMax.z) //NaN Check - geoMax.z = 0; // +std::numeric_limits::max(); // max double isnt valid for boost! - - // 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(geoMin.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); - - entity::MultiPoint::Ptr mpEntity( new entity::MultiPoint() ); // Note: this wast IDs - recommended to use a factory in future - - mpEntity->setCoordinates(cornerCoordinates); - mpEntity->setCS(geo->getCS()); - - // 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 - - /* // 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; - mpEntity->getGeometry()->apply_ro(ef); - - // create the bBox out of bPoints. - Box box( - Point(ef.getMin().x, ef.getMin().y, ef.getMin().z), - Point(ef.getMax().x, ef.getMax().y, ef.getMax().z) - ); - - // create a transformed copy of the geometry. - auto geom = geo->clone(); - geom->transformToCS(rootCS_); - - return ValuePair(box, geom); -} - - -void SpatialIndex::insertGeo(entity::Geometry::Ptr geo) -{ - // sanity: it needs a geometry, and a spatial reference. - if (!geo->getGeometry() || !geo->getCS()) 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 - } - - // 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); - } -} -/* -const std::map& SpatialIndex::getGeoBoxes() const -{ - return geo2box_; -} -*/ -entity::Geometry::Ptr SpatialIndex::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; -} }} diff --git a/test/main.cpp b/test/main.cpp index c021026..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(std::make_shared()) ); + 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<3>::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 index 6b6b9c1..5a8520d 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -129,7 +129,7 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) GeodeticCS::Ptr globalCS(new GeodeticCS()); - SpatialIndex::Ptr index(new SpatialIndex(globalCS)); + SpatialIndex2D::Ptr index(new SpatialIndex2D(globalCS)); core.addModule(index); //SpatialConclusion::Ptr conclusion(new SpatialConclusion(index)); @@ -151,11 +151,12 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) nds->setCS(globalCS); //auto queryNDS = SpatialIndexQuery<3>::containsBoxOf(nds); //auto queryNDS = SpatialIndexQuery<3>::withinBoxOf(nds); - auto queryNDS = SpatialIndexQuery<3>::intersectsBoxOf(nds); + /* + auto queryNDS = SpatialIndexQuery2D::intersectsBoxOf(nds); core.answerQuery(queryNDS); BOOST_CHECK_EQUAL(queryNDS->results.size(), 2); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. - + */ /* auto queryWithinBox = SpatialIndexQuery::withinBox(Eigen::Vector3d{0, 0, 0}, Eigen::Vector3d{10, 10 ,10}, cs); core.answerQuery(queryWithinBox); diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 14bde31..056313d 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -30,7 +30,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) LocalCS::Ptr cs(new LocalCS()); - SpatialIndex::Ptr index(new SpatialIndex(cs)); + SpatialIndex<3>::Ptr index(new SpatialIndex<3>(cs)); core.addModule(index); @@ -59,7 +59,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) LocalCS::Ptr cs(new LocalCS()); core.addEntity(cs); - SpatialIndex::Ptr index(new SpatialIndex(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: From e8363c8208d9429348f73a025911dcf7e8cbd28e Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 21 Sep 2018 11:15:32 +0200 Subject: [PATCH 47/64] Fix static assert from eigen in SpatialIndexQuery2D --- include/sempr/query/SpatialIndexQuery.hpp | 11 +++-- test/spatial_conclusion_tests.cpp | 56 ++++++++++++++++------- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index c4a001a..1e02aac 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -59,6 +59,7 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery::Point Point; typedef typename processing::SpatialIndexBase::ValuePair ValuePair; typedef Eigen::Matrix EigenVector; + using Ptr = std::shared_ptr< SpatialIndexQuery >; ~SpatialIndexQuery() {} @@ -198,11 +199,11 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery= 2) { - vec.x() = coord.x; - vec.y() = coord.y; + vec[0] = coord.x; + vec[1] = coord.y; } if (dim >= 3) { - vec.z() = coord.z; + vec[3] = coord.z; } return vec; diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 5a8520d..8b593af 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -20,6 +20,20 @@ geom::MultiPoint* setupQuadrangle(const std::array& min, const std::ar 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; @@ -28,7 +42,7 @@ std::vector getOsnaCoords() osnaCorner.emplace_back(52.302939, 8.034527); osnaCorner.emplace_back(52.297650, 8.107368); osnaCorner.emplace_back(52.245902, 8.087810); - osnaCorner.emplace_back(53.029056, 8.858612); + osnaCorner.emplace_back(53.029056, 8.858612); //close the ring return osnaCorner; } @@ -136,27 +150,37 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) //core.addModule(conclusion); //build up a quadrangle - Polygon::Ptr osna( new Polygon() ); - osna->setCoordinates(getOsnaCoords()); - osna->setCS(globalCS); - core.addEntity(osna); - - Polygon::Ptr bremen( new Polygon() ); - bremen->setCoordinates(getBremenCoords()); - bremen->setCS(globalCS); - core.addEntity(bremen); + { + Polygon::Ptr osna( new Polygon() ); + osna->setCoordinates(getOsnaCoords()); + osna->setCS(globalCS); + core.addEntity(osna); + } + + { + Polygon::Ptr bremen( new Polygon() ); + bremen->setCoordinates(getBremenCoords()); + bremen->setCS(globalCS); + core.addEntity(bremen); + } + + { + Polygon::Ptr vechta( new Polygon() ); + vechta->setCoordinates(getVechtaCoords()); + vechta->setCS(globalCS); + core.addEntity(vechta); + } Polygon::Ptr nds( new Polygon() ); nds->setCoordinates(getNDSCoords()); nds->setCS(globalCS); - //auto queryNDS = SpatialIndexQuery<3>::containsBoxOf(nds); - //auto queryNDS = SpatialIndexQuery<3>::withinBoxOf(nds); - /* - auto queryNDS = SpatialIndexQuery2D::intersectsBoxOf(nds); + //auto queryNDS = SpatialIndexQuery2D::containsBoxOf(nds); + auto queryNDS = SpatialIndexQuery2D::withinBoxOf(nds); + //auto queryNDS = SpatialIndexQuery2D::intersectsBoxOf(nds); core.answerQuery(queryNDS); - BOOST_CHECK_EQUAL(queryNDS->results.size(), 2); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. - */ + BOOST_CHECK_EQUAL(queryNDS->results.size(), 3); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. + /* auto queryWithinBox = SpatialIndexQuery::withinBox(Eigen::Vector3d{0, 0, 0}, Eigen::Vector3d{10, 10 ,10}, cs); core.answerQuery(queryWithinBox); From 4b873f51964633f6fa7ccf90218d748a46ef3950 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 21 Sep 2018 15:17:00 +0200 Subject: [PATCH 48/64] Move type mapping and QueryType to SpatialIndexBase --- include/sempr/processing/SpatialIndex.hpp | 152 +++++++++--------- include/sempr/processing/SpatialIndexBase.hpp | 145 +++++++++++++++++ include/sempr/query/SpatialIndexQuery.hpp | 124 ++++---------- test/spatial_index_tests.cpp | 2 +- 4 files changed, 254 insertions(+), 169 deletions(-) create mode 100644 include/sempr/processing/SpatialIndexBase.hpp diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 6d93934..359c00f 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -14,6 +14,8 @@ #include +#include + #include // required for EntityEvent #include // required for EntityEvent @@ -77,38 +79,39 @@ class SpatialIndex : try { - // ToDo: Transform Box and Geom of the Pair into the root ref system. + // Transform Box and Geom of the Pair into the root ref system of the index. + auto transformedPair = transformToRoot(query->refBoxGeometryPair(), query->refCS()); - Box region = query->refBoxGeometryPair().first; + Box region = transformedPair.first; - typedef query::SpatialIndexQuery<3>::QueryType QueryType; + //typedef query::SpatialIndexQuery<3>::QueryType QueryType; switch (query->mode()) { - case QueryType::WITHIN: + case SpatialQueryType::WITHIN: rtree_.query(bgi::within(region), std::back_inserter(tmpResults)); break; - case QueryType::NOTWITHIN: + case SpatialQueryType::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: + case SpatialQueryType::CONTAINS: rtree_.query(bgi::contains(region), std::back_inserter(tmpResults)); break; - case QueryType::NOTCONTAINS: + case SpatialQueryType::NOTCONTAINS: rtree_.query(!bgi::contains(region), std::back_inserter(tmpResults)); break; - case QueryType::INTERSECTS: + case SpatialQueryType::INTERSECTS: rtree_.query(bgi::intersects(region), std::back_inserter(tmpResults)); break; - case QueryType::NOTINTERSECTS: + case SpatialQueryType::NOTINTERSECTS: rtree_.query(!bgi::intersects(region), std::back_inserter(tmpResults)); break; default: - std::cout << "SpatialIndex: Mode " << query->mode() << " not implemented." << '\n'; + std::cout << "SpatialIndex: Mode " << int(query->mode()) << " not implemented." << '\n'; } } @@ -124,7 +127,6 @@ class SpatialIndex : } - const std::map& getGeoBoxes() const { return geo2box_; } private: @@ -214,7 +216,7 @@ class SpatialIndex : // skip geometry with a wrong coordinate dimension if (geo->getGeometry()->getCoordinateDimension() != dim) { - std::cout << "2D Geometry in 3D Index! -- skips this!" << std::endl; + std::cout << "2D Geometry in 3D Index! -- skip this!" << std::endl; return; } @@ -264,10 +266,57 @@ class SpatialIndex : // 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; + } + + 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 @@ -276,40 +325,23 @@ class SpatialIndex : // 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! - - std::vector cornerCoordinates; - - if (dim == 2) - { - geos::geom::Coordinate coord; - coord = geos::geom::Coordinate(geoMin.x, geoMin.y); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMax.y); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMin.y); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMax.y); cornerCoordinates.push_back(coord); - } - - if (dim == 3) - { - geos::geom::Coordinate coord; - coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMin.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMin.y, geoMax.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMin.z); cornerCoordinates.push_back(coord); - coord = geos::geom::Coordinate(geoMax.x, geoMax.y, geoMax.z); cornerCoordinates.push_back(coord); - } 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(geo->getCS()); + 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 - /* // really strange happenings - this is different to the manually apply of the env filter!!! + 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); @@ -322,43 +354,11 @@ class SpatialIndex : */ EnvelopeFilter ef; - mpEntity->getGeometry()->apply_ro(ef); - - // create the bBox out of bPoints. - Box box; - - if (dim == 2) - { - box = Box( - Point(ef.getMin().x, ef.getMin().y), - Point(ef.getMax().x, ef.getMax().y) - ); - } - if (dim == 3) - { - box = Box( - Point(ef.getMin().x, ef.getMin().y, ef.getMin().z), - Point(ef.getMax().x, ef.getMax().y, ef.getMax().z) - ); - } + geomBox->getGeometry()->apply_ro(ef); - // 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; + auto min = this->toPoint(ef.getMin()); + auto max = this->toPoint(ef.getMax()); + return Box(min, max); } }; diff --git a/include/sempr/processing/SpatialIndexBase.hpp b/include/sempr/processing/SpatialIndexBase.hpp new file mode 100644 index 0000000..82e2ff9 --- /dev/null +++ b/include/sempr/processing/SpatialIndexBase.hpp @@ -0,0 +1,145 @@ +#ifndef SEMPR_PROCESSING_SPATIALINDEXBASE_HPP_ +#define SEMPR_PROCESSING_SPATIALINDEXBASE_HPP_ + +#include + +#include + +#include +#include +#include +#include + +#include // required for EntityEvent +#include // required for EntityEvent + + +namespace sempr { namespace processing { + +namespace bg = boost::geometry; +namespace bgi = boost::geometry::index; + +/* + 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... +*/ +// negative constraints have to be odd! +enum class SpatialQueryType { + WITHIN = 0, NOTWITHIN, + CONTAINS = 2, NOTCONTAINS, + INTERSECTS = 4, NOTINTERSECTS +}; + +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 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 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 1e02aac..0a8947c 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -2,6 +2,7 @@ #define SEMPR_QUERY_SPATIALQUERY_HPP_ #include +#include #include #include #include @@ -14,27 +15,6 @@ #include #include -// forward declaration of SpatialIndex // ToDo: Move this to a own Header File! -namespace sempr { namespace processing { - namespace bg = boost::geometry; - namespace bgi = boost::geometry::index; - - 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? - */ - 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; - }; -}} - namespace sempr { namespace query { @@ -54,12 +34,12 @@ namespace sempr { namespace query { template class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery > { public: - - typedef typename processing::SpatialIndexBase::Box Box; - typedef typename processing::SpatialIndexBase::Point Point; - typedef typename processing::SpatialIndexBase::ValuePair ValuePair; - typedef Eigen::Matrix EigenVector; - + 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; using Ptr = std::shared_ptr< SpatialIndexQuery >; ~SpatialIndexQuery() {} @@ -69,28 +49,16 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery 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... - */ - // negative constraints have to be odd! - enum QueryType { - WITHIN = 0, NOTWITHIN, - CONTAINS = 2, NOTCONTAINS, - INTERSECTS = 4, NOTINTERSECTS - }; // ToDo rename it to SpatialQueryType and make a enum class out of it! - /** 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 { return qtype_; } + SpatialQueryType mode() const { return qtype_; } /** set the mode of the query */ - void mode(QueryType m) { qtype_ = m; } + void mode(SpatialQueryType m) { qtype_ = m; } /** inverts the mode: WITHIN <-> NOTWITHIN etc. */ void invert() @@ -99,7 +67,7 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQueryreferenceCS_ = cs; } - Box createBox(const EigenVector& min, const EigenVector& max) + Box createBox(const Vector& min, const Vector& max) { - return Box(toPoint(min), toPoint(max)); + auto minP = SpatialIndexBase::toPoint(min); + auto maxP = SpatialIndexBase::toPoint(max); + return Box(minP, maxP); } // helper: create query from geo or upper/lower and a type - static SpatialIndexQuery::Ptr createBoxQuery(entity::Geometry::Ptr geometry, QueryType type) + static SpatialIndexQuery::Ptr createBoxQuery(entity::Geometry::Ptr geometry, SpatialQueryType type) { geos::geom::Coordinate min, max; geometry->findEnvelope(min, max); - auto lower = toVector(min); - auto upper = toVector(max); + auto lower = SpatialIndexBase::toEigen(min); + auto upper = SpatialIndexBase::toEigen(max); return createBoxQuery(lower, upper, geometry->getCS(), type); } - static SpatialIndexQuery::Ptr createBoxQuery(const EigenVector& min, const EigenVector& max, - entity::SpatialReference::Ptr cs, QueryType type) + static SpatialIndexQuery::Ptr createBoxQuery(const Vector& min, const Vector& max, + entity::SpatialReference::Ptr cs, SpatialQueryType type) { if (!cs) return SpatialIndexQuery::Ptr(); @@ -195,36 +165,6 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery= 2) - { - vec[0] = coord.x; - vec[1] = coord.y; - } - if (dim >= 3) - { - vec[3] = coord.z; - } - - return vec; - } - }; typedef SpatialIndexQuery<2> SpatialIndexQuery2D; diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 056313d..070d29b 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -113,7 +113,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) // intersects query->results.clear(); - query->mode(SpatialIndexQuery<3>::INTERSECTS); + query->mode(SpatialQueryType::INTERSECTS); core.answerQuery(query); BOOST_CHECK_EQUAL(query->results.size(), expected_intersects.size()); From b60d0b30f9933b17faa31c7dbf7f31e52a6483d2 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Fri, 21 Sep 2018 15:56:16 +0200 Subject: [PATCH 49/64] Add support for Nearest, CoveredBy and Overlaps spatial queries. --- include/sempr/processing/SpatialIndex.hpp | 24 ++++++++++--- include/sempr/processing/SpatialIndexBase.hpp | 18 +++++----- include/sempr/query/SpatialIndexQuery.hpp | 34 +++++++++++++++++-- 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 359c00f..d589263 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -84,13 +84,15 @@ class SpatialIndex : Box region = transformedPair.first; - //typedef query::SpatialIndexQuery<3>::QueryType QueryType; 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::NOTWITHIN: + case SpatialQueryType::NOT_WITHIN: rtree_.query(!bgi::within(region), std::back_inserter(tmpResults)); break; @@ -99,16 +101,30 @@ class SpatialIndex : case SpatialQueryType::CONTAINS: rtree_.query(bgi::contains(region), std::back_inserter(tmpResults)); break; - case SpatialQueryType::NOTCONTAINS: + 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::NOTINTERSECTS: + 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." << '\n'; diff --git a/include/sempr/processing/SpatialIndexBase.hpp b/include/sempr/processing/SpatialIndexBase.hpp index 82e2ff9..efe521f 100644 --- a/include/sempr/processing/SpatialIndexBase.hpp +++ b/include/sempr/processing/SpatialIndexBase.hpp @@ -13,24 +13,22 @@ #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; -/* - 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... -*/ // negative constraints have to be odd! enum class SpatialQueryType { - WITHIN = 0, NOTWITHIN, - CONTAINS = 2, NOTCONTAINS, - INTERSECTS = 4, NOTINTERSECTS + NEAREST, + WITHIN = 2, NOT_WITHIN, + CONTAINS = 4, NOT_CONTAINS, + INTERSECTS = 6, NOT_INTERSECTS, + COVERED = 8, NOT_COVERED, + OVERLAPS = 10, NOT_OVERLAPS }; template diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index 0a8947c..521b11a 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -63,6 +63,7 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery 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). @@ -89,6 +90,21 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuery Date: Fri, 21 Sep 2018 17:10:24 +0200 Subject: [PATCH 50/64] Allow Geometry based spatial queries (not Box based) for intersections, contains and within conditions. --- include/sempr/processing/SpatialIndex.hpp | 75 ++++++++++++++++++++++- include/sempr/query/SpatialIndexQuery.hpp | 37 +++++++++++ test/spatial_conclusion_tests.cpp | 10 --- test/spatial_index_tests.cpp | 71 ++++++++++++++++++++- 4 files changed, 178 insertions(+), 15 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index d589263..08f36c1 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -84,7 +84,8 @@ class SpatialIndex : Box region = transformedPair.first; - switch (query->mode()) { + 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; @@ -127,9 +128,13 @@ class SpatialIndex : break; default: - std::cout << "SpatialIndex: Mode " << int(query->mode()) << " not implemented." << '\n'; + 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) { @@ -307,6 +312,72 @@ class SpatialIndex : 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(); ++resultIt) + if (!ref.second->getGeometry()->within(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + + break; + } + case SpatialQueryType::NOT_WITHIN: + { + for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + if (ref.second->getGeometry()->within(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + + break; + } + + case SpatialQueryType::CONTAINS: + { + for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + if (!ref.second->getGeometry()->contains(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + + break; + } + case SpatialQueryType::NOT_CONTAINS: + { + for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + if (ref.second->getGeometry()->contains(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + + break; + } + + case SpatialQueryType::INTERSECTS: + { + for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + if (!ref.second->getGeometry()->intersects(resultIt->second->getGeometry())) + resultIt = results.erase(resultIt); + + break; + } + case SpatialQueryType::NOT_INTERSECTS: + { + for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + if (ref.second->getGeometry()->intersects(resultIt->second->getGeometry())) + resultIt = results.erase(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; diff --git a/include/sempr/query/SpatialIndexQuery.hpp b/include/sempr/query/SpatialIndexQuery.hpp index 521b11a..1d7d87f 100644 --- a/include/sempr/query/SpatialIndexQuery.hpp +++ b/include/sempr/query/SpatialIndexQuery.hpp @@ -106,6 +106,26 @@ class SpatialIndexQuery : public Query, public core::OType< SpatialIndexQuerygetCS(); + 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 createBoxQuery(entity::Geometry::Ptr geometry, SpatialQueryType type) { diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 8b593af..6164e4e 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -180,16 +180,6 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) core.answerQuery(queryNDS); BOOST_CHECK_EQUAL(queryNDS->results.size(), 3); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. - - /* - auto queryWithinBox = SpatialIndexQuery::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); - core.answerQuery(queryIntersecBox); - BOOST_CHECK_EQUAL(queryIntersecBox->results.size(), 1); - */ } diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 070d29b..9f38989 100644 --- a/test/spatial_index_tests.cpp +++ b/test/spatial_index_tests.cpp @@ -30,7 +30,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) LocalCS::Ptr cs(new LocalCS()); - SpatialIndex<3>::Ptr index(new SpatialIndex<3>(cs)); + SpatialIndex3D::Ptr index(new SpatialIndex<3>(cs)); core.addModule(index); @@ -41,16 +41,81 @@ BOOST_AUTO_TEST_SUITE(spatial_index) mp->setCS(cs); core.addEntity(mp); - auto queryWithinBox = SpatialIndexQuery<3>::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<3>::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); } + /* + 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; + + 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(1, 10); coords.push_back(coord); + coord = geos::geom::Coordinate(5, 13); coords.push_back(coord); + coord = geos::geom::Coordinate(4, 8); coords.push_back(coord); + coord = geos::geom::Coordinate(5, 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(3.5, 8); coords.push_back(coord); + coord = geos::geom::Coordinate(7, 14); coords.push_back(coord); + coord = geos::geom::Coordinate(7, 0); coords.push_back(coord); + coord = geos::geom::Coordinate(3.5, 8); 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(), 1); // shall be 0. but geos believes that they are intersecting each other. + //ToDo: Check this in detail. Its possible that geos does not like concave polygons! + + } + BOOST_AUTO_TEST_CASE(spatial_index_complex) { Core core; From af716a0025f03407e9aa845e622c1b62af10c7dd Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 24 Sep 2018 13:08:30 +0200 Subject: [PATCH 51/64] Fix geometry check for Spatial Queries and update unit test --- include/sempr/processing/SpatialIndex.hpp | 27 +++++++++++++++++------ test/spatial_index_tests.cpp | 20 ++++++++--------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 08f36c1..380e027 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -322,51 +322,64 @@ class SpatialIndex : { case SpatialQueryType::WITHIN: { - for (auto resultIt = results.begin(); resultIt != results.end(); ++resultIt) + 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(); ++resultIt) + 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(); ++resultIt) + 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(); ++resultIt) + 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(); ++resultIt) + 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(); ++resultIt) + for (auto resultIt = results.begin(); resultIt != results.end();) if (ref.second->getGeometry()->intersects(resultIt->second->getGeometry())) resultIt = results.erase(resultIt); + else + ++resultIt; break; } diff --git a/test/spatial_index_tests.cpp b/test/spatial_index_tests.cpp index 9f38989..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; @@ -80,10 +81,10 @@ BOOST_AUTO_TEST_SUITE(spatial_index) geos::geom::Coordinate coord; coord = geos::geom::Coordinate(1, 4); coords.push_back(coord); - coord = geos::geom::Coordinate(1, 10); coords.push_back(coord); - coord = geos::geom::Coordinate(5, 13); coords.push_back(coord); - coord = geos::geom::Coordinate(4, 8); coords.push_back(coord); - coord = geos::geom::Coordinate(5, 1); 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); @@ -96,10 +97,10 @@ BOOST_AUTO_TEST_SUITE(spatial_index) std::vector coords; geos::geom::Coordinate coord; - coord = geos::geom::Coordinate(3.5, 8); coords.push_back(coord); - coord = geos::geom::Coordinate(7, 14); coords.push_back(coord); - coord = geos::geom::Coordinate(7, 0); coords.push_back(coord); - coord = geos::geom::Coordinate(3.5, 8); coords.push_back(coord); // close + 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); } @@ -111,8 +112,7 @@ BOOST_AUTO_TEST_SUITE(spatial_index) auto queryIntersection = SpatialIndexQuery2D::intersects(right); core.answerQuery(queryIntersection); - BOOST_CHECK_EQUAL(queryIntersection->results.size(), 1); // shall be 0. but geos believes that they are intersecting each other. - //ToDo: Check this in detail. Its possible that geos does not like concave polygons! + BOOST_CHECK_EQUAL(queryIntersection->results.size(), 0); // shall be 0. } From 432cfe1fd9893fbb2517bca82a454c3581e59e75 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 24 Sep 2018 17:07:19 +0200 Subject: [PATCH 52/64] SpatialConclusion for 2D and 3D. Add SpatialThing as example entity. --- include/sempr/entity/example/SpatialThing.hpp | 36 +++ .../sempr/processing/SpatialConclusion.hpp | 148 ++++++------ include/sempr/processing/SpatialIndex.hpp | 4 +- src/entity/example/SpatialThing.cpp | 22 ++ src/processing/SpatialConclusion.cpp | 217 ------------------ test/spatial_conclusion_tests.cpp | 53 +++-- test/test_utils.hpp | 1 + 7 files changed, 161 insertions(+), 320 deletions(-) create mode 100644 include/sempr/entity/example/SpatialThing.hpp create mode 100644 src/entity/example/SpatialThing.cpp diff --git a/include/sempr/entity/example/SpatialThing.hpp b/include/sempr/entity/example/SpatialThing.hpp new file mode 100644 index 0000000..7c5aed9 --- /dev/null +++ b/include/sempr/entity/example/SpatialThing.hpp @@ -0,0 +1,36 @@ +#ifndef SEMPR_ENTITY_SPATIALTHING_H_ +#define SEMPR_ENTITY_SPATIALTHING_H_ + +#include +#include +#include + +#include + +namespace sempr { namespace entity { + + +#pragma db object +class SpatialThing : public Entity { + SEMPR_ENTITY +public: + using Ptr = std::shared_ptr; + + SpatialThing(); + SpatialThing(const core::IDGenBase*); + + Polygon::Ptr& geometry() { return polygon_; } + + std::string& type() { return type_; } + +protected: + friend class odb::access; + + std::string type_; + Polygon::Ptr polygon_; +}; + +}} + + +#endif /* end of include guard: SEMPR_ENTITY_SPATIALTHING_H_ */ diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 4ec3f33..cb9495a 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -29,14 +29,10 @@ namespace sempr { namespace processing { * * @prefix geo: * @prefix ogc: - * ogc:sfEquals - * ogc:sfDisjoint //b - * ogc:sfIntersects //b - * ogc:sfTouches //? - * ogc:sfWithin //b - * ogc:sfContains //b - * ogc:sfOverlaps //b - * ogc:sfCrosses //only for multipoint/polygon, multipoint/linestring, linestring/linestring, linestring/polygon, and linestring/multipolygon comparisons. + * ogc:sfIntersects + * ogc:sfWithin + * ogc:sfContains + * ogc:sfOverlaps * * @prefix spatial: * spatial:north @@ -44,7 +40,7 @@ namespace sempr { namespace processing { * spatial:west * spatial:east * - * Note: To fullfill the GeoSPARQL Entity the SpatialEntity have to be makred in RDF as SubClassOf ogc:SpatialObject and the depending geometry as ogc:Geometry + * Note: To fullfill the GeoSPARQL Entity 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 * @@ -55,7 +51,12 @@ namespace sempr { namespace processing { * below * on (like above but connected!) * under (like below but connected!) - * + * + * 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 object pointer. * @@ -63,17 +64,19 @@ namespace sempr { namespace processing { */ -template +template < std::size_t dim, class SpatialEntity> class SpatialConclusion : public Module< core::EntityEvent, core::EntityEvent > { public: - using Ptr = std::shared_ptr< SpatialConclusion >; + using Ptr = std::shared_ptr< SpatialConclusion >; + + typedef typename processing::SpatialIndexBase::ValuePair ValuePair; // isGlobal is set if both geometries are transformed in the same global reference system - typedef std::function::Box& self,const SpatialIndex<3>::Box& other, bool isGlobal)> CheckBoxFunction; - typedef std::function CheckGeometryFunction; + //typedef std::function::Box& self,const SpatialIndex::Box& other, bool isGlobal)> CheckBoxFunction; + typedef std::function CheckFunction; - SpatialConclusion(const std::weak_ptr< SpatialIndex<3> >& spatialIndex) : + SpatialConclusion(const std::weak_ptr< SpatialIndex >& spatialIndex) : index_(spatialIndex) { globalRoot_ = false; @@ -116,19 +119,19 @@ class SpatialConclusion : public Module< core::EntityEvent, core: // todo found and update the based enities } - void registerCheckFunction(const std::string& relationPredicate, const CheckBoxFunction& checker) + void registerCheckFunction(const std::string& relationPredicate, const CheckFunction& checker) { - checkBoxFunctions_[relationPredicate] = checker; + checkFunctions_[relationPredicate] = checker; } - void registerCheckFunction(const std::string& relationPredicate, const CheckGeometryFunction& checker) + entity::RDFVector::Ptr getConclusion(const std::shared_ptr& entity) { - checkGeomFunctions_[relationPredicate] = checker; + return rdfMap_.at(entity->id()); } private: - std::weak_ptr< SpatialIndex<3> > index_; + std::weak_ptr< SpatialIndex > index_; std::map spatialGeometry_; @@ -136,8 +139,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: bool globalRoot_; - std::map checkBoxFunctions_; - std::map checkGeomFunctions_; + std::map checkFunctions_; void removeEntity(const std::shared_ptr& entity) @@ -212,7 +214,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: void removeBackRelation(const std::string& id) { - std::string objID = sempr::baseURI() + id; + std::string objID = "<" + sempr::baseURI() + id + ">"; for (auto rdfIt = rdfMap_.begin(); rdfIt != rdfMap_.end(); ++rdfIt) { @@ -249,66 +251,38 @@ class SpatialConclusion : public Module< core::EntityEvent, core: auto idx = index_.lock(); if (idx) { - auto selfBox = idx->geo2box_[self].first; + 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 = checkBoxFunctions_.begin(); checkBoxIt != checkBoxFunctions_.end(); ++checkBoxIt) - { - - for (auto other : idx->geo2box_) - { - //check from self to others - bool selfRelated = checkBoxIt->second(selfBox, other.second.first, globalRoot_); - if (selfRelated) - { - // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t(sempr::baseURI() + id, checkBoxIt->first, sempr::baseURI() + spatialGeometry_.at(other.first)); - rdfMap_[id]->addTriple(t, true); - } - - //check from others to self - bool otherRelated = checkBoxIt->second(other.second.first, selfBox, globalRoot_); - if (otherRelated) - { - auto otherID = spatialGeometry_.at(other.first); - // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t(sempr::baseURI() + otherID, checkBoxIt->first, sempr::baseURI() + id); - rdfMap_[otherID]->addTriple(t, true); - changedRDF.insert(rdfMap_[otherID]); //mark vactor as changed - } - } - - } - - - auto selfGeometry = idx->geo2box_[self].second; - - // check every registered geom check function - for (auto checkGeomIt = checkGeomFunctions_.begin(); checkGeomIt != checkGeomFunctions_.end(); ++checkGeomIt) + for (auto checkBoxIt = checkFunctions_.begin(); checkBoxIt != checkFunctions_.end(); ++checkBoxIt) { for (auto other : idx->geo2box_) { //check from self to others - bool selfRelated = checkGeomIt->second(selfGeometry, other.second.second, globalRoot_); + bool selfRelated = checkBoxIt->second(selfPair, other.second, globalRoot_); if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t(sempr::baseURI() + id, checkGeomIt->first, sempr::baseURI() + spatialGeometry_.at(other.first)); + entity::Triple t( "<" + sempr::baseURI() + id + ">", + "<" + checkBoxIt->first + ">", + "<" + sempr::baseURI() + spatialGeometry_.at(other.first) + ">"); rdfMap_[id]->addTriple(t, true); } //check from others to self - bool otherRelated = checkGeomIt->second(other.second.second, selfGeometry, globalRoot_); + bool otherRelated = checkBoxIt->second(other.second, selfPair, globalRoot_); if (otherRelated) { auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t(sempr::baseURI() + otherID, checkGeomIt->first, sempr::baseURI() + id); + entity::Triple t( "<" + sempr::baseURI() + otherID+ ">", + "<" + checkBoxIt->first + ">", + "<" + sempr::baseURI() + id+ ">"); rdfMap_[otherID]->addTriple(t, true); - changedRDF.insert(rdfMap_[otherID]); //mark vactor as changed + changedRDF.insert(rdfMap_[otherID]); //mark vector as changed } } @@ -330,17 +304,17 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } - static bool check2D(const SpatialIndex<3>::Box& test) - { - double h = abs(test.min_corner().get<2>() - test.max_corner().get<2>()); - return h < 0.00001; // objects with a high less than 0.1mm - } - static bool checkNorthOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) + static bool checkNorthOf(const ValuePair& self, const ValuePair& other, bool isGlobal) { if (isGlobal) { - return self.min_corner().get<0>() >= other.max_corner().get<0>(); // x coordinate is lat (only in WGS84) + auto selfBox = self.first; + auto selfMinX = bg::get<0>(selfBox.min_corner()); + + auto otherBox = other.first; + auto otherMaxX = bg::get<0>(otherBox.max_corner()); + return selfMinX >= otherMaxX; // x coordinate is lat (only in WGS84) } else { @@ -348,11 +322,16 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkSouthOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) + static bool checkSouthOf(const ValuePair& self, const ValuePair& other, bool isGlobal) { if (isGlobal) { - return self.max_corner().get<0>() <= other.min_corner().get<0>(); // x coordinate is lat (only in WGS84) + auto selfBox = self.first; + auto selfMaxX = bg::get<0>(selfBox.max_corner()); + + auto otherBox = other.first; + auto otherMinX = bg::get<0>(otherBox.min_corner()); + return selfMaxX <= otherMinX; // x coordinate is lat (only in WGS84) } else { @@ -360,11 +339,16 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkEastOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) + static bool checkEastOf(const ValuePair& self, const ValuePair& other, bool isGlobal) { if (isGlobal) { - return self.min_corner().get<1>() >= other.max_corner().get<1>(); // y coordinate is lon (only in WGS84) + auto selfBox = self.first; + auto selfMinY = bg::get<1>(selfBox.min_corner()); + + auto otherBox = other.first; + auto otherMaxY = bg::get<1>(otherBox.max_corner()); + return selfMinY >= otherMaxY; // y coordinate is lon (only in WGS84) } else { @@ -372,11 +356,16 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } } - static bool checkWestOf(const SpatialIndex<3>::Box& self, const SpatialIndex<3>::Box& other, bool isGlobal) + static bool checkWestOf(const ValuePair& self, const ValuePair& other, bool isGlobal) { if (isGlobal) { - return self.max_corner().get<1>() <= other.min_corner().get<1>(); // y coordinate is lon (only in WGS84) + auto selfBox = self.first; + auto selfMaxY = bg::get<1>(selfBox.max_corner()); + + auto otherBox = other.first; + auto otherMinY = bg::get<1>(otherBox.min_corner()); + return selfMaxY <= otherMinY; // y coordinate is lon (only in WGS84) } else { @@ -385,10 +374,15 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } - }; +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 380e027..f1135ec 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -28,7 +28,7 @@ namespace sempr { namespace processing { namespace bg = boost::geometry; namespace bgi = boost::geometry::index; -template +template < std::size_t dim, class SpatialEntity> class SpatialConclusion; @@ -151,7 +151,7 @@ class SpatialIndex : const std::map& getGeoBoxes() const { return geo2box_; } private: - template + template < std::size_t dimension, class SpatialEntity> friend class SpatialConclusion; /** diff --git a/src/entity/example/SpatialThing.cpp b/src/entity/example/SpatialThing.cpp new file mode 100644 index 0000000..68cf2f7 --- /dev/null +++ b/src/entity/example/SpatialThing.cpp @@ -0,0 +1,22 @@ +#include +#include + + +namespace sempr { namespace entity { + +SEMPR_ENTITY_SOURCE(SpatialThing) + +SpatialThing::SpatialThing(const core::IDGenBase* idgen) + : Entity(idgen) +{ + setDiscriminator(); +} + +SpatialThing::SpatialThing() : + SpatialThing(new core::IDGen()) +{ +} + + +} /* entity */ +} /* sempr */ diff --git a/src/processing/SpatialConclusion.cpp b/src/processing/SpatialConclusion.cpp index 4258663..8fb6e70 100644 --- a/src/processing/SpatialConclusion.cpp +++ b/src/processing/SpatialConclusion.cpp @@ -8,222 +8,5 @@ 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()) { - casimportere 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); -}SpatialConclusion - -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/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 6164e4e..8c2881b 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -38,11 +38,11 @@ std::vector getOsnaCoords() { std::vector osnaCorner; - osnaCorner.emplace_back(53.029056, 8.858612); + 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.245902, 8.087810); - osnaCorner.emplace_back(53.029056, 8.858612); //close the ring + osnaCorner.emplace_back(52.245900, 8.087793); + osnaCorner.emplace_back(52.245902, 8.087810); //close the ring return osnaCorner; } @@ -146,40 +146,45 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) SpatialIndex2D::Ptr index(new SpatialIndex2D(globalCS)); core.addModule(index); - //SpatialConclusion::Ptr conclusion(new SpatialConclusion(index)); - //core.addModule(conclusion); + SpatialConclusion2D::Ptr conclusion(new SpatialConclusion2D(index)); + core.addModule(conclusion); - //build up a quadrangle - { - Polygon::Ptr osna( new Polygon() ); - osna->setCoordinates(getOsnaCoords()); - osna->setCS(globalCS); - core.addEntity(osna); - } + SopranoModule::Ptr soprano(new SopranoModule()); + core.addModule(soprano); + SpatialThing::Ptr osna( new SpatialThing() ); { - Polygon::Ptr bremen( new Polygon() ); - bremen->setCoordinates(getBremenCoords()); - bremen->setCS(globalCS); - core.addEntity(bremen); + 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); + SpatialThing::Ptr bremen( new SpatialThing() ); { - Polygon::Ptr vechta( new Polygon() ); - vechta->setCoordinates(getVechtaCoords()); - vechta->setCS(globalCS); - core.addEntity(vechta); + 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 nds( new Polygon() ); nds->setCoordinates(getNDSCoords()); nds->setCS(globalCS); - //auto queryNDS = SpatialIndexQuery2D::containsBoxOf(nds); - auto queryNDS = SpatialIndexQuery2D::withinBoxOf(nds); - //auto queryNDS = SpatialIndexQuery2D::intersectsBoxOf(nds); + auto queryNDS = SpatialIndexQuery2D::intersects(nds); core.answerQuery(queryNDS); - BOOST_CHECK_EQUAL(queryNDS->results.size(), 3); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. + BOOST_CHECK_EQUAL(queryNDS->results.size(), 1); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. + + auto osnaContext = conclusion->getConclusion(osna); + + BOOST_CHECK_EQUAL(osnaContext->size(), 2); // Osna is in the south west of bremen - so there shall be two triple. } diff --git a/test/test_utils.hpp b/test/test_utils.hpp index 1dbbc70..d7da7b7 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -37,6 +37,7 @@ #include #include +#include // reference systems From 5b191e11b853ec4c4bc70b65b52d369915016260 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Wed, 26 Sep 2018 11:32:20 +0200 Subject: [PATCH 53/64] Fix shortcut in RDF Triple and add test for a SpatialConclusion based SPARQL Query --- .../sempr/processing/SpatialConclusion.hpp | 31 +++++++++++++------ test/spatial_conclusion_tests.cpp | 24 ++++++++++++-- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index cb9495a..217c805 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -119,8 +119,11 @@ class SpatialConclusion : public Module< core::EntityEvent, core: // todo found and update the based enities } + // 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! void registerCheckFunction(const std::string& relationPredicate, const CheckFunction& checker) { + // ToDo: check that the pedicate is a complete uri and now shortcut! checkFunctions_[relationPredicate] = checker; } @@ -266,9 +269,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t( "<" + sempr::baseURI() + id + ">", - "<" + checkBoxIt->first + ">", - "<" + sempr::baseURI() + spatialGeometry_.at(other.first) + ">"); + entity::Triple t( toURI(id), + checkBoxIt->first, + toURI(spatialGeometry_.at(other.first)) ); rdfMap_[id]->addTriple(t, true); } @@ -278,9 +281,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: { auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t( "<" + sempr::baseURI() + otherID+ ">", - "<" + checkBoxIt->first + ">", - "<" + sempr::baseURI() + id+ ">"); + entity::Triple t( toURI(otherID), + checkBoxIt->first, + toURI(id) ); rdfMap_[otherID]->addTriple(t, true); changedRDF.insert(rdfMap_[otherID]); //mark vector as changed } @@ -295,16 +298,24 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } + // register the default set of check functions void initDefaultChecker() { - registerCheckFunction("spatial:north", checkNorthOf); - registerCheckFunction("spatial:south", checkSouthOf); - registerCheckFunction("spatial:east", checkEastOf); - registerCheckFunction("spatial:west", checkWestOf); + registerCheckFunction("", checkNorthOf); + registerCheckFunction("", checkSouthOf); + registerCheckFunction("", checkEastOf); + registerCheckFunction("", checkWestOf); } + std::string toURI(const std::string& id) + { + return "<" + sempr::baseURI() + id + ">"; + } + + //ToDo: Add checks for ogc:sfIntersects, ogc:sfWithin, ogc:sfContains, ogc:sfOverlaps + //ToDo: Checks the coordinate Systems for this conditions. For WGS84 and ENU/LTG the x axis points to the north. In ECEF its depends on the z axis and for a projection the y axis points to north and x to the east! static bool checkNorthOf(const ValuePair& self, const ValuePair& other, bool isGlobal) { if (isGlobal) diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 8c2881b..9d13d51 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -163,6 +163,17 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) } core.addEntity(osna); + SpatialThing::Ptr vechta( new SpatialThing() ); + { + 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); + SpatialThing::Ptr bremen( new SpatialThing() ); { Polygon::Ptr bremenPolygon( new Polygon() ); @@ -180,11 +191,18 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) auto queryNDS = SpatialIndexQuery2D::intersects(nds); core.answerQuery(queryNDS); - BOOST_CHECK_EQUAL(queryNDS->results.size(), 1); // Osna and Bremen are in NDS if the query use a box. But in real Bremen is no part of NDS. + 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. - auto osnaContext = conclusion->getConclusion(osna); + SPARQLQuery::Ptr query(new SPARQLQuery()); + query->prefixes["spatial"] = "http://jena.apache.org/spatial#"; + query->query = "SELECT ?o WHERE { ?o spatial:south " + sempr::baseURI()+ bremen->id() + " . }"; + core.answerQuery(query); - BOOST_CHECK_EQUAL(osnaContext->size(), 2); // Osna is in the south west of bremen - so there shall be two triple. + BOOST_CHECK_EQUAL(query->results.size(), 2); // Vechta and Osnabrück are in the south of Bremen. } From 28e89b3344dee351d8629f94da49fa956093baa5 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Wed, 26 Sep 2018 12:26:22 +0200 Subject: [PATCH 54/64] check URI and RDF Shortcut in SpatialConclusion --- include/sempr/core/RDF.hpp | 103 +++++++++--------- .../sempr/processing/SpatialConclusion.hpp | 59 +++++++--- src/core/RDF.cpp | 20 ++++ test/spatial_conclusion_tests.cpp | 12 +- 4 files changed, 129 insertions(+), 65 deletions(-) diff --git a/include/sempr/core/RDF.hpp b/include/sempr/core/RDF.hpp index b99e380..813476c 100644 --- a/include/sempr/core/RDF.hpp +++ b/include/sempr/core/RDF.hpp @@ -7,63 +7,66 @@ namespace sempr { const std::string& baseURI(); + const std::string buildURI(const std::string& id); + const std::string extractID(const std::string& uri); + 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 { + # 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 { + # 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/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 217c805..4213a46 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -121,10 +121,16 @@ class SpatialConclusion : public Module< core::EntityEvent, core: // 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! - void registerCheckFunction(const std::string& relationPredicate, const CheckFunction& checker) + bool registerCheckFunction(const std::string& relationPredicate, const CheckFunction& checker) { - // ToDo: check that the pedicate is a complete uri and now shortcut! - checkFunctions_[relationPredicate] = checker; + if (checkURI(relationPredicate)) + { + checkFunctions_[relationPredicate] = checker; + return true; + } else + { + return false; + } } entity::RDFVector::Ptr getConclusion(const std::shared_ptr& entity) @@ -217,7 +223,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: void removeBackRelation(const std::string& id) { - std::string objID = "<" + sempr::baseURI() + id + ">"; + std::string objURI = sempr::buildURI(id); for (auto rdfIt = rdfMap_.begin(); rdfIt != rdfMap_.end(); ++rdfIt) { @@ -226,7 +232,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: for (auto tIt = rdfIt->second->begin(); tIt != rdfIt->second->end(); ++tIt) { // search for object id in back linked rfd triple - if ((*tIt).object == objID) + if ((*tIt).object == objURI) { toRemove.emplace_back(*tIt); } @@ -269,9 +275,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t( toURI(id), + entity::Triple t( sempr::buildURI(id), checkBoxIt->first, - toURI(spatialGeometry_.at(other.first)) ); + sempr::buildURI(spatialGeometry_.at(other.first)) ); rdfMap_[id]->addTriple(t, true); } @@ -281,9 +287,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: { auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t( toURI(otherID), + entity::Triple t( sempr::buildURI(otherID), checkBoxIt->first, - toURI(id) ); + sempr::buildURI(id) ); rdfMap_[otherID]->addTriple(t, true); changedRDF.insert(rdfMap_[otherID]); //mark vector as changed } @@ -298,6 +304,36 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } + 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() { @@ -307,11 +343,6 @@ class SpatialConclusion : public Module< core::EntityEvent, core: registerCheckFunction("", checkWestOf); } - std::string toURI(const std::string& id) - { - return "<" + sempr::baseURI() + id + ">"; - } - //ToDo: Add checks for ogc:sfIntersects, ogc:sfWithin, ogc:sfContains, ogc:sfOverlaps diff --git a/src/core/RDF.cpp b/src/core/RDF.cpp index 0240110..ed401ae 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) { + return std::string( "<" + baseURI() + id + ">" ); + } + + /** + Extract the ID of a given sempr URI + */ + const std::string extractID(const std::string& uri) { + 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/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 9d13d51..703cdc9 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -137,6 +137,16 @@ std::vector getNDSCoords() BOOST_AUTO_TEST_SUITE(spatial_conclusion) std::string dbfile = "test_spatial_conclusion_sqlite.db"; + BOOST_AUTO_TEST_CASE(spatial_conclusion_id_uri) + { + SpatialThing::Ptr farfaraway( new SpatialThing() ); + + auto uri = sempr::buildURI(farfaraway->id()); + auto id = sempr::extractID(uri); + + BOOST_CHECK_EQUAL(id, farfaraway->id()); + } + BOOST_AUTO_TEST_CASE(spatial_conclusion_cities) { Core core; @@ -199,7 +209,7 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) SPARQLQuery::Ptr query(new SPARQLQuery()); query->prefixes["spatial"] = "http://jena.apache.org/spatial#"; - query->query = "SELECT ?o WHERE { ?o spatial:south " + sempr::baseURI()+ bremen->id() + " . }"; + query->query = "SELECT ?o WHERE { ?o spatial:south " + sempr::buildURI(bremen->id()) + " . }"; core.answerQuery(query); BOOST_CHECK_EQUAL(query->results.size(), 2); // Vechta and Osnabrück are in the south of Bremen. From f0c0dc56c9ea3931108b822fdf7bd8314fa27dea Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Thu, 4 Oct 2018 15:01:56 +0200 Subject: [PATCH 55/64] Add generic cardinal direction checker for WGS84 --- .../sempr/processing/SpatialConclusion.hpp | 116 +++++------------- 1 file changed, 34 insertions(+), 82 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 4213a46..50309b8 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -40,7 +40,7 @@ namespace sempr { namespace processing { * spatial:west * spatial:east * - * Note: To fullfill the GeoSPARQL Entity the SpatialEntity have to be marked in RDF as SubClassOf ogc:SpatialObject and the depending geometry as ogc:Geometry + * Note: To fullfill the GeoSPARQL 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 * @@ -51,6 +51,7 @@ namespace sempr { namespace processing { * below * on (like above but connected!) * under (like below but connected!) + * perpendicular * * Relations in discussion: * ogc:sfEquals @@ -74,20 +75,11 @@ class SpatialConclusion : public Module< core::EntityEvent, core: // 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; + typedef std::function CheckFunction; SpatialConclusion(const std::weak_ptr< SpatialIndex >& spatialIndex) : index_(spatialIndex) { - globalRoot_ = false; - if (auto idx = index_.lock()) - { - auto globalTest = std::dynamic_pointer_cast(idx->rootCS_); - if(globalTest) - globalRoot_ = true; - } - - initDefaultChecker(); }; @@ -146,8 +138,6 @@ class SpatialConclusion : public Module< core::EntityEvent, core: std::map rdfMap_; // temporary rdf vector for every entity (mapped by id) - bool globalRoot_; - std::map checkFunctions_; @@ -250,7 +240,6 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } - } @@ -271,7 +260,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: for (auto other : idx->geo2box_) { //check from self to others - bool selfRelated = checkBoxIt->second(selfPair, other.second, globalRoot_); + bool selfRelated = checkBoxIt->second(selfPair, other.second, index_.lock()->rootCS_); if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID @@ -282,14 +271,14 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } //check from others to self - bool otherRelated = checkBoxIt->second(other.second, selfPair, globalRoot_); + 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), checkBoxIt->first, - sempr::buildURI(id) ); + sempr::buildURI(id) ); rdfMap_[otherID]->addTriple(t, true); changedRDF.insert(rdfMap_[otherID]); //mark vector as changed } @@ -330,90 +319,53 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } catch (const std::out_of_range& oor) { return false; } - + return isShortcut; } // register the default set of check functions void initDefaultChecker() { - registerCheckFunction("", checkNorthOf); - registerCheckFunction("", checkSouthOf); - registerCheckFunction("", checkEastOf); - registerCheckFunction("", checkWestOf); + registerCheckFunction("", directionCheck<0>); + registerCheckFunction("", directionCheck<1>); + registerCheckFunction("", directionCheck<2>); + registerCheckFunction("", directionCheck<3>); } //ToDo: Add checks for ogc:sfIntersects, ogc:sfWithin, ogc:sfContains, ogc:sfOverlaps - - //ToDo: Checks the coordinate Systems for this conditions. For WGS84 and ENU/LTG the x axis points to the north. In ECEF its depends on the z axis and for a projection the y axis points to north and x to the east! - static bool checkNorthOf(const ValuePair& self, const ValuePair& other, bool isGlobal) + // A generic direction check function for north east south west relations. The direction is equal to the GlobalCS direction enum. + template + static bool directionCheck(const ValuePair& self, const ValuePair& other, const entity::SpatialReference::Ptr& ref) { - if (isGlobal) - { - auto selfBox = self.first; - auto selfMinX = bg::get<0>(selfBox.min_corner()); + auto globalRef = std::dynamic_pointer_cast(ref); - auto otherBox = other.first; - auto otherMaxX = bg::get<0>(otherBox.max_corner()); - return selfMinX >= otherMaxX; // x coordinate is lat (only in WGS84) - } - else - { - return false; - } - } + const std::size_t axis = direction % 2; // quick hack to get the dim of the direction for wgs84. - static bool checkSouthOf(const ValuePair& self, const ValuePair& other, bool isGlobal) - { - if (isGlobal) + if (globalRef) { - auto selfBox = self.first; - auto selfMaxX = bg::get<0>(selfBox.max_corner()); + bool positive = direction <= 1; // North and East are the positive directions - auto otherBox = other.first; - auto otherMinX = bg::get<0>(otherBox.min_corner()); - return selfMaxX <= otherMinX; // x coordinate is lat (only in WGS84) - } - else - { - return false; - } - } - - static bool checkEastOf(const ValuePair& self, const ValuePair& other, bool isGlobal) - { - if (isGlobal) - { auto selfBox = self.first; - auto selfMinY = bg::get<1>(selfBox.min_corner()); - auto otherBox = other.first; - auto otherMaxY = bg::get<1>(otherBox.max_corner()); - return selfMinY >= otherMaxY; // y coordinate is lon (only in WGS84) - } - else - { - return false; - } - } - - static bool checkWestOf(const ValuePair& self, const ValuePair& other, bool isGlobal) - { - if (isGlobal) - { - auto selfBox = self.first; - auto selfMaxY = bg::get<1>(selfBox.max_corner()); - auto otherBox = other.first; - auto otherMinY = bg::get<1>(otherBox.min_corner()); - return selfMaxY <= otherMinY; // y coordinate is lon (only in WGS84) - } - else - { - return false; + if (positive) + { + auto selfMin = bg::get(selfBox.min_corner()); + auto otherMax = bg::get(otherBox.max_corner()); + return selfMin >= otherMax; + } + else + { + auto selfMax = bg::get(selfBox.max_corner()); + auto otherMin = bg::get(otherBox.min_corner()); + return selfMax <= otherMin; + } } + + return false; } + //ToDo: Checks the coordinate Systems for this conditions. For WGS84 the x axis points to the north. In ECEF its depends on the z axis and for a projection and ENU/LTG the y axis points to north and x to the east! }; @@ -425,6 +377,6 @@ template using SpatialConclusion3D = SpatialConclusion<3, SpatialEntity>; - }} +}} #endif /* end of include guard SEMPR_PROCESSING_SPATIAL_CONCLUSION_HPP_ */ From ff818efa86b85ba576b0e81e1fb2321dc2ce1d0d Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Thu, 4 Oct 2018 17:38:06 +0200 Subject: [PATCH 56/64] DirectionCheck now supportes different global coordinate systems --- .../spatial/reference/EarthCenteredCS.hpp | 2 + .../entity/spatial/reference/GeodeticCS.hpp | 2 + .../entity/spatial/reference/GlobalCS.hpp | 23 ++++++++---- .../spatial/reference/LocalTangentPlaneCS.hpp | 2 + .../entity/spatial/reference/ProjectionCS.hpp | 1 + .../sempr/processing/SpatialConclusion.hpp | 37 +++++++++++++------ include/sempr/processing/SpatialIndexBase.hpp | 10 +++++ .../spatial/reference/EarthCenteredCS.cpp | 15 ++++++++ src/entity/spatial/reference/GeodeticCS.cpp | 13 +++++++ src/entity/spatial/reference/GlobalCS.cpp | 5 +++ .../spatial/reference/LocalTangentPlaneCS.cpp | 16 ++++++++ src/entity/spatial/reference/ProjectionCS.cpp | 15 ++++++++ 12 files changed, 122 insertions(+), 19 deletions(-) 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 0693a78..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,6 +26,21 @@ class GlobalCS : public SpatialReference { //from this to other virtual FilterList to(const GlobalCS::Ptr other) const; + enum CardinalDirection + { + NORTH, + EAST, + SOUTH, + WEST + }; + + /** + * @brief Find the dimension that representate the cardinal direction. + * + * @return std::size_t The dimension (X = 0, Y = 1, Z = 2) + */ + virtual std::size_t directionDimension(const CardinalDirection& direction) const; + protected: GlobalCS(); GlobalCS(const core::IDGenBase*); @@ -52,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/LocalTangentPlaneCS.hpp b/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp index 071576c..ef90ff4 100644 --- a/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp +++ b/include/sempr/entity/spatial/reference/LocalTangentPlaneCS.hpp @@ -23,6 +23,8 @@ class LocalTangentPlaneCS : public GeocentricCS { 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/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/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 50309b8..110060f 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -72,6 +72,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: using Ptr = std::shared_ptr< SpatialConclusion >; 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; @@ -326,24 +327,24 @@ class SpatialConclusion : public Module< core::EntityEvent, core: // register the default set of check functions void initDefaultChecker() { - registerCheckFunction("", directionCheck<0>); - registerCheckFunction("", directionCheck<1>); - registerCheckFunction("", directionCheck<2>); - registerCheckFunction("", directionCheck<3>); + registerCheckFunction("", directionCheck); + registerCheckFunction("", directionCheck); + registerCheckFunction("", directionCheck); + registerCheckFunction("", directionCheck); } //ToDo: Add checks for ogc:sfIntersects, ogc:sfWithin, ogc:sfContains, ogc:sfOverlaps - // A generic direction check function for north east south west relations. The direction is equal to the GlobalCS direction enum. - template + // 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); - const std::size_t axis = direction % 2; // quick hack to get the dim of the direction for wgs84. - 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; @@ -351,14 +352,14 @@ class SpatialConclusion : public Module< core::EntityEvent, core: if (positive) { - auto selfMin = bg::get(selfBox.min_corner()); - auto otherMax = bg::get(otherBox.max_corner()); + auto selfMin = getMin(selfBox, axis); + auto otherMax = getMax(otherBox, axis); return selfMin >= otherMax; } else { - auto selfMax = bg::get(selfBox.max_corner()); - auto otherMin = bg::get(otherBox.min_corner()); + auto selfMax = getMax(selfBox, axis); + auto otherMin = getMin(otherBox, axis); return selfMax <= otherMin; } } @@ -367,6 +368,18 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } //ToDo: Checks the coordinate Systems for this conditions. For WGS84 the x axis points to the north. In ECEF its depends on the z axis and for a projection and ENU/LTG the y axis points to north and x to the east! + 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]; + } + }; diff --git a/include/sempr/processing/SpatialIndexBase.hpp b/include/sempr/processing/SpatialIndexBase.hpp index efe521f..b9c12a9 100644 --- a/include/sempr/processing/SpatialIndexBase.hpp +++ b/include/sempr/processing/SpatialIndexBase.hpp @@ -74,6 +74,11 @@ struct SpatialIndexBase<2> 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; @@ -119,6 +124,11 @@ struct SpatialIndexBase<3> 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; 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 9f238aa..aa1a4e8 100644 --- a/src/entity/spatial/reference/GlobalCS.cpp +++ b/src/entity/spatial/reference/GlobalCS.cpp @@ -59,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/LocalTangentPlaneCS.cpp b/src/entity/spatial/reference/LocalTangentPlaneCS.cpp index 4c424f8..3447b3f 100644 --- a/src/entity/spatial/reference/LocalTangentPlaneCS.cpp +++ b/src/entity/spatial/reference/LocalTangentPlaneCS.cpp @@ -54,6 +54,22 @@ bool LocalTangentPlaneCS::isEqual(const SpatialReference::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/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) { From 9444e99cd52d786ba3e23d177360a5e56412be2d Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 9 Oct 2018 16:37:49 +0200 Subject: [PATCH 57/64] Add removeProperty for SemanticEntity --- include/sempr/entity/SemanticEntity.hpp | 37 +++++++++++++++++++++++-- src/entity/SemanticEntity.cpp | 16 +++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/include/sempr/entity/SemanticEntity.hpp b/include/sempr/entity/SemanticEntity.hpp index 41d3add..b048229 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,6 +252,8 @@ class SemanticEntity : public RDFEntity { #pragma db transient std::vector properties_; + std::vector::iterator findProperty(const RegisteredPropertyBase& prop); + protected: SemanticEntity(const core::IDGenBase*); SemanticEntity(); @@ -268,6 +272,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 +287,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 +301,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 +322,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/src/entity/SemanticEntity.cpp b/src/entity/SemanticEntity.cpp index 401da52..74f8a25 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) @@ -98,6 +105,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 { From 8dc12fb4290a4eb560784d13028d362c4233335c Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 9 Oct 2018 16:39:34 +0200 Subject: [PATCH 58/64] Rename and move SpatialThing to GeometricObject --- include/sempr/entity/GeometricObject.hpp | 57 ++++++++++++++++ include/sempr/entity/example/SpatialThing.hpp | 36 ---------- .../sempr/processing/SpatialConclusion.hpp | 65 ++++++++++++++++++- src/entity/GeometricObject.cpp | 36 ++++++++++ src/entity/example/SpatialThing.cpp | 22 ------- test/spatial_conclusion_tests.cpp | 16 ++--- test/test_utils.hpp | 2 +- 7 files changed, 164 insertions(+), 70 deletions(-) create mode 100644 include/sempr/entity/GeometricObject.hpp delete mode 100644 include/sempr/entity/example/SpatialThing.hpp create mode 100644 src/entity/GeometricObject.cpp delete mode 100644 src/entity/example/SpatialThing.cpp diff --git a/include/sempr/entity/GeometricObject.hpp b/include/sempr/entity/GeometricObject.hpp new file mode 100644 index 0000000..93c73d6 --- /dev/null +++ b/include/sempr/entity/GeometricObject.hpp @@ -0,0 +1,57 @@ +#ifndef SEMPR_ENTITY_GEOMETRICOBJECT_H_ +#define SEMPR_ENTITY_GEOMETRICOBJECT_H_ + +#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(); //ToDo Allow temporary!! + GeometricObject(const core::IDGenBase*); + + 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) { updateProperty(type_, "rdf:type", 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::vector related(const std::string& predicate = ""); + +private: + friend class odb::access; + + Geometry::Ptr geometry_; + + std::string type_; +/* + template + void updateProperty(T& ref, const std::string predicate, const T& value) + { + removeProperty(predicate, value); // will remove the first equal one! + + ref = value; + + registerProperty(predicate, value); + + changed(); + }*/ +}; + +}} + + +#endif /* end of include guard: SEMPR_ENTITY_GEOMETRICOBJECT_H_ */ diff --git a/include/sempr/entity/example/SpatialThing.hpp b/include/sempr/entity/example/SpatialThing.hpp deleted file mode 100644 index 7c5aed9..0000000 --- a/include/sempr/entity/example/SpatialThing.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef SEMPR_ENTITY_SPATIALTHING_H_ -#define SEMPR_ENTITY_SPATIALTHING_H_ - -#include -#include -#include - -#include - -namespace sempr { namespace entity { - - -#pragma db object -class SpatialThing : public Entity { - SEMPR_ENTITY -public: - using Ptr = std::shared_ptr; - - SpatialThing(); - SpatialThing(const core::IDGenBase*); - - Polygon::Ptr& geometry() { return polygon_; } - - std::string& type() { return type_; } - -protected: - friend class odb::access; - - std::string type_; - Polygon::Ptr polygon_; -}; - -}} - - -#endif /* end of include guard: SEMPR_ENTITY_SPATIALTHING_H_ */ diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 110060f..58cd17d 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -260,6 +260,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: 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) @@ -331,9 +334,13 @@ class SpatialConclusion : public Module< core::EntityEvent, core: registerCheckFunction("", directionCheck); registerCheckFunction("", directionCheck); registerCheckFunction("", directionCheck); - } - //ToDo: Add checks for ogc:sfIntersects, ogc:sfWithin, ogc:sfContains, ogc:sfOverlaps + 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. @@ -366,7 +373,59 @@ class SpatialConclusion : public Module< core::EntityEvent, core: return false; } - //ToDo: Checks the coordinate Systems for this conditions. For WGS84 the x axis points to the north. In ECEF its depends on the z axis and for a projection and ENU/LTG the y axis points to north and x to the east! + + 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) { diff --git a/src/entity/GeometricObject.cpp b/src/entity/GeometricObject.cpp new file mode 100644 index 0000000..5bf6551 --- /dev/null +++ b/src/entity/GeometricObject.cpp @@ -0,0 +1,36 @@ +#include +#include + + +namespace sempr { namespace entity { + +SEMPR_ENTITY_SOURCE(GeometricObject) + +GeometricObject::GeometricObject(const core::IDGenBase* idgen) + : SemanticEntity(idgen) +{ + setDiscriminator(); + + registerProperty("rdfs:subClassOf", ""); +} + +GeometricObject::GeometricObject() : + GeometricObject(new core::IDGen()) +{ +} + +void GeometricObject::geometry(const Geometry::Ptr geometry) +{ + if (geometry_) + removeProperty(geometry_->id(), "rdfs:subClassOf", ""); + + geometry_ = geometry; + + if (geometry_) // cover nullptr set case + registerProperty(geometry_->id(), "rdfs:subClassOf", ""); + + changed(); +} + +} /* entity */ +} /* sempr */ diff --git a/src/entity/example/SpatialThing.cpp b/src/entity/example/SpatialThing.cpp deleted file mode 100644 index 68cf2f7..0000000 --- a/src/entity/example/SpatialThing.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include - - -namespace sempr { namespace entity { - -SEMPR_ENTITY_SOURCE(SpatialThing) - -SpatialThing::SpatialThing(const core::IDGenBase* idgen) - : Entity(idgen) -{ - setDiscriminator(); -} - -SpatialThing::SpatialThing() : - SpatialThing(new core::IDGen()) -{ -} - - -} /* entity */ -} /* sempr */ diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 703cdc9..88546cd 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -139,7 +139,7 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) BOOST_AUTO_TEST_CASE(spatial_conclusion_id_uri) { - SpatialThing::Ptr farfaraway( new SpatialThing() ); + GeometricObject::Ptr farfaraway( new GeometricObject() ); auto uri = sempr::buildURI(farfaraway->id()); auto id = sempr::extractID(uri); @@ -156,42 +156,42 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) SpatialIndex2D::Ptr index(new SpatialIndex2D(globalCS)); core.addModule(index); - SpatialConclusion2D::Ptr conclusion(new SpatialConclusion2D(index)); + SpatialConclusion2D::Ptr conclusion(new SpatialConclusion2D(index)); core.addModule(conclusion); SopranoModule::Ptr soprano(new SopranoModule()); core.addModule(soprano); - SpatialThing::Ptr osna( new SpatialThing() ); + 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; + osna->geometry(osnaPolygon); } core.addEntity(osna); - SpatialThing::Ptr vechta( new SpatialThing() ); + 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; + vechta->geometry(vechtaPolygon); } core.addEntity(vechta); - SpatialThing::Ptr bremen( new SpatialThing() ); + 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; + bremen->geometry(bremenPolygon); } core.addEntity(bremen); diff --git a/test/test_utils.hpp b/test/test_utils.hpp index d7da7b7..d1fe062 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -37,7 +37,7 @@ #include #include -#include +#include // reference systems From a2c79f6688f54afdd23d7a0ea29b555cdaa4e621 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 15 Oct 2018 14:19:15 +0200 Subject: [PATCH 59/64] GeometricObject and SemanticEntity may be temporary and clean up the branch --- include/sempr/core/RDF.hpp | 7 +- include/sempr/entity/GeometricObject.hpp | 24 ++--- include/sempr/entity/SemanticEntity.hpp | 4 +- .../sempr/processing/SpatialConclusion.hpp | 87 ++++++++++++++----- include/sempr/processing/SpatialIndex.hpp | 2 +- src/core/RDF.cpp | 10 +-- src/entity/GeometricObject.cpp | 50 +++++++++-- src/entity/SemanticEntity.cpp | 17 ++-- src/processing/SpatialConclusion.cpp | 12 --- src/processing/SpatialIndex.cpp | 13 --- src/query/SpatialIndexQuery.cpp | 5 -- test/spatial_conclusion_tests.cpp | 32 +++++-- 12 files changed, 164 insertions(+), 99 deletions(-) delete mode 100644 src/processing/SpatialConclusion.cpp delete mode 100644 src/processing/SpatialIndex.cpp delete mode 100644 src/query/SpatialIndexQuery.cpp diff --git a/include/sempr/core/RDF.hpp b/include/sempr/core/RDF.hpp index 813476c..e9f2b74 100644 --- a/include/sempr/core/RDF.hpp +++ b/include/sempr/core/RDF.hpp @@ -3,12 +3,13 @@ #include #include +#include namespace sempr { const std::string& baseURI(); - const std::string buildURI(const std::string& id); - const std::string extractID(const std::string& uri); + 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 { @@ -22,6 +23,7 @@ namespace sempr { } namespace rdfs { + const std::string& baseURI(); # define RDFS(name) const std::string& (name)(); RDFS(baseURI) RDFS(Resource) @@ -34,6 +36,7 @@ namespace sempr { } namespace owl { + const std::string& baseURI(); # define OWL(name) const std::string& (name)(); OWL(baseURI) OWL(Class) diff --git a/include/sempr/entity/GeometricObject.hpp b/include/sempr/entity/GeometricObject.hpp index 93c73d6..316ad72 100644 --- a/include/sempr/entity/GeometricObject.hpp +++ b/include/sempr/entity/GeometricObject.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -17,19 +18,20 @@ class GeometricObject : public SemanticEntity { public: using Ptr = std::shared_ptr; - GeometricObject(); //ToDo Allow temporary!! - GeometricObject(const core::IDGenBase*); + 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) { updateProperty(type_, "rdf:type", type); } + void type(const std::string& type); - //void registerProperty(const std::string& predicate, const std::string& object); + 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::vector related(const std::string& predicate = ""); + std::set related(const std::string& predicate = ""); private: friend class odb::access; @@ -37,18 +39,6 @@ class GeometricObject : public SemanticEntity { Geometry::Ptr geometry_; std::string type_; -/* - template - void updateProperty(T& ref, const std::string predicate, const T& value) - { - removeProperty(predicate, value); // will remove the first equal one! - - ref = value; - - registerProperty(predicate, value); - - changed(); - }*/ }; }} diff --git a/include/sempr/entity/SemanticEntity.hpp b/include/sempr/entity/SemanticEntity.hpp index b048229..e58685d 100644 --- a/include/sempr/entity/SemanticEntity.hpp +++ b/include/sempr/entity/SemanticEntity.hpp @@ -255,8 +255,10 @@ class SemanticEntity : public RDFEntity { 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: diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 58cd17d..0ceddf1 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -109,7 +109,24 @@ class SpatialConclusion : public Module< core::EntityEvent, core: void process(core::EntityEvent::Ptr refEvent) override { - // todo found and update the based enities + 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; + } } // register a new check function for a given predicate like north or east. @@ -144,35 +161,56 @@ class SpatialConclusion : public Module< core::EntityEvent, core: void removeEntity(const std::shared_ptr& entity) { - rdfMap_[entity->id()]->removed(); // fire remove event + removeEntity(entity->id()); + } + + void removeEntity(const std::string& id) + { + rdfMap_[id]->removed(); // fire remove event - removeGeometryLink(entity->id()); + removeGeometryLink(id); - rdfMap_.erase(entity->id()); // remove rdf vector for the entity - removeBackRelation(entity->id()); // remove linked back relations ot his entity + 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) { - if (indexedGeometry(entity->geometry())) + 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_[entity->id()] = entity::RDFVector::Ptr(new entity::RDFVector(true)); // not persistant Entity - rdfMap_[entity->id()]->created(); + rdfMap_[id] = entity::RDFVector::Ptr(new entity::RDFVector(true)); // not persistant Entity + rdfMap_[id]->created(); } else { // changed Entity - rdfMap_[entity->id()]->clear(); - rdfMap_[entity->id()]->changed(); + rdfMap_[id]->clear(); + rdfMap_[id]->changed(); } - spatialGeometry_[entity->geometry()] = entity->id(); // build up geometry link - checkEntity(entity->id(), entity->geometry()); + spatialGeometry_[geometry] = id; // build up geometry link + checkEntity(id, geometry); } //otherwise skip this entity! - } bool indexedGeometry(const std::shared_ptr& geometry) @@ -200,21 +238,24 @@ class SpatialConclusion : public Module< core::EntityEvent, core: } 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(entity->id()); - removeBackRelation(entity->id()); - addEntity(entity, true); + removeGeometryLink(id); + removeBackRelation(id); + addEntity(geometry, id, true); } - void removeBackRelation(const std::string& id) { - std::string objURI = sempr::buildURI(id); + std::string objURI = sempr::buildURI(id, sempr::baseURI()); for (auto rdfIt = rdfMap_.begin(); rdfIt != rdfMap_.end(); ++rdfIt) { @@ -268,9 +309,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: if (selfRelated) { // Build Triple: SelfId, Function predicate, OtherID - entity::Triple t( sempr::buildURI(id), + entity::Triple t( sempr::buildURI(id, sempr::baseURI()), checkBoxIt->first, - sempr::buildURI(spatialGeometry_.at(other.first)) ); + sempr::buildURI(spatialGeometry_.at(other.first), sempr::baseURI()) ); rdfMap_[id]->addTriple(t, true); } @@ -280,9 +321,9 @@ class SpatialConclusion : public Module< core::EntityEvent, core: { auto otherID = spatialGeometry_.at(other.first); // Build Triple: OtherID, Function predicate, SelfId - entity::Triple t( sempr::buildURI(otherID), + entity::Triple t( sempr::buildURI(otherID, sempr::baseURI()), checkBoxIt->first, - sempr::buildURI(id) ); + sempr::buildURI(id, sempr::baseURI()) ); rdfMap_[otherID]->addTriple(t, true); changedRDF.insert(rdfMap_[otherID]); //mark vector as changed } diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index f1135ec..76d5dc9 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -195,7 +195,7 @@ class SpatialIndex : void process(core::EntityEvent::Ptr refEvent) override { - typedef core::EntityEvent::EventType EType; + typedef core::EntityEvent::EventType EType; //Will ony be could if the spatial reference itself is changed switch (refEvent->what()) { diff --git a/src/core/RDF.cpp b/src/core/RDF.cpp index ed401ae..15deca7 100644 --- a/src/core/RDF.cpp +++ b/src/core/RDF.cpp @@ -19,17 +19,17 @@ namespace sempr { /** Build a URI based on the sempr::baseURI and the given string in "" format. */ - const std::string buildURI(const std::string& id) { - return std::string( "<" + baseURI() + id + ">" ); + 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) { - auto pos = uri.find(baseURI()); + 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); + auto id = uri.substr(pos + baseURI.length(), uri.length()-pos-baseURI.length()-1); return id; } else diff --git a/src/entity/GeometricObject.cpp b/src/entity/GeometricObject.cpp index 5bf6551..ab24129 100644 --- a/src/entity/GeometricObject.cpp +++ b/src/entity/GeometricObject.cpp @@ -6,31 +6,65 @@ namespace sempr { namespace entity { SEMPR_ENTITY_SOURCE(GeometricObject) -GeometricObject::GeometricObject(const core::IDGenBase* idgen) - : SemanticEntity(idgen) +GeometricObject::GeometricObject() : + GeometricObject(new core::IDGen()) { - setDiscriminator(); +} - registerProperty("rdfs:subClassOf", ""); +GeometricObject::GeometricObject(bool temporary) : + GeometricObject(new core::IDGen(), temporary) +{ } -GeometricObject::GeometricObject() : - GeometricObject(new core::IDGen()) +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_) - removeProperty(geometry_->id(), "rdfs:subClassOf", ""); + SemanticEntity::removeProperty(sempr::buildURI(geometry_->id(), sempr::baseURI()), sempr::buildURI("subClassOf", sempr::core::rdfs::baseURI()), ""); geometry_ = geometry; if (geometry_) // cover nullptr set case - registerProperty(geometry_->id(), "rdfs:subClassOf", ""); + SemanticEntity::registerProperty(sempr::buildURI(geometry_->id(), sempr::baseURI()), sempr::buildURI("subClassOf", sempr::core::rdfs::baseURI()), ""); + + changed(); +} + +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_); changed(); } +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/SemanticEntity.cpp b/src/entity/SemanticEntity.cpp index 74f8a25..94833e6 100644 --- a/src/entity/SemanticEntity.cpp +++ b/src/entity/SemanticEntity.cpp @@ -86,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_) diff --git a/src/processing/SpatialConclusion.cpp b/src/processing/SpatialConclusion.cpp deleted file mode 100644 index 8fb6e70..0000000 --- a/src/processing/SpatialConclusion.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include -#include - -#include - - -namespace sempr { namespace processing { - - -}} diff --git a/src/processing/SpatialIndex.cpp b/src/processing/SpatialIndex.cpp deleted file mode 100644 index 3342364..0000000 --- a/src/processing/SpatialIndex.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include -#include -#include -#include - -#include - - -namespace sempr { namespace processing { - - -}} diff --git a/src/query/SpatialIndexQuery.cpp b/src/query/SpatialIndexQuery.cpp deleted file mode 100644 index ac12860..0000000 --- a/src/query/SpatialIndexQuery.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include - -namespace sempr { namespace query { - -}} diff --git a/test/spatial_conclusion_tests.cpp b/test/spatial_conclusion_tests.cpp index 88546cd..e8a7257 100644 --- a/test/spatial_conclusion_tests.cpp +++ b/test/spatial_conclusion_tests.cpp @@ -141,8 +141,8 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) { GeometricObject::Ptr farfaraway( new GeometricObject() ); - auto uri = sempr::buildURI(farfaraway->id()); - auto id = sempr::extractID(uri); + auto uri = sempr::buildURI(farfaraway->id(), sempr::baseURI()); + auto id = sempr::extractID(uri, sempr::baseURI()); BOOST_CHECK_EQUAL(id, farfaraway->id()); } @@ -195,10 +195,10 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) } core.addEntity(bremen); - Polygon::Ptr nds( new Polygon() ); - nds->setCoordinates(getNDSCoords()); - nds->setCS(globalCS); - auto queryNDS = SpatialIndexQuery2D::intersects(nds); + 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. @@ -209,11 +209,29 @@ BOOST_AUTO_TEST_SUITE(spatial_conclusion) 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()) + " . }"; + 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) From ff42d6818c926eef73b19dc145ce69362d1d06fc Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 16 Oct 2018 15:07:52 +0200 Subject: [PATCH 60/64] GeometricObject will not notify about changes of himself --- include/sempr/processing/SpatialConclusion.hpp | 2 +- include/sempr/processing/SpatialIndex.hpp | 2 +- src/entity/GeometricObject.cpp | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 0ceddf1..3dc1c9a 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -109,7 +109,7 @@ class SpatialConclusion : public Module< core::EntityEvent, core: void process(core::EntityEvent::Ptr refEvent) override { - typedef core::EntityEvent::EventType EType; //Will ony be could if the spatial reference itself is changed + typedef core::EntityEvent::EventType EType; // will ony be called if the spatial reference itself is changed switch (refEvent->what()) { diff --git a/include/sempr/processing/SpatialIndex.hpp b/include/sempr/processing/SpatialIndex.hpp index 76d5dc9..31535f5 100644 --- a/include/sempr/processing/SpatialIndex.hpp +++ b/include/sempr/processing/SpatialIndex.hpp @@ -195,7 +195,7 @@ class SpatialIndex : void process(core::EntityEvent::Ptr refEvent) override { - typedef core::EntityEvent::EventType EType; //Will ony be could if the spatial reference itself is changed + typedef core::EntityEvent::EventType EType; // will ony be could if the spatial reference itself is changed switch (refEvent->what()) { diff --git a/src/entity/GeometricObject.cpp b/src/entity/GeometricObject.cpp index ab24129..3c66134 100644 --- a/src/entity/GeometricObject.cpp +++ b/src/entity/GeometricObject.cpp @@ -33,8 +33,6 @@ void GeometricObject::geometry(const Geometry::Ptr geometry) if (geometry_) // cover nullptr set case SemanticEntity::registerProperty(sempr::buildURI(geometry_->id(), sempr::baseURI()), sempr::buildURI("subClassOf", sempr::core::rdfs::baseURI()), ""); - - changed(); } void GeometricObject::type(const std::string& type) @@ -44,8 +42,6 @@ void GeometricObject::type(const std::string& type) type_ = type; SemanticEntity::registerProperty(sempr::buildURI("type", sempr::core::rdf::baseURI()), type_); - - changed(); } void GeometricObject::registerProperty(const std::string& predicate, const std::string& object) From fb4328dd71a5748335cd2c02d846f61e4f198772 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Tue, 23 Oct 2018 11:45:56 +0200 Subject: [PATCH 61/64] Update SpatialConclusion comments --- include/sempr/processing/SpatialConclusion.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/sempr/processing/SpatialConclusion.hpp b/include/sempr/processing/SpatialConclusion.hpp index 3dc1c9a..b6dc92b 100644 --- a/include/sempr/processing/SpatialConclusion.hpp +++ b/include/sempr/processing/SpatialConclusion.hpp @@ -26,6 +26,7 @@ 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: @@ -40,7 +41,7 @@ namespace sempr { namespace processing { * spatial:west * spatial:east * - * Note: To fullfill the GeoSPARQL the SpatialEntity have to be marked in RDF as SubClassOf ogc:SpatialObject and the depending geometry as ogc:Geometry + * 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 * @@ -59,17 +60,17 @@ namespace sempr { namespace processing { * 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 object pointer. + * 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; From b5e47bcc7cdfbcedc6a269231e7a8eedcb967fd0 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Dec 2018 11:59:25 +0100 Subject: [PATCH 62/64] Fix CalculatePointsModule for changed SpatialIndex and update cmake version and log infos --- CMakeLists.txt | 2 +- cmake/UseODB.cmake | 2 +- src/processing/CalculatePointsModule.cpp | 30 ++++++++++++------------ 3 files changed, 17 insertions(+), 17 deletions(-) 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/src/processing/CalculatePointsModule.cpp b/src/processing/CalculatePointsModule.cpp index 713a374..76693a2 100644 --- a/src/processing/CalculatePointsModule.cpp +++ b/src/processing/CalculatePointsModule.cpp @@ -32,23 +32,23 @@ 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()); + auto spatial = query::SpatialIndexQuery3D::intersectsBoxOf(query->entity()); - // TODO: Use this coords for the calculatePoints() function + 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); + 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) { - 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,9 +59,9 @@ 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; } @@ -75,7 +75,7 @@ void CalculatePointsModule::calculatePoints(const entity::PointCloud::Ptr cloud, 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; } @@ -129,9 +129,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]); } } } From 6be6b222cf379ea87b983e62f9c2d31f706f9110 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Mon, 17 Dec 2018 15:14:50 +0100 Subject: [PATCH 63/64] Update comment --- include/sempr/entity/Triple.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sempr/entity/Triple.hpp b/include/sempr/entity/Triple.hpp index 848c738..3eab918 100644 --- a/include/sempr/entity/Triple.hpp +++ b/include/sempr/entity/Triple.hpp @@ -6,7 +6,7 @@ namespace sempr { namespace entity { #pragma db value -struct Triple { //deprecated could be replaced by the RDFValueTriple as class with an subject(), predicate(), object() getter for the strings. ToDo! +struct Triple { Triple() {} Triple(const std::string& s, const std::string& p, const std::string& o) From cf1bee1c023911fc669a724009819141f18605f6 Mon Sep 17 00:00:00 2001 From: Christoph Tieben Date: Wed, 20 Mar 2019 11:51:04 +0100 Subject: [PATCH 64/64] Fix nullptr exception in calculate pointclouds --- src/processing/CalculatePointsModule.cpp | 25 +++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/processing/CalculatePointsModule.cpp b/src/processing/CalculatePointsModule.cpp index 76693a2..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,21 +33,32 @@ void CalculatePointsModule::process(query::CalculatePointsQuery::Ptr query) cloud->loaded(); } + /* 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()); - auto coords = *(spatial->refBoxGeometryPair().second->getGeometry()->getCoordinates()->toVector()); + //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(spatial->refBoxGeometryPair().second)->setCoordinates(coords); + */ - ask(spatial); + //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") // could this be done by the dynmaic cast? { @@ -68,7 +80,6 @@ void CalculatePointsModule::process(query::CalculatePointsQuery::Ptr query) 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(); @@ -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]); }