diff --git a/externals/libdbglog b/externals/libdbglog index f779c83..3dfa84e 160000 --- a/externals/libdbglog +++ b/externals/libdbglog @@ -1 +1 @@ -Subproject commit f779c830d5ca85fc1be2dec3d0819635631498b7 +Subproject commit 3dfa84e6996f947cf149a3d79a96de0e8fa2e412 diff --git a/externals/libgeo b/externals/libgeo index d3e63de..f5822f0 160000 --- a/externals/libgeo +++ b/externals/libgeo @@ -1 +1 @@ -Subproject commit d3e63ded73e4b175f4e34c0cbbadb95edeedb92e +Subproject commit f5822f052d0c634a38465115d0c3fc9184ba6ea7 diff --git a/externals/vts-libs b/externals/vts-libs index fd677d6..408746b 160000 --- a/externals/vts-libs +++ b/externals/vts-libs @@ -1 +1 @@ -Subproject commit fd677d6180a06da72bc2a4a68bb8e8364ff79684 +Subproject commit 408746ba75d32189399ba7eec395805b767d7a14 diff --git a/mapproxy/src/mapproxy/CMakeLists.txt b/mapproxy/src/mapproxy/CMakeLists.txt index 6981c3e..66e1440 100644 --- a/mapproxy/src/mapproxy/CMakeLists.txt +++ b/mapproxy/src/mapproxy/CMakeLists.txt @@ -22,6 +22,7 @@ set(mapproxy-core_SOURCES support/mmapped/memory.hpp support/mmapped/memory-impl.hpp support/mmapped/tileflags.hpp support/mmapped/qtree-rasterize.hpp + support/atlas.hpp support/atlas.cpp support/mmapped/tilesetindex.hpp support/mmapped/tilesetindex.cpp heightfunction.hpp heightfunction.cpp @@ -47,6 +48,7 @@ set(mapproxy-core_SOURCES definition/surface.cpp definition/surface-spheroid.cpp definition/surface-dem.cpp + definition/surface-meta.hpp definition/surface-meta.cpp definition/geodata.hpp definition/geodata.cpp @@ -128,9 +130,12 @@ set(mapproxy_SOURCES generator.hpp generator/generator.cpp + generator/generators.hpp generator/generators.cpp + generator/registry.hpp generator/registry.cpp generator/factory.hpp generator/metatile.hpp generator/metatile.cpp generator/demregistry.hpp generator/demregistry.cpp + generator/providers.hpp # bound layers generator/tms-raster-base.hpp generator/tms-raster-base.cpp @@ -146,6 +151,7 @@ set(mapproxy_SOURCES generator/surface.hpp generator/surface.cpp generator/surface-spheroid.hpp generator/surface-spheroid.cpp generator/surface-dem.hpp generator/surface-dem.cpp + generator/surface-meta.hpp generator/surface-meta.cpp # free layers generator/geodatavectorbase.hpp generator/geodatavectorbase.cpp diff --git a/mapproxy/src/mapproxy/definition.hpp b/mapproxy/src/mapproxy/definition.hpp index 969c8d7..e7130a1 100644 --- a/mapproxy/src/mapproxy/definition.hpp +++ b/mapproxy/src/mapproxy/definition.hpp @@ -30,6 +30,7 @@ #include "definition/factory.hpp" #include "definition/tms.hpp" #include "definition/surface.hpp" +#include "definition/surface-meta.hpp" #include "definition/geodata.hpp" #include "definition/geodata-semantic.hpp" diff --git a/mapproxy/src/mapproxy/definition/surface-meta.cpp b/mapproxy/src/mapproxy/definition/surface-meta.cpp new file mode 100644 index 0000000..6280f25 --- /dev/null +++ b/mapproxy/src/mapproxy/definition/surface-meta.cpp @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2018 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "utility/premain.hpp" + +#include "jsoncpp/json.hpp" +#include "jsoncpp/as.hpp" + +#include "surface-meta.hpp" +#include "factory.hpp" + +namespace resource { + +constexpr char SurfaceMeta::driverName[]; + +namespace { + +utility::PreMain register_([]() { registerDefinition(); }); + +void parseDefinition(SurfaceMeta &def, const Json::Value &value) +{ + const auto &rid([](const Json::Value &item) -> Resource::Id + { + Resource::Id id; + Json::get(id.group, item, "group"); + Json::get(id.id, item, "id"); + return id; + }); + + def.surface = rid(Json::check(value["surface"], Json::objectValue)); + def.tms = rid(Json::check(value["tms"], Json::objectValue)); +} + +void buildDefinition(Json::Value &value, const SurfaceMeta &def) +{ + const auto &rid([](const Resource::Id &id) -> Json::Value + { + Json::Value value; + value["group"] = id.group; + value["id"] = id.id; + return value; + }); + + value["surface"] = rid(def.surface); + value["tms"] = rid(def.tms); +} + +} // namespace + +void SurfaceMeta::from_impl(const Json::Value &value) +{ + parseDefinition(*this, value); +} + +void SurfaceMeta::to_impl(Json::Value &value) const +{ + buildDefinition(value, *this); +} + +Changed SurfaceMeta::changed_impl(const DefinitionBase &o) const +{ + const auto &other(o.as()); + + if (surface != other.surface) { return Changed::yes; } + if (tms != other.tms) { return Changed::yes; } + + return Changed::no; +} + +Resource::Id::list SurfaceMeta::needsResources_impl() const { + return { surface, tms }; +} + +} // namespace resource diff --git a/mapproxy/src/mapproxy/definition/surface-meta.hpp b/mapproxy/src/mapproxy/definition/surface-meta.hpp new file mode 100644 index 0000000..cafacaa --- /dev/null +++ b/mapproxy/src/mapproxy/definition/surface-meta.hpp @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2019 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef mapproxy_definition_surface_meta_hpp_included_ +#define mapproxy_definition_surface_meta_hpp_included_ + +#include "../resource.hpp" + +// fwd +namespace Json { class Value; } + +namespace resource { + +// meta surface: combines existing surface with existing TMS + +class SurfaceMeta : public DefinitionBase { +public: + Resource::Id surface; + Resource::Id tms; + + static constexpr Resource::Generator::Type type + = Resource::Generator::Type::surface; + + static constexpr char driverName[] = "surface-meta"; + +protected: + virtual void from_impl(const Json::Value &value); + virtual void to_impl(Json::Value &value) const; + virtual Changed changed_impl(const DefinitionBase &other) const; + virtual Resource::Id::list needsResources_impl() const; + virtual bool needsRanges_impl() const { return false; } +}; + +} // namespace resource + +#endif // mapproxy_definition_surface_meta_hpp_included_ + diff --git a/mapproxy/src/mapproxy/fileinfo.cpp b/mapproxy/src/mapproxy/fileinfo.cpp index 55e8bd0..8d58da6 100644 --- a/mapproxy/src/mapproxy/fileinfo.cpp +++ b/mapproxy/src/mapproxy/fileinfo.cpp @@ -318,7 +318,8 @@ Sink::FileInfo TmsFileInfo::sinkFileInfo(std::time_t lastModified) const SurfaceFileInfo::SurfaceFileInfo(const FileInfo &fi) : fileInfo(fi), type(Type::unknown), fileType(vs::File::config) - , tileType(vts::TileFile::meta), flavor(vts::FileFlavor::regular) + , tileType(vts::TileFile::meta), subTileIndex() + , flavor(vts::FileFlavor::regular) , support(), registry(), serviceFile() { if (vts::fromFilename diff --git a/mapproxy/src/mapproxy/generator.hpp b/mapproxy/src/mapproxy/generator.hpp index db34306..187833c 100644 --- a/mapproxy/src/mapproxy/generator.hpp +++ b/mapproxy/src/mapproxy/generator.hpp @@ -39,6 +39,7 @@ #include #include "utility/resourcefetcher.hpp" +#include "utility/format.hpp" #include "vts-libs/storage/support.hpp" #include "vts-libs/vts/mapconfig.hpp" @@ -71,12 +72,28 @@ class GeneratorFinder { std::shared_ptr findGenerator(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const; + , const Resource::Id &resourceId + , bool mustBeReady = true) const; private: virtual std::shared_ptr findGenerator_impl(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const = 0; + , const Resource::Id &resourceId + , bool mustBeReady) const = 0; +}; + +struct GeneratorNotFound : std::runtime_error { + GeneratorNotFound(Resource::Generator::Type generatorType + , const Resource::Id &resourceId) + : std::runtime_error + (utility::format("Cannot find <%s> generator for resource <%s>." + , generatorType, resourceId)) + , generatorType(generatorType) + , resourceId(resourceId) + {} + + Resource::Generator::Type generatorType; + const Resource::Id &resourceId; }; /** Dataset generator. @@ -219,6 +236,16 @@ class Generator : boost::noncopyable { */ bool updatedSince(std::uint64_t timestamp) const; + /** Generic type for provider handling + */ + struct Provider { virtual ~Provider() {} }; + + /** Returns generators provider machinery. Returns null if provider of given + * type is not available. + */ + template + ProviderType* getProvider() const; + protected: Generator(const Params ¶ms, const Properties &props = Properties()); @@ -252,7 +279,9 @@ class Generator : boost::noncopyable { const; Generator::pointer otherGenerator(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const; + , const Resource::Id &resourceId + , bool mustBeReady = true + , bool mandatory = false) const; void supportFile(const vs::SupportFile &support, Sink &sink , const Sink::FileInfo &fileInfo) const; @@ -265,6 +294,10 @@ class Generator : boost::noncopyable { */ bool changeEnforced() const { return changeEnforced_; } + /** Sets new provider. Value is stolen. + */ + void setProvider(std::unique_ptr &&provider); + private: virtual void prepare_impl(Arsenal &arsenal) = 0; virtual vts::MapConfig mapConfig_impl(ResourceRoot root) const = 0; @@ -284,6 +317,7 @@ class Generator : boost::noncopyable { std::atomic readySince_; DemRegistry::pointer demRegistry_; Generator::pointer replace_; + std::unique_ptr provider_; }; /** Set of dataset generators. @@ -384,16 +418,36 @@ inline Generator::pointer Generators::generator(const FileInfo &fileInfo) const std::shared_ptr inline GeneratorFinder::findGenerator(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const + , const Resource::Id &resourceId + , bool mustBeReady) const { - return findGenerator_impl(generatorType, resourceId); + return findGenerator_impl(generatorType, resourceId, mustBeReady); } inline Generator::pointer Generator::otherGenerator(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const + , const Resource::Id &resourceId + , bool mustBeReady, bool mandatory) + const +{ + auto other(generatorFinder_->findGenerator + (generatorType, resourceId, mustBeReady)); + if (!other && mandatory) { + throw GeneratorNotFound(generatorType, resourceId); + } + return other; +} + +template +ProviderType* Generator::getProvider() const +{ + if (!provider_) { return nullptr; } + return dynamic_cast(provider_.get()); +} + +inline void Generator::setProvider(std::unique_ptr &&provider) { - return generatorFinder_->findGenerator(generatorType, resourceId); + provider_ = std::move(provider); } #endif // mapproxy_generator_hpp_included_ diff --git a/mapproxy/src/mapproxy/generator/factory.hpp b/mapproxy/src/mapproxy/generator/factory.hpp index 3ac8aa8..0e40f49 100644 --- a/mapproxy/src/mapproxy/generator/factory.hpp +++ b/mapproxy/src/mapproxy/generator/factory.hpp @@ -40,6 +40,17 @@ struct Generator::Factory { * reference frame. */ virtual bool systemInstance() const { return false; } + + /** Factory registry support + */ + typedef std::map Registry; + + static pointer findFactory(const Resource::Generator &type); + + static void registerType(const Resource::Generator &type + , const pointer &factory); + + static Registry ®istry(); }; #endif // mapproxy_generator_factory_hpp_included_ diff --git a/mapproxy/src/mapproxy/generator/generator.cpp b/mapproxy/src/mapproxy/generator/generator.cpp index 03115f6..1fb25fd 100644 --- a/mapproxy/src/mapproxy/generator/generator.cpp +++ b/mapproxy/src/mapproxy/generator/generator.cpp @@ -59,30 +59,12 @@ namespace { const std::string ResourceFile("resource.json"); -typedef std::map Registry; - -Registry& registry() { - static Registry registry; - return registry; -} - -Generator::Factory::pointer findFactory(const Resource::Generator &type) -{ - const auto &r(registry()); - auto fregistry(r.find(type)); - if (fregistry == r.end()) { - LOGTHROW(err1, UnknownGenerator) - << "Unknown generator type <" << type << ">."; - } - return fregistry->second; -} - } // namespace void Generator::registerType(const Resource::Generator &type , const Factory::pointer &factory) { - registry().insert(Registry::value_type(type, factory)); + Factory::registerType(type, factory); } DefinitionBase::pointer Generator::definition(const Resource::Generator &type) @@ -93,7 +75,7 @@ DefinitionBase::pointer Generator::definition(const Resource::Generator &type) Generator::pointer Generator::create(const Params ¶ms) { try { - return findFactory(params.resource.generator)->create(params); + return Factory::findFactory(params.resource.generator)->create(params); } catch (const boost::bad_any_cast&) { LOGTHROW(err2, InvalidConfiguration) << "Passed resource does not match generator <" @@ -314,864 +296,6 @@ bool Generator::updatedSince(std::uint64_t timestamp) const return readySince_ > timestamp; } -namespace { - -struct TypeKey { - std::string referenceFrame; - Resource::Generator::Type type; - - bool operator<(const TypeKey &o) const { - if (referenceFrame < o.referenceFrame) { return true; } - if (o.referenceFrame < referenceFrame) { return false; } - return type < o.type; - } - - TypeKey(const std::string &referenceFrame, Resource::Generator::Type type) - : referenceFrame(referenceFrame), type(type) - {} -}; - -TypeKey extractTypeKey(const Generator &generator) -{ - const auto r(generator.resource()); - return { r.id.referenceFrame, r.generator.type }; -} - -struct GroupKey { - std::string referenceFrame; - Resource::Generator::Type type; - std::string group; - - bool operator<(const GroupKey &o) const { - if (referenceFrame < o.referenceFrame) { return true; } - if (o.referenceFrame < referenceFrame) { return false; } - - if (group < o.group) { return true; } - if (o.group < group) { return false; } - - return type < o.type; - } - - GroupKey(const std::string &referenceFrame, Resource::Generator::Type type - , const std::string &group) - : referenceFrame(referenceFrame), type(type), group(group) - {} -}; - -GroupKey extractGroupKey(const Generator &generator) -{ - const auto r(generator.resource()); - return { r.id.referenceFrame, r.generator.type, r.id.group }; -} - -Generators::Config fixUp(Generators::Config config) -{ - // sanity check - if (!config.externalUrl.empty() && (config.externalUrl.back() != '/')) { - config.externalUrl.push_back('/'); - } - return config; -} - -} // namespace - -class Generators::Detail - : public boost::noncopyable - , public GeneratorFinder -{ -public: - Detail(const Generators::Config &config - , const ResourceBackend::pointer &resourceBackend) - : config_(fixUp(config)), resourceBackend_(resourceBackend) - , arsenal_(), running_(false), updateRequest_(false), lastUpdate_(0) - , ready_(false), preparing_(0) - , work_(ios_), demRegistry_(std::make_shared()) - { - registerSystemGenerators(); - } - - void checkReady() const; - - Generator::pointer generator(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const; - - Generator::list referenceFrame(const std::string &referenceFrame) const; - - std::vector listGroups(const std::string &referenceFrame - , Resource::Generator::Type type) - const; - - std::vector listIds(const std::string &referenceFrame - , Resource::Generator::Type type - , const std::string &group) const; - - void start(Arsenal &arsenal); - void stop(); - - inline const Config& config() const { return config_; } - - inline const DemRegistry& demRegistry() const { return *demRegistry_; } - - std::uint64_t update(); - - bool updatedSince(std::uint64_t timestamp) const; - - void listResources(std::ostream &os) const; - - void replace(const Generator::pointer &original - , const Generator::pointer &replacement); - - bool has(const Resource::Id &resourceId) const; - - bool isReady(const Resource::Id &resourceId) const; - - std::string url(const Resource::Id &resourceId - , GeneratorInterface::Interface iface) const; - - bool updatedSince(const Resource::Id &resourceId - , std::uint64_t timestamp, bool nothrow) const; - -private: - void registerSystemGenerators(); - - void update(const Resource::map &resources); - - void updater(); - void worker(std::size_t id); - void prepare(const Generator::pointer &generator); - - virtual Generator::pointer - findGenerator_impl(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const; - - const Config config_; - ResourceBackend::pointer resourceBackend_; - Arsenal *arsenal_; - - // resource updater stuff - std::thread updater_; - std::atomic running_; - std::atomic updateRequest_; - std::atomic lastUpdate_; - std::mutex updaterLock_; - std::condition_variable updaterCond_; - - struct ResourceIdIdx {}; - struct GroupIdx {}; - struct TypeIdx {}; - struct ReferenceFrameIdx {}; - - typedef boost::multi_index_container< - Generator::pointer - , bmi::indexed_by< - bmi::ordered_unique > - - , bmi::ordered_unique< - bmi::tag - , BOOST_MULTI_INDEX_CONST_MEM_FUN - (Generator, const Resource::Id&, id) - > - - , bmi::ordered_non_unique< - bmi::tag - , bmi::global_fun - > - - , bmi::ordered_non_unique< - bmi::tag - , bmi::global_fun - > - - , bmi::ordered_non_unique< - bmi::tag - , BOOST_MULTI_INDEX_CONST_MEM_FUN - (Generator, const std::string&, referenceFrameId) - > - > - - > GeneratorMap; - - - - // internals - mutable std::mutex lock_; - GeneratorMap serving_; - - std::atomic ready_; - std::atomic preparing_; - - // prepare stuff - asio::io_service ios_; - asio::io_service::work work_; - std::vector workers_; - - // DEM registry - DemRegistry::pointer demRegistry_; -}; - -void Generators::Detail::checkReady() const -{ - if (ready_) { return; } - throw Unavailable("Server not ready."); -} - -void Generators::Detail::start(Arsenal &arsenal) -{ - // make sure threads are released when something goes wrong - struct Guard { - Guard(const std::function &func) : func(func) {} - ~Guard() { if (func) { func(); } } - void release() { func = {}; } - std::function func; - } guard([this]() { stop(); }); - - // start updater - running_ = true; - std::thread updater(&Detail::updater, this); - updater_.swap(updater); - - arsenal_ = &arsenal; - // TODO: make configurable - std::size_t count(5); - // start workers - for (std::size_t id(1); id <= count; ++id) { - workers_.emplace_back(&Detail::worker, this, id); - } - - guard.release(); -} - -void Generators::Detail::stop() -{ - if (!running_) { return; } - - running_ = false; - ios_.stop(); - - updaterCond_.notify_all(); - updater_.join(); - - while (!workers_.empty()) { - workers_.back().join(); - workers_.pop_back(); - } - arsenal_ = {}; -} - -struct Aborted {}; - -void Generators::Detail::updater() -{ - dbglog::thread_id("updater"); - - // invalidate any update request - updateRequest_ = false; - // never update - lastUpdate_ = 0; - - while (running_) { - // default sleep time in seconds - std::chrono::seconds sleep(config_.resourceUpdatePeriod); - - try { - update(resourceBackend_->load()); - lastUpdate_ = utility::usecFromEpoch(); - } catch (Aborted) { - // pass - } catch (const std::exception &e) { - LOG(err2) << "Resource info update failed: <" << e.what() << ">."; - if (config_.resourceUpdatePeriod > 0) { - sleep = std::chrono::seconds(5); - } - } - - // sleep for configured time minutes - { - std::unique_lock lock(updaterLock_); - - // condition variable wait predicate - const auto predicate([this]() -> bool - { - auto updateRequest - (std::atomic_exchange(&updateRequest_, false)); - return !running_ || updateRequest; - }); - - if (config_.resourceUpdatePeriod > 0) { - // wait for given duration - updaterCond_.wait_for(lock, sleep, predicate); - } else { - // wait untill bugged - updaterCond_.wait(lock, predicate); - } - } - } -} - -void Generators::Detail::worker(std::size_t id) -{ - dbglog::thread_id(str(boost::format("prepare:%u") % id)); - LOG(info2) << "Spawned prepare worker id:" << id << "."; - - for (;;) { - try { - ios_.run(); - LOG(info2) << "Terminated prepare worker id:" << id << "."; - return; - } catch (const std::exception &e) { - LOG(err3) - << "Uncaught exception in worker: <" << e.what() - << ">. Going on."; - } - } -} - -void Generators::Detail::prepare(const Generator::pointer &generator) -{ - ++preparing_; - - ios_.post([=]() - { - try { - generator->prepare(*arsenal_); - if (auto original = generator->replace()) { - replace(original, generator); - } - } catch (const std::exception &e) { - LOG(warn2) - << "Failed to prepare generator for <" - << generator->resource().id << "> (" << e.what() - << "); removing from set of known generators."; - - resourceBackend_->error(generator->resource().id, e.what()); - - // erease from map (under lock) - std::unique_lock lock(lock_); - serving_.erase(generator); - } - --preparing_; - }); -} - -void Generators::Detail::registerSystemGenerators() -{ - for (const auto &ritem : registry()) { - const auto &resourceGenerator(ritem.first); - const auto &factory(ritem.second); - - if (!factory->systemInstance()) { continue; } - - for (const auto &rfitem : vr::system.referenceFrames) { - const auto &rfId(rfitem.first); - const auto &rf(rfitem.second); - LOG(info2) << "About to register " << resourceGenerator - << " generator for" - << " reference frame " << rfitem.first << "."; - - // create resource - Resource resource - (resourceBackend_->genericConfig().fileClassSettings); - resource.id = Resource::Id - (rfId, Generator::systemGroup(), resourceGenerator.driver); - resource.generator = resourceGenerator; - resource.comment = "autoregistered resource"; - resource.referenceFrame = &rf; - resource.definition(resource::definition(resourceGenerator)); - - // start at first valid lod - // lod 22 is arbitrarily deep - resource.lodRange - = vts::LodRange(rf.division.rootLodRange.min, 22); - - // compose lod range from first valid lode - resource.tileRange = vts::TileRange(math::InvalidExtents{}); - for (const auto &item : rf.division.nodes) { - const auto &node(item.second); - if (!node.real()) { continue; } - if (node.id.lod == rf.division.rootLodRange.min) { - math::update(resource.tileRange, node.id.x, node.id.y); - } - } - - // create generator params - Generator::Params params(resource); - params.config = config_; - params.config.root = config_.root; - params.generatorFinder = this; - params.demRegistry = demRegistry_; - params.system = true; - - // create generator - auto g(factory->create(params)); - - // register - serving_.insert(g); - - // and prepare if not ready - if (!g->ready()) { - prepare(g); - } - } - } -} - -Generators::Generators(const Config &config - , const ResourceBackend::pointer &resourceBackend) - : detail_(std::make_shared(config, resourceBackend)) -{} - -Generators::~Generators() -{ - detail().stop(); -} - -void Generators::start(Arsenal &arsenal) -{ - detail().start(arsenal); -} - -void Generators::stop() -{ - detail().stop(); -} - -Generator::list Generators::referenceFrame(const std::string &referenceFrame) - const -{ - return detail().referenceFrame(referenceFrame); -} - -Generator::pointer -Generators::generator(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const -{ - return detail().generator(generatorType, resourceId); -} - -void Generators::Detail::replace(const Generator::pointer &original - , const Generator::pointer &replacement) -{ - std::unique_lock lock(lock_); - // find original in the serving set - auto ioriginal(serving_.find(original)); - // and replace - serving_.replace(ioriginal, replacement); - LOG(info3) - << "Replaced resource <" << original->id() << "> with new definiton."; -} - -void Generators::Detail::update(const Resource::map &resources) -{ - LOG(info2) << "Updating resources."; - - auto iresources(resources.begin()), eresources(resources.end()); - auto &idx(serving_.get()); - auto iserving(idx.begin()), eserving(idx.end()); - - Generator::list toAdd; - Generator::list toRemove; - Generator::list toReplace; - - auto add([&](const Resource &res) - { - if (!running_) { - throw Aborted{}; - } - try { - Generator::Params params(res); - params.config = config_; - params.config.root = config_.root; - params.generatorFinder = this; - params.demRegistry = demRegistry_; - toAdd.push_back(Generator::create(params)); - } catch (const std::exception &e) { - LOG(err2) << "Failed to create generator for resource <" - << iresources->first << ">: <" << e.what() << ">."; - } - }); - - auto replace([&](const Resource &res, const Generator::pointer &original) - { - if (!running_) { - throw Aborted{}; - } - try { - Generator::Params params(res); - params.config = config_; - params.config.root = config_.root; - params.generatorFinder = this; - params.demRegistry = demRegistry_; - params.replace = original; - toReplace.push_back(Generator::create(params)); - } catch (const std::exception &e) { - LOG(err2) << "Failed to re-create generator for resource <" - << iresources->first << ">: <" << e.what() << ">."; - } - }); - - // process common stuff - while ((iresources != eresources) && (iserving != eserving)) { - const auto &resource(iresources->second); - if (iresources->first < (*iserving)->id()) { - // new resource - add(resource); - ++iresources; - } else if ((*iserving)->id() < iresources->first) { - // removed resource - if (!(*iserving)->system()) { - toRemove.push_back(*iserving); - } - ++iserving; - } else { - // existing resource - switch ((*iserving)->changed(resource)) { - case Changed::no: - // same stuff, do nothing - break; - - case Changed::yes: - // changed - if (!config().freezes(resource.generator.type)) { - replace(resource, *iserving); - } - break; - - case Changed::safely: - case Changed::withRevisionBump: - // here comes the fun - replace(resource, *iserving); - break; - } - - ++iresources; - ++iserving; - } - } - - // process tail: added resources - for (; iresources != eresources; ++iresources) { - add(iresources->second); - } - - // process tail: removed resources - for (; iserving != eserving; ++iserving) { - if (!(*iserving)->system()) { - toRemove.push_back(*iserving); - } - } - - // add stuff - for (const auto &generator : toAdd) { - { - std::unique_lock lock(lock_); - serving_.insert(generator); - } - - if (!generator->ready()) { - prepare(generator); - } - } - - // remove stuff - for (const auto &generator : toRemove) { - { - std::unique_lock lock(lock_); - serving_.erase(generator); - } - - // TODO: mark as to be removed for prepare workers - } - - // replace stuff (prepare) - for (const auto &generator : toReplace) { - if (!generator->ready()) { - prepare(generator); - } else { - this->replace(generator->replace(), generator); - } - } - - LOG(info2) << "Resources updated."; - if (!ready_) { - ready_ = true; - LOG(info3) << "Ready to serve."; - } - - // wait till all pending resources are available; not nice but should work - while (preparing_ && running_) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } -} - -Generator::list -Generators::Detail::referenceFrame(const std::string &referenceFrame) - const -{ - checkReady(); - - Generator::list out; - - // use only ready generators that handle datasets for given reference frame - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - for (auto range(idx.equal_range(referenceFrame)); - range.first != range.second; ++range.first) - { - if ((*range.first)->ready()) { - out.push_back(*range.first); - } - } - - return out; -} - -Generator::pointer -Generators::Detail::generator(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) - const -{ - checkReady(); - - // find generator (under lock) - auto generator([&]() -> Generator::pointer - { - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - auto fserving(idx.find(resourceId)); - if (fserving == idx.end()) { return {}; } - return *fserving; - }()); - - if (!generator) { return generator; } - - const auto &resource(generator->resource()); - - // check generator type - if (generatorType != resource.generator.type) { - return {}; - } - - return generator; -} - -Generator::pointer -Generators::Detail::findGenerator_impl(Resource::Generator::Type generatorType - , const Resource::Id &resourceId) const -{ - auto g(generator(generatorType, resourceId)); - if (!g || !g->ready()) { return {}; } - return g; -} - -const Generators::Config& Generators::config() const { - return detail().config(); -} - -std::vector -Generators::Detail::listGroups(const std::string &referenceFrame - , Resource::Generator::Type type) - const -{ - checkReady(); - - std::vector out; - { - std::unique_lock lock(lock_); - - auto &idx(serving_.get()); - std::string prev; - for (auto range(idx.equal_range(TypeKey(referenceFrame, type))); - range.first != range.second; ++range.first) - { - const auto &group((*range.first)->group()); - if (group != prev) { - out.push_back(group); - prev = group; - } - } - } - - return out; -} - -std::vector -Generators::Detail::listIds(const std::string &referenceFrame - , Resource::Generator::Type type - , const std::string &group) - const -{ - checkReady(); - - std::vector out; - { - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - for (auto range(idx.equal_range - (GroupKey(referenceFrame, type, group))); - range.first != range.second; ++range.first) - { - out.push_back((*range.first)->id().id); - } - } - - return out; -} - -std::vector -Generators::listGroups(const std::string &referenceFrame - , Resource::Generator::Type type) const -{ - return detail().listGroups(referenceFrame, type); -} - -std::vector -Generators::listIds(const std::string &referenceFrame - , Resource::Generator::Type type - , const std::string &group) const -{ - return detail().listIds(referenceFrame, type, group); -} - -void Generator::supportFile(const vs::SupportFile &support, Sink &sink - , const Sink::FileInfo &fileInfo) const -{ - if (!support.isTemplate) { - sink.content(support.data, support.size, fileInfo, false); - return; - } - - // expand and send - sink.content(support.expand(config_.variables, config_.defaults) - , fileInfo); -} - -const DemRegistry& Generators::demRegistry() const -{ - return detail().demRegistry(); -} - -std::uint64_t Generators::Detail::update() -{ - const auto start(utility::usecFromEpoch()); - updateRequest_ = true; - updaterCond_.notify_one(); - return start; -} - -std::uint64_t Generators::update() -{ - return detail().update(); -} - -bool Generators::Detail::updatedSince(std::uint64_t timestamp) const -{ - return lastUpdate_ > timestamp; -} - -bool Generators::updatedSince(std::uint64_t timestamp) const -{ - return detail().updatedSince(timestamp); -} - -bool Generators::Detail::has(const Resource::Id &resourceId) const -{ - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - auto fserving(idx.find(resourceId)); - return (fserving != idx.end()); -} - -bool Generators::has(const Resource::Id &resourceId) const -{ - return detail().has(resourceId); -} - -bool Generators::Detail::isReady(const Resource::Id &resourceId) const -{ - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - auto fserving(idx.find(resourceId)); - if (fserving == idx.end()) { return false; } - return (*fserving)->ready(); -} - -bool Generators::isReady(const Resource::Id &resourceId) const -{ - return detail().isReady(resourceId); -} - -std::string Generators::Detail::url(const Resource::Id &resourceId - , GeneratorInterface::Interface iface) - const -{ - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - auto fserving(idx.find(resourceId)); - if (fserving == idx.end()) { - LOGTHROW(err1, UnknownGenerator) - << "No such generator <" << resourceId << ">"; - } - return (*fserving)->url(iface); -} - -std::string Generators::url(const Resource::Id &resourceId - , GeneratorInterface::Interface iface) const -{ - return detail().url(resourceId, iface); -} - -bool Generators::Detail::updatedSince(const Resource::Id &resourceId - , std::uint64_t timestamp - , bool nothrow) const -{ - std::unique_lock lock(lock_); - auto &idx(serving_.get()); - auto fserving(idx.find(resourceId)); - if (fserving == idx.end()) { - if (nothrow) { return false; } - LOGTHROW(err1, UnknownGenerator) - << "No such generator <" << resourceId << ">"; - } - return (*fserving)->updatedSince(timestamp); -} - -bool Generators::updatedSince(const Resource::Id &resourceId - , std::uint64_t timestamp, bool nothrow) const -{ - return detail().updatedSince(resourceId, timestamp, nothrow); -} - -void Generator::status(std::ostream &os) const -{ - os << "<" << id() - << "> (type <" << resource().generator << ">)" - << (ready_ ? "" : " not ready") - << "\n"; -} - -void Generators::Detail::listResources(std::ostream &os) const -{ - - Generator::list generators; - { - std::unique_lock lock(lock_); - for (const auto &generator : serving_) { - generators.push_back(generator); - } - } - - for (const auto &generator : generators) { - generator->status(os); - } -} - -void Generators::listResources(std::ostream &os) const -{ - detail().listResources(os); -} - Generator::Task Generator::generateFile(const FileInfo &fileInfo, Sink sink) const { diff --git a/mapproxy/src/mapproxy/generator/generators.cpp b/mapproxy/src/mapproxy/generator/generators.cpp new file mode 100644 index 0000000..5c368a6 --- /dev/null +++ b/mapproxy/src/mapproxy/generator/generators.cpp @@ -0,0 +1,734 @@ +/** + * Copyright (c) 2017 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dbglog/dbglog.hpp" +#include "utility/path.hpp" +#include "utility/gccversion.hpp" +#include "utility/time.hpp" +#include "utility/raise.hpp" + +#include "../error.hpp" +#include "../definition.hpp" +#include "generators.hpp" +#include "factory.hpp" + +namespace fs = boost::filesystem; +namespace ba = boost::algorithm; + +namespace { + +Generators::Config fixUp(Generators::Config config) +{ + // sanity check + if (!config.externalUrl.empty() && (config.externalUrl.back() != '/')) { + config.externalUrl.push_back('/'); + } + return config; +} + +} // namespace + +Generators::Detail::Detail(const Generators::Config &config + , const ResourceBackend::pointer &resourceBackend) + : config_(fixUp(config)), resourceBackend_(resourceBackend) + , arsenal_(), running_(false), updateRequest_(false), lastUpdate_(0) + , ready_(false), preparing_(0) + , work_(ios_), demRegistry_(std::make_shared()) +{ + registerSystemGenerators(); +} + +void Generators::Detail::checkReady() const +{ + if (ready_) { return; } + throw Unavailable("Server not ready."); +} + +void Generators::Detail::start(Arsenal &arsenal) +{ + // make sure threads are released when something goes wrong + struct Guard { + Guard(const std::function &func) : func(func) {} + ~Guard() { if (func) { func(); } } + void release() { func = {}; } + std::function func; + } guard([this]() { stop(); }); + + // start updater + running_ = true; + std::thread updater(&Detail::updater, this); + updater_.swap(updater); + + arsenal_ = &arsenal; + // TODO: make configurable + std::size_t count(5); + // start workers + for (std::size_t id(1); id <= count; ++id) { + workers_.emplace_back(&Detail::worker, this, id); + } + + guard.release(); +} + +void Generators::Detail::stop() +{ + if (!running_) { return; } + + running_ = false; + ios_.stop(); + + updaterCond_.notify_all(); + updater_.join(); + + while (!workers_.empty()) { + workers_.back().join(); + workers_.pop_back(); + } + arsenal_ = {}; +} + +struct Aborted {}; + +void Generators::Detail::updater() +{ + dbglog::thread_id("updater"); + + // invalidate any update request + updateRequest_ = false; + // never update + lastUpdate_ = 0; + + while (running_) { + // default sleep time in seconds + std::chrono::seconds sleep(config_.resourceUpdatePeriod); + + try { + update(resourceBackend_->load()); + lastUpdate_ = utility::usecFromEpoch(); + } catch (Aborted) { + // pass + } catch (const std::exception &e) { + LOG(err2) << "Resource info update failed: <" << e.what() << ">."; + if (config_.resourceUpdatePeriod > 0) { + sleep = std::chrono::seconds(5); + } + } + + // sleep for configured time minutes + { + std::unique_lock lock(updaterLock_); + + // condition variable wait predicate + const auto predicate([this]() -> bool + { + auto updateRequest + (std::atomic_exchange(&updateRequest_, false)); + return !running_ || updateRequest; + }); + + if (config_.resourceUpdatePeriod > 0) { + // wait for given duration + updaterCond_.wait_for(lock, sleep, predicate); + } else { + // wait untill bugged + updaterCond_.wait(lock, predicate); + } + } + } +} + +void Generators::Detail::worker(std::size_t id) +{ + dbglog::thread_id(str(boost::format("prepare:%u") % id)); + LOG(info2) << "Spawned prepare worker id:" << id << "."; + + for (;;) { + try { + ios_.run(); + LOG(info2) << "Terminated prepare worker id:" << id << "."; + return; + } catch (const std::exception &e) { + LOG(err3) + << "Uncaught exception in worker: <" << e.what() + << ">. Going on."; + } + } +} + +void Generators::Detail::prepare(const Generator::pointer &generator) +{ + ++preparing_; + + ios_.post([=]() + { + try { + generator->prepare(*arsenal_); + if (auto original = generator->replace()) { + replace(original, generator); + } + } catch (const std::exception &e) { + LOG(warn2) + << "Failed to prepare generator for <" + << generator->resource().id << "> (" << e.what() + << "); removing from set of known generators."; + + resourceBackend_->error(generator->resource().id, e.what()); + + // erease from map (under lock) + std::unique_lock lock(lock_); + serving_.erase(generator); + } + --preparing_; + }); +} + +void Generators::Detail::registerSystemGenerators() +{ + for (const auto &ritem : Generator::Factory::registry()) { + const auto &resourceGenerator(ritem.first); + const auto &factory(ritem.second); + + if (!factory->systemInstance()) { continue; } + + for (const auto &rfitem : vr::system.referenceFrames) { + const auto &rfId(rfitem.first); + const auto &rf(rfitem.second); + LOG(info2) << "About to register " << resourceGenerator + << " generator for" + << " reference frame " << rfitem.first << "."; + + // create resource + Resource resource + (resourceBackend_->genericConfig().fileClassSettings); + resource.id = Resource::Id + (rfId, Generator::systemGroup(), resourceGenerator.driver); + resource.generator = resourceGenerator; + resource.comment = "autoregistered resource"; + resource.referenceFrame = &rf; + resource.definition(resource::definition(resourceGenerator)); + + // start at first valid lod + // lod 22 is arbitrarily deep + resource.lodRange + = vts::LodRange(rf.division.rootLodRange.min, 22); + + // compose lod range from first valid lode + resource.tileRange = vts::TileRange(math::InvalidExtents{}); + for (const auto &item : rf.division.nodes) { + const auto &node(item.second); + if (!node.real()) { continue; } + if (node.id.lod == rf.division.rootLodRange.min) { + math::update(resource.tileRange, node.id.x, node.id.y); + } + } + + // create generator params + Generator::Params params(resource); + params.config = config_; + params.config.root = config_.root; + params.generatorFinder = this; + params.demRegistry = demRegistry_; + params.system = true; + + // create generator + auto g(factory->create(params)); + + // register + serving_.insert(g); + + // and prepare if not ready + if (!g->ready()) { + prepare(g); + } + } + } +} + +Generators::Generators(const Config &config + , const ResourceBackend::pointer &resourceBackend) + : detail_(std::make_shared(config, resourceBackend)) +{} + +Generators::~Generators() +{ + detail().stop(); +} + +void Generators::start(Arsenal &arsenal) +{ + detail().start(arsenal); +} + +void Generators::stop() +{ + detail().stop(); +} + +Generator::list Generators::referenceFrame(const std::string &referenceFrame) + const +{ + return detail().referenceFrame(referenceFrame); +} + +Generator::pointer +Generators::generator(Resource::Generator::Type generatorType + , const Resource::Id &resourceId) const +{ + return detail().generator(generatorType, resourceId); +} + +void Generators::Detail::replace(const Generator::pointer &original + , const Generator::pointer &replacement) +{ + std::unique_lock lock(lock_); + // find original in the serving set + auto ioriginal(serving_.find(original)); + // and replace + serving_.replace(ioriginal, replacement); + LOG(info3) + << "Replaced resource <" << original->id() << "> with new definiton."; +} + +void Generators::Detail::update(const Resource::map &resources) +{ + LOG(info2) << "Updating resources."; + + auto iresources(resources.begin()), eresources(resources.end()); + auto &idx(serving_.get()); + auto iserving(idx.begin()), eserving(idx.end()); + + Generator::list toAdd; + Generator::list toRemove; + Generator::list toReplace; + + auto add([&](const Resource &res) + { + if (!running_) { + throw Aborted{}; + } + try { + Generator::Params params(res); + params.config = config_; + params.config.root = config_.root; + params.generatorFinder = this; + params.demRegistry = demRegistry_; + toAdd.push_back(Generator::create(params)); + } catch (const std::exception &e) { + LOG(err2) << "Failed to create generator for resource <" + << iresources->first << ">: <" << e.what() << ">."; + } + }); + + auto replace([&](const Resource &res, const Generator::pointer &original) + { + if (!running_) { + throw Aborted{}; + } + try { + Generator::Params params(res); + params.config = config_; + params.config.root = config_.root; + params.generatorFinder = this; + params.demRegistry = demRegistry_; + params.replace = original; + toReplace.push_back(Generator::create(params)); + } catch (const std::exception &e) { + LOG(err2) << "Failed to re-create generator for resource <" + << iresources->first << ">: <" << e.what() << ">."; + } + }); + + // process common stuff + while ((iresources != eresources) && (iserving != eserving)) { + const auto &resource(iresources->second); + if (iresources->first < (*iserving)->id()) { + // new resource + add(resource); + ++iresources; + } else if ((*iserving)->id() < iresources->first) { + // removed resource + if (!(*iserving)->system()) { + toRemove.push_back(*iserving); + } + ++iserving; + } else { + // existing resource + switch ((*iserving)->changed(resource)) { + case Changed::no: + // same stuff, do nothing + break; + + case Changed::yes: + // changed + if (!config().freezes(resource.generator.type)) { + replace(resource, *iserving); + } + break; + + case Changed::safely: + case Changed::withRevisionBump: + // here comes the fun + replace(resource, *iserving); + break; + } + + ++iresources; + ++iserving; + } + } + + // process tail: added resources + for (; iresources != eresources; ++iresources) { + add(iresources->second); + } + + // process tail: removed resources + for (; iserving != eserving; ++iserving) { + if (!(*iserving)->system()) { + toRemove.push_back(*iserving); + } + } + + // remove stuff + for (const auto &generator : toRemove) { + { + std::unique_lock lock(lock_); + serving_.erase(generator); + } + } + + // add generators; not prepared yet to make them available to the others + // when prepared + for (const auto &generator : toAdd) { + std::unique_lock lock(lock_); + serving_.insert(generator); + } + + // prepare generators if not ready + for (const auto &generator : toAdd) { + if (!generator->ready()) { prepare(generator); } + } + + // replace stuff (prepare) + for (const auto &generator : toReplace) { + if (!generator->ready()) { + prepare(generator); + } else { + this->replace(generator->replace(), generator); + } + } + + LOG(info2) << "Resources updated."; + if (!ready_) { + ready_ = true; + LOG(info3) << "Ready to serve."; + } + + // wait till all pending resources are available; not nice but should work + while (preparing_ && running_) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +Generator::list +Generators::Detail::referenceFrame(const std::string &referenceFrame) + const +{ + checkReady(); + + Generator::list out; + + // use only ready generators that handle datasets for given reference frame + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + for (auto range(idx.equal_range(referenceFrame)); + range.first != range.second; ++range.first) + { + if ((*range.first)->ready()) { + out.push_back(*range.first); + } + } + + return out; +} + +Generator::pointer +Generators::Detail::generator(Resource::Generator::Type generatorType + , const Resource::Id &resourceId + , bool noReadyCheck) + const +{ + if (!noReadyCheck) { checkReady(); } + + // find generator (under lock) + auto generator([&]() -> Generator::pointer + { + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + auto fserving(idx.find(resourceId)); + if (fserving == idx.end()) { return {}; } + return *fserving; + }()); + + if (!generator) { return generator; } + + const auto &resource(generator->resource()); + + // check generator type + if (generatorType != resource.generator.type) { + return {}; + } + + return generator; +} + +Generator::pointer +Generators::Detail::findGenerator_impl(Resource::Generator::Type generatorType + , const Resource::Id &resourceId + , bool mustBeReady) const +{ + auto g(generator(generatorType, resourceId, !mustBeReady)); + if (!g) { return {}; } + if (mustBeReady && !g->ready()) { return {}; } + return g; +} + +const Generators::Config& Generators::config() const { + return detail().config(); +} + +std::vector +Generators::Detail::listGroups(const std::string &referenceFrame + , Resource::Generator::Type type) + const +{ + checkReady(); + + std::vector out; + { + std::unique_lock lock(lock_); + + auto &idx(serving_.get()); + std::string prev; + for (auto range(idx.equal_range(Keys::TypeKey(referenceFrame, type))); + range.first != range.second; ++range.first) + { + const auto &group((*range.first)->group()); + if (group != prev) { + out.push_back(group); + prev = group; + } + } + } + + return out; +} + +std::vector +Generators::Detail::listIds(const std::string &referenceFrame + , Resource::Generator::Type type + , const std::string &group) + const +{ + checkReady(); + + std::vector out; + { + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + for (auto range(idx.equal_range + (Keys::GroupKey(referenceFrame, type, group))); + range.first != range.second; ++range.first) + { + out.push_back((*range.first)->id().id); + } + } + + return out; +} + +std::vector +Generators::listGroups(const std::string &referenceFrame + , Resource::Generator::Type type) const +{ + return detail().listGroups(referenceFrame, type); +} + +std::vector +Generators::listIds(const std::string &referenceFrame + , Resource::Generator::Type type + , const std::string &group) const +{ + return detail().listIds(referenceFrame, type, group); +} + +void Generator::supportFile(const vs::SupportFile &support, Sink &sink + , const Sink::FileInfo &fileInfo) const +{ + if (!support.isTemplate) { + sink.content(support.data, support.size, fileInfo, false); + return; + } + + // expand and send + sink.content(support.expand(config_.variables, config_.defaults) + , fileInfo); +} + +const DemRegistry& Generators::demRegistry() const +{ + return detail().demRegistry(); +} + +std::uint64_t Generators::Detail::update() +{ + const auto start(utility::usecFromEpoch()); + updateRequest_ = true; + updaterCond_.notify_one(); + return start; +} + +std::uint64_t Generators::update() +{ + return detail().update(); +} + +bool Generators::Detail::updatedSince(std::uint64_t timestamp) const +{ + return lastUpdate_ > timestamp; +} + +bool Generators::updatedSince(std::uint64_t timestamp) const +{ + return detail().updatedSince(timestamp); +} + +bool Generators::Detail::has(const Resource::Id &resourceId) const +{ + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + auto fserving(idx.find(resourceId)); + return (fserving != idx.end()); +} + +bool Generators::has(const Resource::Id &resourceId) const +{ + return detail().has(resourceId); +} + +bool Generators::Detail::isReady(const Resource::Id &resourceId) const +{ + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + auto fserving(idx.find(resourceId)); + if (fserving == idx.end()) { return false; } + return (*fserving)->ready(); +} + +bool Generators::isReady(const Resource::Id &resourceId) const +{ + return detail().isReady(resourceId); +} + +std::string Generators::Detail::url(const Resource::Id &resourceId + , GeneratorInterface::Interface iface) + const +{ + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + auto fserving(idx.find(resourceId)); + if (fserving == idx.end()) { + LOGTHROW(err1, UnknownGenerator) + << "No such generator <" << resourceId << ">"; + } + return (*fserving)->url(iface); +} + +std::string Generators::url(const Resource::Id &resourceId + , GeneratorInterface::Interface iface) const +{ + return detail().url(resourceId, iface); +} + +bool Generators::Detail::updatedSince(const Resource::Id &resourceId + , std::uint64_t timestamp + , bool nothrow) const +{ + std::unique_lock lock(lock_); + auto &idx(serving_.get()); + auto fserving(idx.find(resourceId)); + if (fserving == idx.end()) { + if (nothrow) { return false; } + LOGTHROW(err1, UnknownGenerator) + << "No such generator <" << resourceId << ">"; + } + return (*fserving)->updatedSince(timestamp); +} + +bool Generators::updatedSince(const Resource::Id &resourceId + , std::uint64_t timestamp, bool nothrow) const +{ + return detail().updatedSince(resourceId, timestamp, nothrow); +} + +void Generator::status(std::ostream &os) const +{ + os << "<" << id() + << "> (type <" << resource().generator << ">)" + << (ready_ ? "" : " not ready") + << "\n"; +} + +void Generators::Detail::listResources(std::ostream &os) const +{ + + Generator::list generators; + { + std::unique_lock lock(lock_); + for (const auto &generator : serving_) { + generators.push_back(generator); + } + } + + for (const auto &generator : generators) { + generator->status(os); + } +} + +void Generators::listResources(std::ostream &os) const +{ + detail().listResources(os); +} diff --git a/mapproxy/src/mapproxy/generator/generators.hpp b/mapproxy/src/mapproxy/generator/generators.hpp new file mode 100644 index 0000000..d999e43 --- /dev/null +++ b/mapproxy/src/mapproxy/generator/generators.hpp @@ -0,0 +1,223 @@ +/** + * Copyright (c) 2019 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef mapproxy_generator_generators_hpp_included_ +#define mapproxy_generator_generators_hpp_included_ + +#include +#include +#include +#include +#include + +#include "../generator.hpp" + +namespace asio = boost::asio; +namespace bmi = boost::multi_index; + +namespace Keys { + +struct TypeKey { + std::string referenceFrame; + Resource::Generator::Type type; + + bool operator<(const TypeKey &o) const { + if (referenceFrame < o.referenceFrame) { return true; } + if (o.referenceFrame < referenceFrame) { return false; } + return type < o.type; + } + + TypeKey(const std::string &referenceFrame, Resource::Generator::Type type) + : referenceFrame(referenceFrame), type(type) + {} + + static TypeKey extract(const Generator &generator) { + const auto r(generator.resource()); + return { r.id.referenceFrame, r.generator.type }; + } +}; + +struct GroupKey { + std::string referenceFrame; + Resource::Generator::Type type; + std::string group; + + bool operator<(const GroupKey &o) const { + if (referenceFrame < o.referenceFrame) { return true; } + if (o.referenceFrame < referenceFrame) { return false; } + + if (group < o.group) { return true; } + if (o.group < group) { return false; } + + return type < o.type; + } + + GroupKey(const std::string &referenceFrame, Resource::Generator::Type type + , const std::string &group) + : referenceFrame(referenceFrame), type(type), group(group) + {} + + static GroupKey extract(const Generator &generator) { + const auto r(generator.resource()); + return { r.id.referenceFrame, r.generator.type, r.id.group }; + } + +}; + +} // namespace Keys + +class Generators::Detail + : public boost::noncopyable + , public GeneratorFinder +{ +public: + Detail(const Generators::Config &config + , const ResourceBackend::pointer &resourceBackend); + + void checkReady() const; + + Generator::pointer generator(Resource::Generator::Type generatorType + , const Resource::Id &resourceId + , bool noReadyCheck = false) const; + + Generator::list referenceFrame(const std::string &referenceFrame) const; + + std::vector listGroups(const std::string &referenceFrame + , Resource::Generator::Type type) + const; + + std::vector listIds(const std::string &referenceFrame + , Resource::Generator::Type type + , const std::string &group) const; + + void start(Arsenal &arsenal); + void stop(); + + inline const Config& config() const { return config_; } + + inline const DemRegistry& demRegistry() const { return *demRegistry_; } + + std::uint64_t update(); + + bool updatedSince(std::uint64_t timestamp) const; + + void listResources(std::ostream &os) const; + + void replace(const Generator::pointer &original + , const Generator::pointer &replacement); + + bool has(const Resource::Id &resourceId) const; + + bool isReady(const Resource::Id &resourceId) const; + + std::string url(const Resource::Id &resourceId + , GeneratorInterface::Interface iface) const; + + bool updatedSince(const Resource::Id &resourceId + , std::uint64_t timestamp, bool nothrow) const; + +private: + void registerSystemGenerators(); + + void update(const Resource::map &resources); + + void updater(); + void worker(std::size_t id); + void prepare(const Generator::pointer &generator); + + virtual Generator::pointer + findGenerator_impl(Resource::Generator::Type generatorType + , const Resource::Id &resourceId + , bool mustBeReady) const; + + const Config config_; + ResourceBackend::pointer resourceBackend_; + Arsenal *arsenal_; + + // resource updater stuff + std::thread updater_; + std::atomic running_; + std::atomic updateRequest_; + std::atomic lastUpdate_; + std::mutex updaterLock_; + std::condition_variable updaterCond_; + + struct ResourceIdIdx {}; + struct GroupIdx {}; + struct TypeIdx {}; + struct ReferenceFrameIdx {}; + + typedef boost::multi_index_container< + Generator::pointer + , bmi::indexed_by< + bmi::ordered_unique > + + , bmi::ordered_unique< + bmi::tag + , BOOST_MULTI_INDEX_CONST_MEM_FUN + (Generator, const Resource::Id&, id) + > + + , bmi::ordered_non_unique< + bmi::tag + , bmi::global_fun + > + + , bmi::ordered_non_unique< + bmi::tag + , bmi::global_fun + > + + , bmi::ordered_non_unique< + bmi::tag + , BOOST_MULTI_INDEX_CONST_MEM_FUN + (Generator, const std::string&, referenceFrameId) + > + > + + > GeneratorMap; + + + + // internals + mutable std::mutex lock_; + GeneratorMap serving_; + + std::atomic ready_; + std::atomic preparing_; + + // prepare stuff + asio::io_service ios_; + asio::io_service::work work_; + std::vector workers_; + + // DEM registry + DemRegistry::pointer demRegistry_; +}; + +#endif // mapproxy_generator_generators_hpp_included_ diff --git a/mapproxy/src/mapproxy/generator/metatile.cpp b/mapproxy/src/mapproxy/generator/metatile.cpp index 4cf5f28..767cca3 100644 --- a/mapproxy/src/mapproxy/generator/metatile.cpp +++ b/mapproxy/src/mapproxy/generator/metatile.cpp @@ -179,7 +179,8 @@ metatileFromDemImpl(const vts::TileId &tileId, Sink &sink, Arsenal &arsenal , const boost::optional &geoidGrid , const MaskTree &maskTree , const boost::optional &displaySize - , const HeightFunction::pointer &heightFunction) + , const HeightFunction::pointer &heightFunction + , vts::SubMesh::TextureMode textureMode) { auto blocks(metatileBlocks(resource, tileId)); @@ -192,6 +193,9 @@ metatileFromDemImpl(const vts::TileId &tileId, Sink &sink, Arsenal &arsenal vts::MetaTile metatile(tileId, rf.metaBinaryOrder); + const std::size_t internalTextureCount + (textureMode == vts::SubMesh::internal); + auto setChildren([&](const MetatileBlock &block , const vts::TileId &nodeId, vts::MetaNode &node) -> void @@ -412,6 +416,9 @@ metatileFromDemImpl(const vts::TileId &tileId, Sink &sink, Arsenal &arsenal // set credits node.updateCredits(resource.credits); + // texturing + node.internalTextureCount(internalTextureCount); + if (displaySize) { // use display size node.applyDisplaySize(true); @@ -464,12 +471,13 @@ metatileFromDem(const vts::TileId &tileId, Sink &sink, Arsenal &arsenal , const boost::optional &geoidGrid , const MaskTree &maskTree , const boost::optional &displaySize - , const HeightFunction::pointer &heightFunction) + , const HeightFunction::pointer &heightFunction + , vts::SubMesh::TextureMode textureMode) { return metatileFromDemImpl(tileId, sink, arsenal, resource, tileIndex , demDataset, geoidGrid, maskTree, displaySize - , heightFunction); + , heightFunction, textureMode); } @@ -481,10 +489,11 @@ metatileFromDem(const vts::TileId &tileId, Sink &sink, Arsenal &arsenal , const boost::optional &geoidGrid , const MaskTree &maskTree , const boost::optional &displaySize - , const HeightFunction::pointer &heightFunction) + , const HeightFunction::pointer &heightFunction + , vts::SubMesh::TextureMode textureMode) { return metatileFromDemImpl(tileId, sink, arsenal, resource, tileIndex , demDataset, geoidGrid, maskTree, displaySize - , heightFunction); + , heightFunction, textureMode); } diff --git a/mapproxy/src/mapproxy/generator/metatile.hpp b/mapproxy/src/mapproxy/generator/metatile.hpp index 9e3c172..b8226c9 100644 --- a/mapproxy/src/mapproxy/generator/metatile.hpp +++ b/mapproxy/src/mapproxy/generator/metatile.hpp @@ -47,7 +47,9 @@ vts::MetaTile metatileFromDem(const vts::TileId &tileId, Sink &sink , const boost::optional &displaySize = boost::none , const HeightFunction::pointer &heightFunction - = HeightFunction::pointer()); + = HeightFunction::pointer() + , vts::SubMesh::TextureMode textureMode + = vts::SubMesh::external); vts::MetaTile metatileFromDem(const vts::TileId &tileId, Sink &sink , Arsenal &arsenal @@ -59,6 +61,8 @@ vts::MetaTile metatileFromDem(const vts::TileId &tileId, Sink &sink , const boost::optional &displaySize = boost::none , const HeightFunction::pointer &heightFunction - = HeightFunction::pointer()); + = HeightFunction::pointer() + , vts::SubMesh::TextureMode textureMode + = vts::SubMesh::external); #endif // mapproxy_metatile_hpp_included_ diff --git a/mapproxy/src/mapproxy/generator/providers.hpp b/mapproxy/src/mapproxy/generator/providers.hpp new file mode 100644 index 0000000..48565e8 --- /dev/null +++ b/mapproxy/src/mapproxy/generator/providers.hpp @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2017 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef mapproxy_generator_providers_hpp_included_ +#define mapproxy_generator_providers_hpp_included_ + +#include + +#include "vts-libs/registry/extensions.hpp" +#include "vts-libs/vts/tileset/properties.hpp" +#include "vts-libs/vts/mesh.hpp" + +#include "../heightfunction.hpp" +#include "../support/mesh.hpp" +#include "../support/mmapped/tilesetindex.hpp" +#include "../generator.hpp" +#include "../definition.hpp" + +namespace vts = vtslibs::vts; +namespace vre = vtslibs::registry::extensions; + +namespace generator { + +class VtsTilesetProvider { +public: + virtual ~VtsTilesetProvider() {} + + /** Generates mesh and sends it to the output. + */ + Generator::Task generateMesh(const vts::TileId &tileId + , Sink &sink + , const SurfaceFileInfo &fileInfo + , vts::SubMesh::TextureMode textureMode + = vts::SubMesh::external) const; + + /** Generates metatile/debug node and sends it to the output. + */ + Generator::Task generateMetatile(const vts::TileId &tileId, Sink &sink + , const SurfaceFileInfo &fileInfo + , vts::SubMesh::TextureMode textureMode + = vts::SubMesh::external) const; + + /** Alias for surface file generation. + */ + Generator::Task generateFile(const FileInfo &fileInfo, Sink sink) const; + + /** Returns path to VTS file if supported. + */ + boost::optional path(vts::File file) const; + + /** Returns tileset properties. + */ + vts::FullTileSetProperties properties() const; + +private: + virtual Generator::Task + generateMesh_impl(const vts::TileId &tileId, Sink &sink + , const SurfaceFileInfo &fileInfo + , vts::SubMesh::TextureMode textureMode) const = 0; + + virtual Generator::Task + generateMetatile_impl(const vts::TileId &tileId, Sink &sink + , const SurfaceFileInfo &fileInfo + , vts::SubMesh::TextureMode textureMode) const = 0; + + virtual Generator::Task + generateFile_impl(const FileInfo &fileInfo, Sink sink) const = 0; + + virtual boost::optional path_impl(vts::File file) + const = 0; + + virtual vts::FullTileSetProperties properties_impl() const = 0; +}; + +class VtsAtlasProvider { +public: + virtual ~VtsAtlasProvider() {} + + /** Generates image/atlas and sends it to the output. + */ + Generator::Task generateAtlas(const vts::TileId &tileId, Sink &sink + , const Sink::FileInfo &sfi + , bool atlas = false) const; + +private: + /** Generates image/atlas and sends it to the output. + */ + virtual Generator::Task + generateAtlas_impl(const vts::TileId &tileId, Sink &sink + , const Sink::FileInfo &sfi + , bool atlas = false) const = 0; +}; + +// inlines + +inline Generator::Task +VtsTilesetProvider::generateMesh(const vts::TileId &tileId, Sink &sink + , const SurfaceFileInfo &fileInfo + , vts::SubMesh::TextureMode textureMode) + const +{ + return generateMesh_impl(tileId, sink, fileInfo, textureMode); +} + +inline Generator::Task +VtsTilesetProvider::generateMetatile(const vts::TileId &tileId, Sink &sink + , const SurfaceFileInfo &fileInfo + , vts::SubMesh::TextureMode + textureMode) + const +{ + return generateMetatile_impl(tileId, sink, fileInfo, textureMode); +} + +inline Generator::Task +VtsTilesetProvider::generateFile(const FileInfo &fileInfo, Sink sink) const +{ + return generateFile_impl(fileInfo, sink); +} + +inline boost::optional +VtsTilesetProvider::path(vts::File file) const +{ + return path_impl(file); +} + +inline vts::FullTileSetProperties VtsTilesetProvider::properties() const +{ + return properties_impl(); +} + +inline Generator::Task +VtsAtlasProvider::generateAtlas(const vts::TileId &tileId, Sink &sink + , const Sink::FileInfo &sfi + , bool atlas) const +{ + return generateAtlas_impl(tileId, sink, sfi, atlas); +} + +} // namespace generator + +#endif // mapproxy_generator_providers_hpp_included_ diff --git a/mapproxy/src/mapproxy/generator/registry.cpp b/mapproxy/src/mapproxy/generator/registry.cpp new file mode 100644 index 0000000..a226561 --- /dev/null +++ b/mapproxy/src/mapproxy/generator/registry.cpp @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2019 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "factory.hpp" + +Generator::Factory::pointer +Generator::Factory::findFactory(const Resource::Generator &type) +{ + const auto &r(registry()); + auto fregistry(r.find(type)); + if (fregistry == r.end()) { + LOGTHROW(err1, UnknownGenerator) + << "Unknown generator type <" << type << ">."; + } + return fregistry->second; +} + +void Generator::Factory::registerType(const Resource::Generator &type + , const pointer &factory) +{ + registry().insert(Registry::value_type(type, factory)); +} + +Generator::Factory::Registry& Generator::Factory::registry() +{ + static Registry registry; + return registry; +} diff --git a/mapproxy/src/mapproxy/generator/surface-dem.cpp b/mapproxy/src/mapproxy/generator/surface-dem.cpp index 05eeec5..b7f4035 100644 --- a/mapproxy/src/mapproxy/generator/surface-dem.cpp +++ b/mapproxy/src/mapproxy/generator/surface-dem.cpp @@ -228,7 +228,9 @@ vts::MapConfig SurfaceDem::mapConfig_impl(ResourceRoot root) const void SurfaceDem::generateMetatile(const vts::TileId &tileId , Sink &sink , const SurfaceFileInfo &fi - , Arsenal &arsenal) const + , Arsenal &arsenal + , vts::SubMesh::TextureMode textureMode) + const { sink.checkAborted(); @@ -237,7 +239,7 @@ void SurfaceDem::generateMetatile(const vts::TileId &tileId return; } - auto metatile(generateMetatileImpl(tileId, sink, arsenal)); + auto metatile(generateMetatileImpl(tileId, sink, arsenal, textureMode)); // write metatile to stream std::ostringstream os; @@ -247,13 +249,14 @@ void SurfaceDem::generateMetatile(const vts::TileId &tileId vts::MetaTile SurfaceDem::generateMetatileImpl(const vts::TileId &tileId - , Sink &sink - , Arsenal &arsenal) const + , Sink &sink, Arsenal &arsenal + , vts::SubMesh::TextureMode textureMode) const { return metatileFromDem(tileId, sink, arsenal, resource() , index_->tileIndex, dem_.dataset , dem_.geoidGrid, maskTree_, boost::none - , definition_.heightFunction); + , definition_.heightFunction + , textureMode); } namespace { diff --git a/mapproxy/src/mapproxy/generator/surface-dem.hpp b/mapproxy/src/mapproxy/generator/surface-dem.hpp index e9adf85..269de62 100644 --- a/mapproxy/src/mapproxy/generator/surface-dem.hpp +++ b/mapproxy/src/mapproxy/generator/surface-dem.hpp @@ -24,8 +24,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef mapproxy_generator_surface_spheroid_hpp_included_ -#define mapproxy_generator_surface_spheroid_hpp_included_ +#ifndef mapproxy_generator_surface_dem_hpp_included_ +#define mapproxy_generator_surface_dem_hpp_included_ #include "vts-libs/vts/tileset/tilesetindex.hpp" #include "vts-libs/vts/tileset/properties.hpp" @@ -54,7 +54,9 @@ class SurfaceDem : public SurfaceBase { virtual void generateMetatile(const vts::TileId &tileId , Sink &sink , const SurfaceFileInfo &fileInfo - , Arsenal &arsenal) const; + , Arsenal &arsenal + , vts::SubMesh::TextureMode textureMode) + const; virtual AugmentedMesh generateMeshImpl(const vts::NodeInfo &nodeInfo, Sink &sink @@ -66,8 +68,10 @@ class SurfaceDem : public SurfaceBase { , Arsenal &arsenal) const; vts::MetaTile generateMetatileImpl(const vts::TileId &tileId - , Sink &sink - , Arsenal &arsenal) const; + , Sink &sink, Arsenal &arsenal + , vts::SubMesh::TextureMode textureMode + = vts::SubMesh::external) + const; void addToRegistry(); @@ -85,4 +89,4 @@ class SurfaceDem : public SurfaceBase { } // namespace generator -#endif // mapproxy_generator_surface_spheroid_hpp_included_ +#endif // mapproxy_generator_surface_dem_hpp_included_ diff --git a/mapproxy/src/mapproxy/generator/surface-meta.cpp b/mapproxy/src/mapproxy/generator/surface-meta.cpp new file mode 100644 index 0000000..06735c2 --- /dev/null +++ b/mapproxy/src/mapproxy/generator/surface-meta.cpp @@ -0,0 +1,229 @@ +/** + * Copyright (c) 2019 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + +#include "utility/premain.hpp" +#include "utility/raise.hpp" +#include "utility/path.hpp" + +#include "geometry/mesh.hpp" + +#include "geo/coordinates.hpp" + +#include "imgproc/rastermask/mappedqtree.hpp" + +#include "jsoncpp/json.hpp" +#include "jsoncpp/as.hpp" + +#include "vts-libs/storage/fstreams.hpp" +#include "vts-libs/registry/json.hpp" +#include "vts-libs/registry/py.hpp" +#include "vts-libs/vts/io.hpp" +#include "vts-libs/vts/nodeinfo.hpp" +#include "vts-libs/vts/tileset/config.hpp" +#include "vts-libs/vts/metatile.hpp" +#include "vts-libs/vts/csconvertor.hpp" +#include "vts-libs/vts/math.hpp" +#include "vts-libs/vts/mesh.hpp" +#include "vts-libs/vts/opencv/navtile.hpp" +#include "vts-libs/vts/service.hpp" +#include "vts-libs/vts/tileset/tilesetindex.hpp" + +#include "../error.hpp" +#include "../support/metatile.hpp" +#include "../support/mesh.hpp" +#include "../support/srs.hpp" +#include "../support/geo.hpp" +#include "../support/grid.hpp" +#include "../support/coverage.hpp" +#include "../support/tileindex.hpp" + +#include "surface-meta.hpp" +#include "factory.hpp" + +namespace fs = boost::filesystem; +namespace vr = vtslibs::registry; +namespace vs = vtslibs::storage; +namespace vts = vtslibs::vts; + +namespace generator { + +namespace { + +struct Factory : Generator::Factory { + virtual Generator::pointer create(const Generator::Params ¶ms) + { + return std::make_shared(params); + } + +private: + static utility::PreMain register_; +}; + +utility::PreMain Factory::register_([]() +{ + Generator::registerType(std::make_shared()); +}); + +} // namespace + +SurfaceMeta::SurfaceMeta(const Params ¶ms) + : Generator(params) + , definition_(resource().definition()) +{} + +SurfaceMeta::~SurfaceMeta() {} + +void SurfaceMeta::prepare_impl(Arsenal&) +{ + LOG(info2) << "Preparing <" << id() << ">."; + + // find surface and tms generators (no need to be ready but fail when not + // present) + surface_ = otherGenerator + (Resource::Generator::Type::surface + , Resource::Id(referenceFrameId(), definition_.surface), false, true); + + tms_ = otherGenerator + (Resource::Generator::Type::tms + , Resource::Id(referenceFrameId(), definition_.tms), false, true); + + ts_ = surface_->getProvider(); + if (!ts_) { + LOGTHROW(err2, std::runtime_error) + << "Driver for <" + << Resource::Id(referenceFrameId(), definition_.surface) + << "> doesn't provide VTS tileset support."; + } + + atlas_ = tms_->getProvider(); + if (!atlas_) { + LOGTHROW(err2, std::runtime_error) + << "Driver for <" + << Resource::Id(referenceFrameId(), definition_.surface) + << "> doesn't provide VTS atlas support."; + } +} + +vts::MapConfig SurfaceMeta::mapConfig_impl(ResourceRoot root) const +{ + const auto path(prependRoot(fs::path(), resource(), root)); + + auto mc(vts::mapConfig + (ts_->properties(), resource().registry, {}, path)); + + // force 2d interface existence + mc.surfaces.front().has2dInterface = true; + + // add local services + vts::service::addLocal(mc, path); + return mc; +} + +typedef vts::TileIndex::Flag TiFlag; + +Generator::Task SurfaceMeta::tileindex(const SurfaceFileInfo &fi, Sink&) + const +{ + const auto path(ts_->path(vts::File::tileIndex).value()); + + return [=](Sink &sink, Arsenal&) { + // rewrite original tileindex to contain atlas + vts::tileset::Index index(referenceFrame().metaBinaryOrder); + vts::tileset::loadTileSetIndex(index, path); + + auto &ti(index.tileIndex); + + // abuse combine-with-itself to modify flags + const auto &combiner([&](TiFlag::value_type o, TiFlag::value_type) + -> TiFlag::value_type + { + return TiFlag::isReal(o) ? (o | TiFlag::atlas) : o; + }); + ti.combine(ti, combiner); + + std::ostringstream os; + vts::tileset::saveTileSetIndex(index, os); + sink.content(os.str(), fi.sinkFileInfo()); + }; +} + +Generator::Task SurfaceMeta +::generateFile_impl(const FileInfo &fileInfo, Sink &sink) const +{ + if (fileInfo.interface.interface != GeneratorInterface::Interface::vts) { + return ts_->generateFile(fileInfo, sink); + } + + SurfaceFileInfo fi(fileInfo); + + switch (fi.type) { + + case SurfaceFileInfo::Type::file: { + + switch (fi.fileType) { + case vts::File::tileIndex: + return tileindex(fi, sink); + default: break; + } break; + + } break; + + case SurfaceFileInfo::Type::tile: { + + switch (fi.tileType) { + case vts::TileFile::meta: + return ts_->generateMetatile(fi.tileId, sink, fi + , vts::SubMesh::internal); + + case vts::TileFile::mesh: + return ts_->generateMesh(fi.tileId, sink, fi + , vts::SubMesh::internal); + + case vts::TileFile::atlas: + return atlas_->generateAtlas + (fi.tileId, sink, fi.sinkFileInfo() + , (fi.flavor == vts::FileFlavor::raw)); + + default: break; + } break; + + } break; + + default: break; + } + + return ts_->generateFile(fileInfo, sink); +} + +} // namespace generator diff --git a/mapproxy/src/mapproxy/generator/surface-meta.hpp b/mapproxy/src/mapproxy/generator/surface-meta.hpp new file mode 100644 index 0000000..0a9c485 --- /dev/null +++ b/mapproxy/src/mapproxy/generator/surface-meta.hpp @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2019 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef mapproxy_generator_surface_meta_hpp_included_ +#define mapproxy_generator_surface_meta_hpp_included_ + +#include "../generator.hpp" +#include "../definition.hpp" + +#include "providers.hpp" + +namespace vts = vtslibs::vts; +namespace vr = vtslibs::registry; + +namespace generator { + +class SurfaceMeta : public Generator { +public: + SurfaceMeta(const Params ¶ms); + + ~SurfaceMeta(); + + typedef resource::SurfaceMeta Definition; + +private: + virtual void prepare_impl(Arsenal &arsenal); + virtual vts::MapConfig mapConfig_impl(ResourceRoot root) const ; + + virtual Task generateFile_impl(const FileInfo &fileInfo + , Sink &sink) const; + + Generator::Task tileindex(const SurfaceFileInfo &fileInfo, Sink &sink) + const; + + const Definition &definition_; + + pointer surface_; + pointer tms_; + + /** Pointer owned by surface. + */ + VtsTilesetProvider *ts_; + + /** Pointer owned by tms. + */ + VtsAtlasProvider *atlas_; +}; + +} // namespace generator + +#endif // mapproxy_generator_surface_meta_hpp_included_ diff --git a/mapproxy/src/mapproxy/generator/surface-spheroid.cpp b/mapproxy/src/mapproxy/generator/surface-spheroid.cpp index 5fdea41..6733cce 100644 --- a/mapproxy/src/mapproxy/generator/surface-spheroid.cpp +++ b/mapproxy/src/mapproxy/generator/surface-spheroid.cpp @@ -241,7 +241,9 @@ const int metatileSamplesPerTile(8); void SurfaceSpheroid::generateMetatile(const vts::TileId &tileId , Sink &sink , const SurfaceFileInfo &fi - , Arsenal&) const + , Arsenal& + , vts::SubMesh::TextureMode textureMode) + const { sink.checkAborted(); @@ -293,6 +295,10 @@ void SurfaceSpheroid::generateMetatile(const vts::TileId &tileId } }); + // set internal texture count to 1 if we are generating internal textures + const std::size_t internalTextureCount + (textureMode == vts::SubMesh::internal); + for (const auto &block : blocks) { const auto &view(block.view); const auto &extents(block.extents); @@ -426,6 +432,9 @@ void SurfaceSpheroid::generateMetatile(const vts::TileId &tileId // set credits node.updateCredits(resource().credits); + // texturing + node.internalTextureCount(internalTextureCount); + // mesh is (almost) flat -> use tile area if (geometry) { node.applyTexelSize(true); diff --git a/mapproxy/src/mapproxy/generator/surface-spheroid.hpp b/mapproxy/src/mapproxy/generator/surface-spheroid.hpp index b15d263..81dee0a 100644 --- a/mapproxy/src/mapproxy/generator/surface-spheroid.hpp +++ b/mapproxy/src/mapproxy/generator/surface-spheroid.hpp @@ -49,7 +49,9 @@ class SurfaceSpheroid : public SurfaceBase { virtual void generateMetatile(const vts::TileId &tileId , Sink &sink , const SurfaceFileInfo &fileInfo - , Arsenal &arsenal) const; + , Arsenal &arsenal + , vts::SubMesh::TextureMode textureMode) + const; virtual AugmentedMesh generateMeshImpl(const vts::NodeInfo &nodeInfo , Sink &sink diff --git a/mapproxy/src/mapproxy/generator/surface.cpp b/mapproxy/src/mapproxy/generator/surface.cpp index 3915f04..7d0bce1 100644 --- a/mapproxy/src/mapproxy/generator/surface.cpp +++ b/mapproxy/src/mapproxy/generator/surface.cpp @@ -36,6 +36,7 @@ #include "utility/raise.hpp" #include "utility/path.hpp" #include "utility/gzipper.hpp" +#include "utility/cppversion.hpp" #include "imgproc/rastermask/cvmat.hpp" #include "imgproc/png.hpp" @@ -71,6 +72,7 @@ #include "files.hpp" #include "surface.hpp" +#include "providers.hpp" namespace fs = boost::filesystem; namespace bio = boost::iostreams; @@ -94,6 +96,75 @@ terrainSupport(const Generator::Params ¶ms) } // namespace +class SurfaceProvider + : public Generator::Provider + , public VtsTilesetProvider +{ +public: + SurfaceProvider(SurfaceBase &surface) + : surface_(surface) + {} + +private: + Generator::Task generateMesh_impl(const vts::TileId &tileId, Sink& + , const SurfaceFileInfo &fileInfo + , vts::SubMesh::TextureMode textureMode) + const override + { + return [=](Sink &sink, Arsenal &arsenal) { + surface_.generateMesh + (tileId, sink, fileInfo, arsenal, textureMode); + }; + } + + Generator::Task generateMetatile_impl(const vts::TileId &tileId, Sink& + , const SurfaceFileInfo &fileInfo + , vts::SubMesh::TextureMode + textureMode) + const override + { + return [=](Sink &sink, Arsenal &arsenal) { + if (fileInfo.flavor == vts::FileFlavor::debug) { + // debug metanode + surface_.generateDebugNode + (tileId, sink, fileInfo, arsenal, textureMode); + } else { + // real metatile + surface_.generateMetatile + (tileId, sink, fileInfo, arsenal, textureMode); + } + }; + } + + Generator::Task generateFile_impl(const FileInfo &fileInfo, Sink sink) + const override + { + return surface_.generateFile(fileInfo, sink); + } + + /** Returns path to VTS file if supported. + */ + boost::optional path_impl(vts::File file) const override + { + switch (file) { + case vts::File::config: + case vts::File::tileIndex: + break; + + default: + return boost::none; + } + + return surface_.filePath(file); + } + + vts::FullTileSetProperties properties_impl() const { + return surface_.properties_; + } + + SurfaceBase &surface_; +}; + fs::path SurfaceBase::filePath(vts::File fileType) const { switch (fileType) { @@ -111,7 +182,9 @@ SurfaceBase::SurfaceBase(const Params ¶ms) : Generator(params, terrainSupport(params)) , definition_(resource().definition()) , tms_(params.resource.referenceFrame->findExtension()) -{} +{ + setProvider(std::make_unique(*this)); +} bool SurfaceBase::loadFiles(const Definition &definition) { @@ -281,7 +354,7 @@ ::generateFile_impl(const FileInfo &fileInfo, Sink &sink) const case SurfaceFileInfo::Type::tile: { switch (fi.tileType) { case vts::TileFile::meta: - return[=](Sink &sink, Arsenal &arsenal) { + return [=](Sink &sink, Arsenal &arsenal) { if (fi.flavor == vts::FileFlavor::debug) { // debug metanode generateDebugNode(fi.tileId, sink, fi, arsenal); @@ -292,7 +365,7 @@ ::generateFile_impl(const FileInfo &fileInfo, Sink &sink) const }; case vts::TileFile::mesh: - return[=](Sink &sink, Arsenal &arsenal) { + return [=](Sink &sink, Arsenal &arsenal) { generateMesh(fi.tileId, sink, fi, arsenal); }; @@ -302,19 +375,19 @@ ::generateFile_impl(const FileInfo &fileInfo, Sink &sink) const break; case vts::TileFile::navtile: - return[=](Sink &sink, Arsenal &arsenal) { + return [=](Sink &sink, Arsenal &arsenal) { generateNavtile(fi.tileId, sink, fi, arsenal); }; break; case vts::TileFile::meta2d: - return[=](Sink &sink, Arsenal &arsenal) { + return [=](Sink &sink, Arsenal &arsenal) { generate2dMetatile(fi.tileId, sink, fi, arsenal); }; break; case vts::TileFile::mask: - return[=](Sink &sink, Arsenal &arsenal) { + return [=](Sink &sink, Arsenal &arsenal) { generate2dMask(fi.tileId, sink, fi, arsenal); }; break; @@ -325,7 +398,7 @@ ::generateFile_impl(const FileInfo &fileInfo, Sink &sink) const break; case vts::TileFile::credits: - return[=](Sink &sink, Arsenal &arsenal) { + return [=](Sink &sink, Arsenal &arsenal) { generateCredits(fi.tileId, sink, fi, arsenal); }; break; @@ -360,7 +433,8 @@ ::generateFile_impl(const FileInfo &fileInfo, Sink &sink) const void SurfaceBase::generateMesh(const vts::TileId &tileId , Sink &sink , const SurfaceFileInfo &fi - , Arsenal &arsenal) const + , Arsenal &arsenal + , vts::SubMesh::TextureMode textureMode) const { auto flags(index_->tileIndex.get(tileId)); if (!vts::TileIndex::Flag::isReal(flags)) { @@ -385,7 +459,8 @@ void SurfaceBase::generateMesh(const vts::TileId &tileId vts::Mesh mesh(false); if (!lm.mesh.vertices.empty()) { // local mesh is valid -> add as a submesh into output mesh - auto &sm(addSubMesh(mesh, lm.mesh, nodeInfo, lm.geoidGrid)); + auto &sm(addSubMesh(mesh, lm.mesh, nodeInfo, lm.geoidGrid + , textureMode)); if (lm.textureLayerId) { sm.textureLayer = lm.textureLayerId; } @@ -488,10 +563,25 @@ void SurfaceBase::generateCredits(const vts::TileId& void SurfaceBase::generateDebugNode(const vts::TileId &tileId , Sink &sink , const SurfaceFileInfo &fi - , Arsenal &) const + , Arsenal & + , vts::SubMesh::TextureMode textureMode) + const { // generate debug metanode - const auto debugNode(vts::getNodeDebugInfo(index_->tileIndex, tileId)); + const auto debugNode([&]() + { + if (textureMode == vts::SubMesh::external) { + return vts::getNodeDebugInfo(index_->tileIndex, tileId); + } + + // internal texture -> add atlas flag + return vts::getNodeDebugInfo + (index_->tileIndex, tileId + , [](vts::TileIndex::Flag::value_type f) { + return (vts::TileIndex::Flag::isReal(f) + ? (f | vts::TileIndex::Flag::atlas) : f); + }); + }()); std::ostringstream os; vts::saveDebug(os, debugNode); @@ -564,7 +654,7 @@ ::terrainInterface(const FileInfo &fileInfo, Sink &sink) const break; case TerrainFileInfo::Type::tile: - return[=](Sink &sink, Arsenal &arsenal) { + return [=](Sink &sink, Arsenal &arsenal) { generateTerrain(fi.tileId, sink, fi, arsenal, tms); }; diff --git a/mapproxy/src/mapproxy/generator/surface.hpp b/mapproxy/src/mapproxy/generator/surface.hpp index 53272f4..7d43e14 100644 --- a/mapproxy/src/mapproxy/generator/surface.hpp +++ b/mapproxy/src/mapproxy/generator/surface.hpp @@ -71,12 +71,16 @@ class SurfaceBase : public Generator { virtual void generateMetatile(const vts::TileId &tileId , Sink &sink , const SurfaceFileInfo &fileInfo - , Arsenal &arsenal) const = 0; + , Arsenal &arsenal + , vts::SubMesh::TextureMode textureMode + = vts::SubMesh::external) const = 0; void generateMesh(const vts::TileId &tileId , Sink &sink , const SurfaceFileInfo &fileInfo - , Arsenal &arsenal) const; + , Arsenal &arsenal + , vts::SubMesh::TextureMode textureMode + = vts::SubMesh::external) const; void generate2dMask(const vts::TileId &tileId , Sink &sink @@ -101,7 +105,9 @@ class SurfaceBase : public Generator { void generateDebugNode(const vts::TileId &tileId , Sink &sink , const SurfaceFileInfo &fileInfo - , Arsenal &arsenal) const; + , Arsenal &arsenal + , vts::SubMesh::TextureMode textureMode + = vts::SubMesh::external) const; /** Generic mesh generation, used by both surface and terrain interface. */ @@ -130,6 +136,8 @@ class SurfaceBase : public Generator { const Definition &definition_; const vre::Tms *tms_; + + friend class SurfaceProvider; }; } // namespace generator diff --git a/mapproxy/src/mapproxy/generator/tms-bing.cpp b/mapproxy/src/mapproxy/generator/tms-bing.cpp index 9abfb7b..ab9f26d 100644 --- a/mapproxy/src/mapproxy/generator/tms-bing.cpp +++ b/mapproxy/src/mapproxy/generator/tms-bing.cpp @@ -89,7 +89,6 @@ TmsBing::TmsBing(const Params ¶ms) void TmsBing::prepare_impl(Arsenal&) { LOG(info2) << "Preparing <" << id() << ">."; - makeReady(); } namespace { diff --git a/mapproxy/src/mapproxy/generator/tms-raster-base.cpp b/mapproxy/src/mapproxy/generator/tms-raster-base.cpp index 56d69ab..ed381fb 100644 --- a/mapproxy/src/mapproxy/generator/tms-raster-base.cpp +++ b/mapproxy/src/mapproxy/generator/tms-raster-base.cpp @@ -33,6 +33,7 @@ #include "files.hpp" #include "tms-raster-base.hpp" +#include "providers.hpp" namespace uq = utility::query; @@ -53,6 +54,32 @@ wmtsSupport(const Generator::Params ¶ms } // namespace +class AtlasProvider + : public Generator::Provider + , public VtsAtlasProvider +{ +public: + AtlasProvider(TmsRasterBase &tms) + : tms_(tms) + {} + +private: + Generator::Task generateAtlas_impl(const vts::TileId &tileId, Sink& + , const Sink::FileInfo &sfi + , bool atlas) const override + { + TmsRasterBase::ImageFlags imageFlags; + imageFlags.forceFormat = true; + imageFlags.atlas = atlas; + return[=](Sink &sink, Arsenal &arsenal) { + tms_.generateTileImage(tileId, sfi, RasterFormat::jpg + , sink, arsenal, imageFlags); + }; + } + + TmsRasterBase &tms_; +}; + TmsRasterBase ::TmsRasterBase(const Params ¶ms , const boost::optional &format) @@ -61,7 +88,9 @@ ::TmsRasterBase(const Params ¶ms , wmts_(properties().isSupported(GeneratorInterface::Interface::wmts) ? params.resource.referenceFrame->findExtension() : nullptr) -{} +{ + setProvider(std::make_unique(*this)); +} Generator::Task TmsRasterBase ::generateFile_impl(const FileInfo &fileInfo, Sink &sink) const @@ -147,8 +176,10 @@ ::wmtsInterface(const FileInfo &fileInfo, Sink &sink) const case WmtsFileInfo::Type::image: return [=](Sink &sink, Arsenal &arsenal) { + ImageFlags imageFlags; + imageFlags.dontOptimize = true; generateTileImage(fi.tileId, fi.sinkFileInfo(), fi.format - , sink, arsenal, true); + , sink, arsenal, imageFlags); }; case WmtsFileInfo::Type::capabilities: diff --git a/mapproxy/src/mapproxy/generator/tms-raster-base.hpp b/mapproxy/src/mapproxy/generator/tms-raster-base.hpp index e5063d4..6e79a9e 100644 --- a/mapproxy/src/mapproxy/generator/tms-raster-base.hpp +++ b/mapproxy/src/mapproxy/generator/tms-raster-base.hpp @@ -47,11 +47,24 @@ class TmsRasterBase : public Generator { = boost::none); protected: + struct ImageFlags { + bool dontOptimize; + bool atlas; + bool forceFormat; + + ImageFlags() + : dontOptimize(false), atlas(false), forceFormat(false) + {} + bool checkFormat(RasterFormat requested, RasterFormat configured) + const; + }; + virtual void generateTileImage(const vts::TileId &tileId - , Sink::FileInfo &&sfi + , const Sink::FileInfo &sfi , RasterFormat format , Sink &sink, Arsenal &arsenal - , bool dontOptimize = false) const = 0; + , const ImageFlags &imageFlags + = ImageFlags()) const = 0; private: virtual Task generateFile_impl(const FileInfo &fileInfo @@ -70,8 +83,18 @@ class TmsRasterBase : public Generator { RasterFormat format_; const vre::Wmts *wmts_; + + friend class AtlasProvider; }; +inline bool TmsRasterBase::ImageFlags::checkFormat(RasterFormat requested + , RasterFormat configured) + const +{ + if (atlas || forceFormat) { return true; } + return (requested == configured); +} + } // namespace generator #endif // mapproxy_generator_tms_raster_base_hpp_included_ diff --git a/mapproxy/src/mapproxy/generator/tms-raster-synthetic.cpp b/mapproxy/src/mapproxy/generator/tms-raster-synthetic.cpp index e67de80..70645b8 100644 --- a/mapproxy/src/mapproxy/generator/tms-raster-synthetic.cpp +++ b/mapproxy/src/mapproxy/generator/tms-raster-synthetic.cpp @@ -49,6 +49,7 @@ #include "../support/metatile.hpp" #include "../support/tileindex.hpp" #include "../support/revision.hpp" +#include "../support/atlas.hpp" #include "tms-raster-synthetic.hpp" #include "factory.hpp" @@ -222,13 +223,14 @@ ::generateVtsFile_impl(const FileInfo &fileInfo, Sink &sink) const } void TmsRasterSynthetic::generateTileImage(const vts::TileId &tileId - , Sink::FileInfo &&sfi + , const Sink::FileInfo &sfi , RasterFormat format , Sink &sink, Arsenal& - , bool dontOptimize) const + , const ImageFlags &imageFlags) + const { sink.checkAborted(); - if (format != definition_.format) { + if (!imageFlags.checkFormat(format, definition_.format)) { return sink.error (utility::makeError ("Format <%s> is not supported by this resource (%s)." @@ -244,7 +246,7 @@ void TmsRasterSynthetic::generateTileImage(const vts::TileId &tileId bool valid(true); if (!nodeInfo.productive()) { - if (!dontOptimize) { + if (!imageFlags.dontOptimize) { return sink.error (utility::makeError("No valid data.")); } @@ -260,22 +262,8 @@ void TmsRasterSynthetic::generateTileImage(const vts::TileId &tileId , vr::BoundLayer::tileWidth , cv::Vec3b(0, 0, 0))); - // serialize - std::vector buf; - switch (format) { - case RasterFormat::jpg: - // TODO: configurable quality - cv::imencode(".jpg", tile, buf - , { cv::IMWRITE_JPEG_QUALITY, 75 }); - break; - - case RasterFormat::png: - cv::imencode(".png", tile, buf - , { cv::IMWRITE_PNG_COMPRESSION, 9 }); - break; - } - - sink.content(buf, sfi); + // send image to client + sendImage(tile, sfi, format, imageFlags.atlas, sink); } void TmsRasterSynthetic::generateTileMask(const vts::TileId &tileId diff --git a/mapproxy/src/mapproxy/generator/tms-raster-synthetic.hpp b/mapproxy/src/mapproxy/generator/tms-raster-synthetic.hpp index 42af120..1f5f3c6 100644 --- a/mapproxy/src/mapproxy/generator/tms-raster-synthetic.hpp +++ b/mapproxy/src/mapproxy/generator/tms-raster-synthetic.hpp @@ -70,9 +70,10 @@ class TmsRasterSynthetic , Sink &sink) const; virtual void - generateTileImage(const vts::TileId &tileId, Sink::FileInfo &&fi - , RasterFormat format, Sink &sink, Arsenal &arsenal - , bool dontOptimize = false) const; + generateTileImage(const vts::TileId &tileId, const Sink::FileInfo &fi + , RasterFormat format + , Sink &sink, Arsenal &arsenal + , const ImageFlags &imageFlags = ImageFlags()) const; virtual cv::Mat generateTileImage(const vts::TileId &tileId) const = 0; diff --git a/mapproxy/src/mapproxy/generator/tms-raster.cpp b/mapproxy/src/mapproxy/generator/tms-raster.cpp index a8a76f1..23afc09 100644 --- a/mapproxy/src/mapproxy/generator/tms-raster.cpp +++ b/mapproxy/src/mapproxy/generator/tms-raster.cpp @@ -51,6 +51,7 @@ #include "../support/mmapped/qtree.hpp" #include "../support/mmapped/qtree-rasterize.hpp" #include "../support/revision.hpp" +#include "../support/atlas.hpp" #include "../support/wmts.hpp" #include "tms-raster.hpp" @@ -173,7 +174,6 @@ void TmsRaster::prepare_impl(Arsenal&) index_ = boost::in_place(deliveryIndexPath); // done - makeReady(); return; } @@ -204,8 +204,6 @@ void TmsRaster::prepare_impl(Arsenal&) // some invalid pixels hasMetatiles_ = !ds.allValid(); } - - makeReady(); } RasterFormat TmsRaster::format() const @@ -340,7 +338,7 @@ Generator::Task TmsRaster::generateVtsFile_impl(const FileInfo &fileInfo case TmsFileInfo::Type::image: { return[=](Sink &sink, Arsenal &arsenal) { generateTileImage(fi.tileId, fi.sinkFileInfo(), fi.format - , sink, arsenal, false); + , sink, arsenal, ImageFlags()); }; } @@ -365,35 +363,21 @@ Generator::Task TmsRaster::generateVtsFile_impl(const FileInfo &fileInfo } void TmsRaster::generateTileImage(const vts::TileId &tileId - , Sink::FileInfo &&fi + , const Sink::FileInfo &fi , RasterFormat format , Sink &sink, Arsenal &arsenal - , bool dontOptimize) const + , const ImageFlags &imageFlags) const { sink.checkAborted(); const auto &serialize([&](const cv::Mat &tile, const DatasetDesc &ds) -> void { - // serialize - std::vector buf; - switch (format) { - case RasterFormat::jpg: - // TODO: configurable quality - cv::imencode(".jpg", tile, buf - , { cv::IMWRITE_JPEG_QUALITY, 75 }); - break; - - case RasterFormat::png: - cv::imencode(".png", tile, buf - , { cv::IMWRITE_PNG_COMPRESSION, 9 }); - break; - } - - sink.content(buf, fi.setMaxAge(ds.maxAge)); + sendImage(tile, Sink::FileInfo(fi).setMaxAge(ds.maxAge) + , format, imageFlags.atlas, sink); }); - if (format != this->format()) { + if (!imageFlags.checkFormat(format, this->format())) { return sink.error (utility::makeError ("Format <%s> is not supported by this resource (%s)." @@ -410,7 +394,7 @@ void TmsRaster::generateTileImage(const vts::TileId &tileId if (!nodeInfo.productive() || (index_ && !vts::TileIndex::Flag::isReal(index_->get(tileId)))) { - if (!dontOptimize) { + if (!imageFlags.dontOptimize) { return sink.error (utility::makeError("No valid data.")); } @@ -433,7 +417,7 @@ void TmsRaster::generateTileImage(const vts::TileId &tileId // * transparent: we cannot report transparecny for empty/full image // * dontOptimize set: we are forbidden to return empty/full image const auto operation - ((ds.dynamic || transparent() || dontOptimize) + ((ds.dynamic || transparent() || imageFlags.dontOptimize) ? GdalWarper::RasterRequest::Operation::imageNoOpt : GdalWarper::RasterRequest::Operation::image); diff --git a/mapproxy/src/mapproxy/generator/tms-raster.hpp b/mapproxy/src/mapproxy/generator/tms-raster.hpp index 57de822..75cf1e9 100644 --- a/mapproxy/src/mapproxy/generator/tms-raster.hpp +++ b/mapproxy/src/mapproxy/generator/tms-raster.hpp @@ -99,10 +99,11 @@ class TmsRaster , Sink &sink) const; virtual void generateTileImage(const vts::TileId &tileId - , Sink::FileInfo &&fi + , const Sink::FileInfo &fi , RasterFormat format , Sink &sink, Arsenal &arsenal - , bool dontOptimize) const; + , const ImageFlags &imageFlags + = ImageFlags()) const; void generateTileMask(const vts::TileId &tileId , const TmsFileInfo &fi diff --git a/mapproxy/src/mapproxy/resource.cpp b/mapproxy/src/mapproxy/resource.cpp index 7ba1a9d..bafe95f 100644 --- a/mapproxy/src/mapproxy/resource.cpp +++ b/mapproxy/src/mapproxy/resource.cpp @@ -716,3 +716,12 @@ std::istream& operator>>(std::istream &is, GeneratorInterface &gi) return is; } +Resource::Id::list Resource::needsResources() const +{ + // fetch list of needed resource IDs and inject reference frame + auto neededIds(definition_->needsResources()); + for (auto &neededId : neededIds) { + neededId.referenceFrame = id.referenceFrame; + } + return neededIds; +} diff --git a/mapproxy/src/mapproxy/resource.hpp b/mapproxy/src/mapproxy/resource.hpp index d71dd04..fdffe96 100644 --- a/mapproxy/src/mapproxy/resource.hpp +++ b/mapproxy/src/mapproxy/resource.hpp @@ -51,61 +51,8 @@ namespace Json { class Value; } enum class Changed { yes, no, safely, withRevisionBump }; -/** Base of all resource definitions. - */ -class DefinitionBase { -public: - typedef std::shared_ptr pointer; - virtual ~DefinitionBase() {} - - void from(const Json::Value &value) { from_impl(value); } - void to(Json::Value &value) const { to_impl(value); } - Changed changed(const DefinitionBase &other) const { - return changed_impl(other); - } - - /** Are credits frozen in the resources's dataset? - */ - bool frozenCredits() const { return frozenCredits_impl(); } - - /** Returns true if resource needs lod and tile ranges. - */ - bool needsRanges() const { return needsRanges_impl(); } - - template const T& as() const { - if (const auto *value = dynamic_cast(this)) { - return *value; - } - LOGTHROW(err1, Error) - << "Incompatible resource definitions: cannot convert <" - << typeid(*this).name() << "> into <" << typeid(T).name() << ">."; - throw; - } - -private: - /** Fills in this definition from given input value. - */ - virtual void from_impl(const Json::Value &value) = 0; - - /** Fills in this definition into given output value. - */ - virtual void to_impl(Json::Value &value) const = 0; - - /** Compares this resource definition the other one and check whether they - * are basically the same. The definitions can differ but the difference - * must not affect resource generation. - */ - virtual Changed changed_impl(const DefinitionBase &other) const = 0; - - /** If generated dataset freezes credit info into its published data then - * credits cannot be changed. - */ - virtual bool frozenCredits_impl() const { return true; } - - /** Returns true if resource needs lod and tile ranges. - */ - virtual bool needsRanges_impl() const { return true; } -}; +// fwd +class DefinitionBase; namespace vr = vtslibs::registry; namespace vts = vtslibs::vts; @@ -159,8 +106,12 @@ struct Resource { Id(const std::string &referenceFrame, const std::string &group , const std::string &id) : referenceFrame(referenceFrame), group(group), id(id) {} + + Id(const std::string &referenceFrame, const Id &id) + : referenceFrame(referenceFrame), group(id.group), id(id.id) {} bool operator<(const Id &o) const; bool operator==(const Id &o) const; + bool operator!=(const Id &o) const; typedef std::vector list; }; @@ -178,6 +129,7 @@ struct Resource { : type(type), driver(driver) {} bool operator<(const Generator &o) const; bool operator==(const Generator &o) const; + bool operator!=(const Generator &o) const; template static Generator from() { return { Definition::type, Definition::driverName }; @@ -203,15 +155,11 @@ struct Resource { FileClassSettings fileClassSettings; - DefinitionBase::pointer definition() const { - return definition_; - } + std::shared_ptr definition() const { return definition_; } - template const T& definition() const { - return definition_->as(); - } + template const T& definition() const; - void definition(const DefinitionBase::pointer &definition) { + void definition(const std::shared_ptr &definition) { definition_ = definition; } @@ -224,11 +172,79 @@ struct Resource { Changed changed(const Resource &o) const; + Id::list needsResources() const; + private: /** Definition: based on type and driver, created by resource * parser/generator and interpreted by driver. */ - DefinitionBase::pointer definition_; + std::shared_ptr definition_; +}; + +/** Base of all resource definitions. + */ +class DefinitionBase { +public: + typedef std::shared_ptr pointer; + virtual ~DefinitionBase() {} + + void from(const Json::Value &value) { from_impl(value); } + void to(Json::Value &value) const { to_impl(value); } + Changed changed(const DefinitionBase &other) const { + return changed_impl(other); + } + + /** Are credits frozen in the resources's dataset? + */ + bool frozenCredits() const { return frozenCredits_impl(); } + + /** Returns true if resource needs lod and tile ranges. + */ + bool needsRanges() const { return needsRanges_impl(); } + + /** Returns list of resources this resource depends on. + */ + Resource::Id::list needsResources() const { return needsResources_impl(); } + + template const T& as() const { + if (const auto *value = dynamic_cast(this)) { + return *value; + } + LOGTHROW(err1, Error) + << "Incompatible resource definitions: cannot convert <" + << typeid(*this).name() << "> into <" << typeid(T).name() << ">."; + throw; + } + +private: + /** Fills in this definition from given input value. + */ + virtual void from_impl(const Json::Value &value) = 0; + + /** Fills in this definition into given output value. + */ + virtual void to_impl(Json::Value &value) const = 0; + + /** Compares this resource definition the other one and check whether they + * are basically the same. The definitions can differ but the difference + * must not affect resource generation. + */ + virtual Changed changed_impl(const DefinitionBase &other) const = 0; + + /** If generated dataset freezes credit info into its published data then + * credits cannot be changed. + */ + virtual bool frozenCredits_impl() const { return true; } + + /** Returns true if resource needs lod and tile ranges. + */ + virtual bool needsRanges_impl() const { return true; } + + /** Returns list of resources this resource depends on. + */ + virtual Resource::Id::list needsResources_impl() const { + return {}; + } }; struct GeneratorInterface { @@ -426,6 +442,10 @@ inline bool Resource::Id::operator==(const Id &o) const { && (id == o.id)); } +inline bool Resource::Id::operator!=(const Id &o) const { + return !operator==(o); +} + inline bool Resource::Generator::operator<(const Generator &o) const { return std::tie(type, driver) < std::tie(o.type, o.driver); } @@ -435,6 +455,10 @@ inline bool Resource::Generator::operator==(const Generator &o) const { && (driver == o.driver)); } +inline bool Resource::Generator::operator!=(const Generator &o) const { + return !operator==(o); +} + inline Resource::Id addReferenceFrame(Resource::Id rid , std::string referenceFrame) { @@ -510,4 +534,8 @@ operator&(GeneratorInterface::Interface l, GeneratorInterface::Interface r) & static_cast(r)); } +template const T& Resource::definition() const { + return definition_->as(); +} + #endif // mapproxy_resource_hpp_included_ diff --git a/mapproxy/src/mapproxy/support/atlas.cpp b/mapproxy/src/mapproxy/support/atlas.cpp new file mode 100644 index 0000000..0cf7767 --- /dev/null +++ b/mapproxy/src/mapproxy/support/atlas.cpp @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2019 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "vts-libs/vts/opencv/atlas.hpp" + +#include "atlas.hpp" + +namespace vts = vtslibs::vts; + +void sendImage(const cv::Mat &image, const Sink::FileInfo &sfi + , RasterFormat format, bool atlas, Sink &sink) +{ + if (atlas) { + // serialize as a single-image atlas + + // TODO: make quality configurable + vts::opencv::Atlas a(75); + a.add(image); + + std::ostringstream os; + a.serialize(os); + sink.content(os.str(), sfi); + return; + } + + // serialize as a raw image + std::vector buf; + switch (format) { + case RasterFormat::jpg: + // TODO: make quality configurable + cv::imencode(".jpg", image, buf + , { cv::IMWRITE_JPEG_QUALITY, 75 }); + break; + + case RasterFormat::png: + cv::imencode(".png", image, buf + , { cv::IMWRITE_PNG_COMPRESSION, 9 }); + break; + } + + sink.content(buf, sfi); +} diff --git a/mapproxy/src/mapproxy/support/atlas.hpp b/mapproxy/src/mapproxy/support/atlas.hpp new file mode 100644 index 0000000..aa04260 --- /dev/null +++ b/mapproxy/src/mapproxy/support/atlas.hpp @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2019 Melown Technologies SE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef mapproxy_support_atlas_hpp_included_ +#define mapproxy_support_atlas_hpp_included_ + +#include + +#include "../resource.hpp" +#include "../sink.hpp" + +/** Sends image from cv::Mat into sink in given format. If atlas is set it + * generates single-image VTS atlas (always JPEG). + */ +void sendImage(const cv::Mat &image, const Sink::FileInfo &sfi + , RasterFormat format, bool atlas, Sink &sink); + +#endif // mapproxy_support_atlas_hpp_included_ diff --git a/mapproxy/src/mapproxy/support/mesh.cpp b/mapproxy/src/mapproxy/support/mesh.cpp index e7f0faa..aeba0f7 100644 --- a/mapproxy/src/mapproxy/support/mesh.cpp +++ b/mapproxy/src/mapproxy/support/mesh.cpp @@ -382,26 +382,32 @@ class TextureNormalizer { vts::SubMesh& addSubMesh(vts::Mesh &mesh, const geometry::Mesh &gmesh , const vts::NodeInfo &nodeInfo - , const boost::optional &geoidGrid) + , const boost::optional &geoidGrid + , vts::SubMesh::TextureMode textureMode) { const auto extents(nodeInfo.extents()); const auto l2g(geo::local2geo(extents)); mesh.submeshes.emplace_back(); auto &sm(mesh.submeshes.back()); - sm.textureMode = vts::SubMesh::external; + sm.textureMode = textureMode; + const bool generateTc(textureMode == vts::SubMesh::TextureMode::internal); TextureNormalizer tn(extents); const auto conv(sds2phys(nodeInfo, geoidGrid)); - bool generateEtc(nodeInfo.node().externalTexture); + const bool generateEtc(nodeInfo.node().externalTexture); + + const bool textured(generateEtc || generateTc); for (const auto &v : gmesh.vertices) { // convert v from local coordinates to division SRS then to physical SRS sm.vertices.push_back(conv(transform(l2g, v))); - // generate external texture coordinates if instructed - if (generateEtc) { - sm.etc.push_back(tn(v)); + // generate external/internal texture coordinates if instructed + if (textured) { + const auto tc(tn(v)); + if (generateEtc) { sm.etc.push_back(tc); } + if (generateTc) { sm.tc.push_back(tc); } } } @@ -409,6 +415,9 @@ vts::SubMesh& addSubMesh(vts::Mesh &mesh, const geometry::Mesh &gmesh sm.faces.emplace_back(f.a, f.b, f.c); } + // texture faces same as regular faces + if (generateTc) { sm.facesTc = sm.faces; } + return sm; } diff --git a/mapproxy/src/mapproxy/support/mesh.hpp b/mapproxy/src/mapproxy/support/mesh.hpp index 066940a..5f3ee4e 100644 --- a/mapproxy/src/mapproxy/support/mesh.hpp +++ b/mapproxy/src/mapproxy/support/mesh.hpp @@ -89,7 +89,9 @@ void addSkirt(geometry::Mesh &mesh, const vts::NodeInfo &nodeInfo); vts::SubMesh& addSubMesh(vts::Mesh &mesh, const geometry::Mesh &gmesh , const vts::NodeInfo &nodeInfo - , const boost::optional &geoidGrid); + , const boost::optional &geoidGrid + , vts::SubMesh::TextureMode textureMode + = vts::SubMesh::external); qmf::Mesh qmfMesh(const geometry::Mesh &gmesh, const vts::NodeInfo &nodeInfo , const std::string &srs