/* -*-c++-*- */ /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph * Copyright 2020 Pelican Mapping * http://osgearth.org * * osgEarth is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/> */ #ifndef OSGEARTH_MAPBOXGL_IMAGE_LAYER #define OSGEARTH_MAPBOXGL_IMAGE_LAYER 1 #include <osgEarth/ImageLayer> #include <osgEarth/FeatureSource> #include <osgEarth/JsonUtils> #include <osgEarth/MapboxGLGlyphManager> namespace osgEarth { namespace MapBoxGL { template< class T > class Stop { public: Stop(float z, T v) : zoom(z), value(std::move(v)) { } T value; float zoom; }; template<typename T> inline T lerp(float t, const T& a, const T& b) { return (T)(a + (b - a) * t); } template<> inline GeoPoint lerp(float t, const GeoPoint& a, const GeoPoint& b) { return a.interpolate(b, t); } template< class T > class PropertyExpression { using StopType = Stop< T >; public: PropertyExpression() = default; const std::vector< StopType >& stops() const { return _stops; } std::vector< StopType >& stops() { return _stops; } T evalute(float zoom) const { if (_stops.empty()) { return T(); } else if (zoom <= _stops[0].zoom) { return _stops[0].value; } else if (zoom >= _stops.back().zoom) { return _stops.back().value; } else { for (unsigned int i = 0; i < _stops.size(); ++i) { const StopType& a = _stops[i]; const StopType& b = _stops[i + 1]; if (a.zoom <= zoom && b.zoom >= zoom) { // Normalize the value between 0 and 1 for interpolation. float t = (zoom - a.zoom) / (b.zoom - a.zoom); auto val = lerp(t, a.value, b.value); return val; } } } return T(); } private: float _base = 1.0f; std::vector< StopType > _stops; }; template< class T > class PropertyValue { public: PropertyValue() = default; PropertyValue(T constant) : _constant(std::move(constant)), _isConstant(true) { } PropertyValue(PropertyExpression<T> expression) : _expression(std::move(expression)), _isConstant(false) { } void setConstant(T constant) { _constant = std::move(constant); _isConstant = true; } void setExpression(PropertyExpression<T> expression) { _expression = std::move(expression); _isConstant = false; } bool isConstant() const { return _isConstant; } bool isExpression() const { return !_isConstant; } T evaluate(float zoom) const { if (_isConstant) { return _constant; } return _expression.evalute(zoom); } private: T _constant; PropertyExpression<T> _expression; bool _isConstant = true; }; // https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#paint-property class Paint { public: Paint(); const PropertyValue<Color>& backgroundColor() const; PropertyValue<Color>& backgroundColor(); const PropertyValue<float>& backgroundOpacity() const; PropertyValue<float>& backgroundOpacity(); const PropertyValue<Color>& fillColor() const; PropertyValue<Color>& fillColor(); const optional<std::string>& fillPattern() const; optional<std::string>& fillPattern(); const PropertyValue<Color>& lineColor() const; PropertyValue<Color>& lineColor(); const PropertyValue<float>& lineWidth() const; PropertyValue<float>& lineWidth(); const optional<std::string>& textField() const; optional<std::string>& textField(); const optional<std::string>& textFont() const; optional<std::string>& textFont(); const PropertyValue<Color>& textColor() const; PropertyValue<Color>& textColor(); const PropertyValue<Color>& textHaloColor() const; PropertyValue<Color>& textHaloColor(); const PropertyValue<float>& textSize() const; PropertyValue<float>& textSize(); const optional<std::string>& textAnchor() const; optional<std::string>& textAnchor(); const optional<std::string>& iconImage() const; optional<std::string>& iconImage(); const optional<std::string>& visibility() const; optional<std::string>& visibility(); private: //Background PropertyValue<Color> _backgroundColor = PropertyValue<Color>(Color::Transparent); PropertyValue<float> _backgroundOpacity = PropertyValue<float>(1.0f); // Fill optional<bool> _fillAntialias; PropertyValue<Color> _fillColor = PropertyValue<Color>(Color("#000000")); PropertyValue<float> _fillOpacity = PropertyValue<float>(1.0f); optional<std::string> _fillPattern; // Line PropertyValue<Color> _lineColor = PropertyValue<Color>(Color::Green); PropertyValue<float> _lineWidth = PropertyValue<float>(1.0f); // Text optional<std::string> _textField; optional<std::string> _textFont; PropertyValue<Color> _textColor = PropertyValue<Color>(Color::Black); PropertyValue<Color> _textHaloColor = PropertyValue<Color>(Color::Transparent); PropertyValue<float> _textSize = PropertyValue<float>(16.0f); optional<std::string> _textAnchor; // Icon optional<std::string> _iconImage; optional<std::string> _visibility; }; class OSGEARTH_EXPORT StyleSheet { public: class Source { public: Source(); const std::string& attribution() const; std::string& attribution(); const std::string& url() const; std::string& url(); const std::string& type() const; std::string& type(); const std::string& name() const; std::string& name(); const std::vector< std::string >& tiles() const; std::vector< std::string >& tiles(); FeatureSource* featureSource(); const FeatureSource* featureSource() const; void loadFeatureSource(const std::string& styleSheetURI, const osgDB::Options* options); private: std::string _attribution; std::string _url; std::string _type; std::string _name; std::vector< std::string > _tiles; osg::ref_ptr< FeatureSource > _featureSource; }; class FilterExpression { public: Json::Value _filter; }; class Layer { public: Layer(); const std::string& id() const; std::string& id(); const std::string& source() const; std::string& source(); const std::string& sourceLayer() const; std::string& sourceLayer(); const std::string& type() const; std::string& type(); const unsigned int& minZoom() const; unsigned int& minZoom(); const unsigned int& maxZoom() const; unsigned int& maxZoom(); Paint& paint(); const Paint& paint() const; FilterExpression& filter(); const FilterExpression& filter() const; private: std::string _id; std::string _source; std::string _sourceLayer; std::string _type; unsigned int _minZoom = 0; unsigned int _maxZoom = 24; Paint _paint; FilterExpression _filter; }; const std::string& version() const; const std::string& name() const; const URI& glyphs() const; const URI& sprite() const; std::vector< Layer >& layers(); const std::vector< Layer >& layers() const; std::vector< Source >& sources(); const std::vector< Source >& sources() const; const ResourceLibrary* spriteLibrary() const; static StyleSheet load(const URI& location, const osgDB::Options* options); StyleSheet(); private: std::string _version; std::string _name; URI _sprite; URI _glyphs; std::vector< Source > _sources; std::vector< Layer > _layers; osg::ref_ptr< ResourceLibrary > _spriteLibrary; static ResourceLibrary* loadSpriteLibrary(const URI& sprite); }; } class OSGEARTH_EXPORT MapBoxGLImageLayer : public osgEarth::ImageLayer { public: // serialization class OSGEARTH_EXPORT Options : public ImageLayer::Options { public: META_LayerOptions(osgEarth, Options, ImageLayer::Options); OE_OPTION(URI, url); OE_OPTION(std::string, key); OE_OPTION(bool, disableText); OE_OPTION(float, pixelScale); virtual Config getConfig() const; private: void fromConfig( const Config& conf ); }; public: META_Layer(osgEarth, MapBoxGLImageLayer, Options, osgEarth::ImageLayer, MapboxGLImage); //! URL of the style url void setURL(const URI& value); const URI& getURL() const; //! API key void setKey(const std::string& value); const std::string& getKey() const; void setDisableText(const bool& value); const bool& getDisableText() const; void setPixelScale(const float& value); const float& getPixelScale() const; // Opens the layer and returns a status virtual Status openImplementation(); virtual GeoImage createImageImplementation(const TileKey& key, ProgressCallback* progress) const; protected: // Layer // Called by Map when it adds this layer virtual void addedToMap(const class Map*); // Called by Map when it removes this layer virtual void removedFromMap(const class Map*); // post-ctor initialization virtual void init(); protected: virtual ~MapBoxGLImageLayer() { } osg::observer_ptr< const osgEarth::Map > _map; MapBoxGL::StyleSheet _styleSheet; osg::ref_ptr< MapboxGLGlyphManager > _glyphManager; }; } // namespace osgEarth OSGEARTH_SPECIALIZE_CONFIG(osgEarth::MapBoxGLImageLayer::Options); #endif // OSGEARTH_MAPBOXGL_IMAGE_LAYER