[core] Add ImageSource support to style parsers
This commit is contained in:
@@ -355,6 +355,7 @@ set(MBGL_CORE_FILES
|
||||
|
||||
# style/conversion
|
||||
include/mbgl/style/conversion/constant.hpp
|
||||
include/mbgl/style/conversion/coordinate.hpp
|
||||
include/mbgl/style/conversion/data_driven_property_value.hpp
|
||||
include/mbgl/style/conversion/filter.hpp
|
||||
include/mbgl/style/conversion/function.hpp
|
||||
@@ -437,11 +438,15 @@ set(MBGL_CORE_FILES
|
||||
|
||||
# style/sources
|
||||
include/mbgl/style/sources/geojson_source.hpp
|
||||
include/mbgl/style/sources/image_source.hpp
|
||||
include/mbgl/style/sources/raster_source.hpp
|
||||
include/mbgl/style/sources/vector_source.hpp
|
||||
src/mbgl/style/sources/geojson_source.cpp
|
||||
src/mbgl/style/sources/geojson_source_impl.cpp
|
||||
src/mbgl/style/sources/geojson_source_impl.hpp
|
||||
src/mbgl/style/sources/image_source.cpp
|
||||
src/mbgl/style/sources/image_source_impl.cpp
|
||||
src/mbgl/style/sources/image_source_impl.hpp
|
||||
src/mbgl/style/sources/raster_source.cpp
|
||||
src/mbgl/style/sources/raster_source_impl.cpp
|
||||
src/mbgl/style/sources/raster_source_impl.hpp
|
||||
|
||||
37
include/mbgl/style/conversion/coordinate.hpp
Normal file
37
include/mbgl/style/conversion/coordinate.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
template<>
|
||||
struct Converter<std::unique_ptr<LatLng>> {
|
||||
public:
|
||||
template <class V>
|
||||
optional<std::unique_ptr<LatLng>> operator() (const V& value, Error& error) const {
|
||||
if (!isArray(value) || arrayLength(value) < 2 ) {
|
||||
error = { "coordinate array must contain numeric longtitude and latitude values" };
|
||||
return {};
|
||||
}
|
||||
//Style spec uses GeoJSON convention for specifying coordinates
|
||||
optional<float> latitude = toNumber(arrayMember(value, 1));
|
||||
optional<float> longitude = toNumber(arrayMember(value, 0));
|
||||
|
||||
if (!latitude || !longitude) {
|
||||
error = { "coordinate array must contain numeric longtitude and latitude values" };
|
||||
return {};
|
||||
}
|
||||
if (*latitude < -90 || *latitude > 90 ){
|
||||
error = { "coordinate latitude must be between -90 and 90" };
|
||||
return {};
|
||||
}
|
||||
return { std::make_unique<LatLng>(*latitude, *longitude) };
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/style/conversion/coordinate.hpp>
|
||||
#include <mbgl/style/conversion/geojson.hpp>
|
||||
#include <mbgl/style/conversion/geojson_options.hpp>
|
||||
#include <mbgl/style/conversion/tileset.hpp>
|
||||
@@ -8,6 +9,8 @@
|
||||
#include <mbgl/style/sources/geojson_source.hpp>
|
||||
#include <mbgl/style/sources/raster_source.hpp>
|
||||
#include <mbgl/style/sources/vector_source.hpp>
|
||||
#include <mbgl/style/sources/image_source.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
@@ -16,6 +19,7 @@ namespace conversion {
|
||||
template <>
|
||||
struct Converter<std::unique_ptr<Source>> {
|
||||
public:
|
||||
|
||||
template <class V>
|
||||
optional<std::unique_ptr<Source>> operator()(const V& value, Error& error, const std::string& id) const {
|
||||
if (!isObject(value)) {
|
||||
@@ -41,6 +45,8 @@ public:
|
||||
return convertVectorSource(id, value, error);
|
||||
} else if (*type == "geojson") {
|
||||
return convertGeoJSONSource(id, value, error);
|
||||
} else if (*type == "image") {
|
||||
return convertImageSource(id, value, error);
|
||||
} else {
|
||||
error = { "invalid source type" };
|
||||
return {};
|
||||
@@ -136,6 +142,48 @@ private:
|
||||
|
||||
return { std::move(result) };
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<std::unique_ptr<Source>> convertImageSource(const std::string& id,
|
||||
const V& value,
|
||||
Error& error) const {
|
||||
auto urlValue = objectMember(value, "url");
|
||||
if (!urlValue) {
|
||||
error = { "Image source must have a url value" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto urlString = toString(*urlValue);
|
||||
if (!urlString) {
|
||||
error = { "Image url must be a URL string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto coordinatesValue = objectMember(value, "coordinates");
|
||||
if (!coordinatesValue) {
|
||||
error = { "Image source must have a coordinates values" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) {
|
||||
error = { "Image coordinates must be an array of four longitude latitude pairs" };
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<LatLng> coordinates;
|
||||
coordinates.reserve(4);
|
||||
for( std::size_t i=0; i < arrayLength(*coordinatesValue); i++) {
|
||||
auto latLng = conversion::convert<LatLng>(arrayMember(*coordinatesValue,i), error);
|
||||
if (!latLng) {
|
||||
return {};
|
||||
}
|
||||
coordinates.push_back(*latLng);
|
||||
}
|
||||
auto result = std::make_unique<ImageSource>(id, coordinates);
|
||||
result->setURL(*urlString);
|
||||
|
||||
return { std::move(result) };
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
41
include/mbgl/style/sources/image_source.hpp
Normal file
41
include/mbgl/style/sources/image_source.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/source.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/optional.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
class LatLng;
|
||||
class AsyncRequest;
|
||||
|
||||
namespace style {
|
||||
|
||||
class ImageSource : public Source {
|
||||
public:
|
||||
ImageSource(std::string id, const std::vector<LatLng>);
|
||||
~ImageSource() override;
|
||||
|
||||
const std::string& getURL() const;
|
||||
void setURL(const std::string& url) ;
|
||||
|
||||
void setImage(mbgl::UnassociatedImage);
|
||||
|
||||
void setCoordinates(const std::vector<LatLng>&);
|
||||
std::vector<LatLng> getCoordinates() const;
|
||||
|
||||
class Impl;
|
||||
const Impl& impl() const;
|
||||
|
||||
void loadDescription(FileSource&) final;
|
||||
private:
|
||||
optional<std::string> url;
|
||||
std::unique_ptr<AsyncRequest> req;
|
||||
};
|
||||
|
||||
template <>
|
||||
inline bool Source::is<ImageSource>() const {
|
||||
return getType() == SourceType::Image;
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -10,7 +10,8 @@ enum class SourceType : uint8_t {
|
||||
Raster,
|
||||
GeoJSON,
|
||||
Video,
|
||||
Annotations
|
||||
Annotations,
|
||||
Image
|
||||
};
|
||||
|
||||
namespace style {
|
||||
|
||||
@@ -120,6 +120,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
|
||||
|
||||
case SourceType::Video:
|
||||
case SourceType::Annotations:
|
||||
case SourceType::Image:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -197,6 +198,7 @@ void OfflineDownload::activateDownload() {
|
||||
|
||||
case SourceType::Video:
|
||||
case SourceType::Annotations:
|
||||
case SourceType::Image:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <mbgl/style/layer_impl.hpp>
|
||||
#include <mbgl/style/rapidjson_conversion.hpp>
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/style/conversion/coordinate.hpp>
|
||||
#include <mbgl/style/conversion/source.hpp>
|
||||
#include <mbgl/style/conversion/layer.hpp>
|
||||
#include <mbgl/style/conversion/light.hpp>
|
||||
@@ -55,10 +56,12 @@ StyleParseResult Parser::parse(const std::string& json) {
|
||||
|
||||
if (document.HasMember("center")) {
|
||||
const JSValue& value = document["center"];
|
||||
if (value.IsArray() && value.Size() >= 2) {
|
||||
// Style spec uses lon/lat order
|
||||
latLng = LatLng(value[1].IsNumber() ? value[1].GetDouble() : 0,
|
||||
value[0].IsNumber() ? value[0].GetDouble() : 0);
|
||||
conversion::Error error;
|
||||
auto convertedLatLng = conversion::convert<LatLng>(value, error);
|
||||
if (convertedLatLng) {
|
||||
latLng = *convertedLatLng;
|
||||
} else {
|
||||
Log::Warning(Event::ParseStyle, "center coordinate must be a longitude, latitude pair");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
85
src/mbgl/style/sources/image_source.cpp
Normal file
85
src/mbgl/style/sources/image_source.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <mbgl/style/sources/image_source.hpp>
|
||||
#include <mbgl/style/sources/image_source_impl.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/style/source_observer.hpp>
|
||||
#include <mbgl/util/premultiply.hpp>
|
||||
#include <mbgl/storage/file_source.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
ImageSource::ImageSource(std::string id, const std::vector<LatLng> coords_)
|
||||
: Source(makeMutable<Impl>(std::move(id), coords_)) {
|
||||
}
|
||||
|
||||
ImageSource::~ImageSource() = default;
|
||||
|
||||
const ImageSource::Impl& ImageSource::impl() const {
|
||||
return static_cast<const Impl&>(*baseImpl);
|
||||
}
|
||||
|
||||
void ImageSource::setCoordinates(const std::vector<LatLng>& coords_) {
|
||||
baseImpl = makeMutable<Impl>(impl(), coords_);
|
||||
observer->onSourceChanged(*this);
|
||||
}
|
||||
|
||||
std::vector<LatLng> ImageSource::getCoordinates() const {
|
||||
return impl().getCoordinates();
|
||||
}
|
||||
|
||||
void ImageSource::setURL(const std::string& url_) {
|
||||
url = std::move(url_);
|
||||
// Signal that the source description needs a reload
|
||||
if (loaded || req) {
|
||||
loaded = false;
|
||||
req.reset();
|
||||
observer->onSourceDescriptionChanged(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageSource::setImage(mbgl::UnassociatedImage image_) {
|
||||
url = {};
|
||||
if (req) {
|
||||
req.reset();
|
||||
}
|
||||
loaded = true;
|
||||
baseImpl = makeMutable<Impl>(impl(), std::move(image_));
|
||||
observer->onSourceChanged(*this);
|
||||
}
|
||||
|
||||
const std::string& ImageSource::getURL() const {
|
||||
return *url;
|
||||
}
|
||||
|
||||
void ImageSource::loadDescription(FileSource& fileSource) {
|
||||
if (!url) {
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
if (req || loaded) {
|
||||
return;
|
||||
}
|
||||
const Resource imageResource { Resource::Unknown, *url, {}, Resource::Necessity::Required };
|
||||
|
||||
req = fileSource.request(imageResource, [this](Response res) {
|
||||
if (res.error) {
|
||||
observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message)));
|
||||
} else if (res.notModified) {
|
||||
return;
|
||||
} else if (res.noContent) {
|
||||
observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty image url")));
|
||||
} else {
|
||||
try {
|
||||
UnassociatedImage image = util::unpremultiply(decodeImage(*res.data));
|
||||
baseImpl = makeMutable<Impl>(impl(), std::move(image));
|
||||
} catch (...) {
|
||||
observer->onSourceError(*this, std::current_exception());
|
||||
}
|
||||
loaded = true;
|
||||
observer->onSourceLoaded(*this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
42
src/mbgl/style/sources/image_source_impl.cpp
Normal file
42
src/mbgl/style/sources/image_source_impl.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <mbgl/style/sources/image_source_impl.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
ImageSource::Impl::Impl(std::string id_, std::vector<LatLng> coords_)
|
||||
: Source::Impl(SourceType::Image, std::move(id_)),
|
||||
coords(std::move(coords_)) {
|
||||
}
|
||||
|
||||
ImageSource::Impl::Impl(const Impl& other, std::vector<LatLng> coords_)
|
||||
: Source::Impl(other),
|
||||
coords(std::move(coords_)),
|
||||
image(other.image.clone()) {
|
||||
}
|
||||
|
||||
ImageSource::Impl::Impl(const Impl& rhs, UnassociatedImage image_)
|
||||
: Source::Impl(rhs),
|
||||
coords(rhs.coords),
|
||||
image(std::move(image_)) {
|
||||
}
|
||||
ImageSource::Impl::~Impl() = default;
|
||||
|
||||
void ImageSource::Impl::setImage(UnassociatedImage image_) {
|
||||
image = std::move(image_);
|
||||
}
|
||||
|
||||
const UnassociatedImage& ImageSource::Impl::getImage() const {
|
||||
return image;
|
||||
}
|
||||
|
||||
std::vector<LatLng> ImageSource::Impl::getCoordinates() const {
|
||||
return coords;
|
||||
}
|
||||
|
||||
optional<std::string> ImageSource::Impl::getAttribution() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
31
src/mbgl/style/sources/image_source_impl.hpp
Normal file
31
src/mbgl/style/sources/image_source_impl.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/source_impl.hpp>
|
||||
#include <mbgl/style/sources/image_source.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
class LatLng;
|
||||
|
||||
namespace style {
|
||||
|
||||
class ImageSource::Impl : public Source::Impl {
|
||||
public:
|
||||
Impl(std::string id, std::vector<LatLng> coords);
|
||||
Impl(const Impl& rhs, std::vector<LatLng> coords);
|
||||
Impl(const Impl& rhs, UnassociatedImage image);
|
||||
|
||||
~Impl() final;
|
||||
|
||||
void setImage(UnassociatedImage );
|
||||
const mbgl::UnassociatedImage& getImage() const;
|
||||
std::vector<LatLng> getCoordinates() const;
|
||||
|
||||
optional<std::string> getAttribution() const final;
|
||||
private:
|
||||
std::vector<LatLng> coords;
|
||||
mbgl::UnassociatedImage image;
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -11,6 +11,7 @@ MBGL_DEFINE_ENUM(SourceType, {
|
||||
{ SourceType::GeoJSON, "geojson" },
|
||||
{ SourceType::Video, "video" },
|
||||
{ SourceType::Annotations, "annotations" },
|
||||
{ SourceType::Image, "image" },
|
||||
});
|
||||
|
||||
MBGL_DEFINE_ENUM(VisibilityType, {
|
||||
|
||||
7
test/fixtures/style_parser/center-not-latlong.info.json
vendored
Normal file
7
test/fixtures/style_parser/center-not-latlong.info.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"default": {
|
||||
"log": [
|
||||
[1, "WARNING", "ParseStyle", "center coordinate must be a longitude, latitude pair"]
|
||||
]
|
||||
}
|
||||
}
|
||||
4
test/fixtures/style_parser/center-not-latlong.style.json
vendored
Normal file
4
test/fixtures/style_parser/center-not-latlong.style.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": 8,
|
||||
"center" : [ 75.123, 181.123]
|
||||
}
|
||||
7
test/fixtures/style_parser/image-coordinates.info.json
vendored
Normal file
7
test/fixtures/style_parser/image-coordinates.info.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"default": {
|
||||
"log": [
|
||||
[1, "WARNING", "ParseStyle", "Image coordinates must be an array of four longitude latitude pairs"]
|
||||
]
|
||||
}
|
||||
}
|
||||
14
test/fixtures/style_parser/image-coordinates.style.json
vendored
Normal file
14
test/fixtures/style_parser/image-coordinates.style.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": 8,
|
||||
"center" : [ 75.123, 81.123],
|
||||
"sources": {
|
||||
"image": {
|
||||
"type": "image",
|
||||
"url": "local://0.png",
|
||||
"coordinates": [
|
||||
[ 12.2344, 87.23434],
|
||||
[-12.234, 12.41242]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
7
test/fixtures/style_parser/image-url.info.json
vendored
Normal file
7
test/fixtures/style_parser/image-url.info.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"default": {
|
||||
"log": [
|
||||
[1, "WARNING", "ParseStyle", "Image source must have a url value"]
|
||||
]
|
||||
}
|
||||
}
|
||||
9
test/fixtures/style_parser/image-url.style.json
vendored
Normal file
9
test/fixtures/style_parser/image-url.style.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"version": 8,
|
||||
"center" : [ 75.123, 81.123],
|
||||
"sources": {
|
||||
"image": {
|
||||
"type": "image"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <mbgl/style/sources/raster_source.hpp>
|
||||
#include <mbgl/style/sources/vector_source.hpp>
|
||||
#include <mbgl/style/sources/geojson_source.hpp>
|
||||
#include <mbgl/style/sources/image_source.hpp>
|
||||
|
||||
#include <mbgl/renderer/sources/render_raster_source.hpp>
|
||||
#include <mbgl/renderer/sources/render_vector_source.hpp>
|
||||
@@ -16,6 +17,9 @@
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
#include <mbgl/util/premultiply.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
|
||||
#include <mbgl/util/tileset.hpp>
|
||||
#include <mbgl/util/default_thread_pool.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
@@ -436,3 +440,39 @@ TEST(Source, GeoJSonSourceUrlUpdate) {
|
||||
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST(Source, ImageSourceImageUpdate) {
|
||||
SourceTest test;
|
||||
|
||||
test.fileSource.response = [&] (const Resource& resource) {
|
||||
EXPECT_EQ("http://url", resource.url);
|
||||
Response response;
|
||||
response.data = std::make_unique<std::string>(util::read_file("test/fixtures/image/no_profile.png"));
|
||||
return response;
|
||||
};
|
||||
test.styleObserver.sourceChanged = [&] (Source&) {
|
||||
// Should be called (test will hang if it doesn't)
|
||||
test.end();
|
||||
};
|
||||
std::vector<LatLng> coords;
|
||||
|
||||
ImageSource source("source", coords);
|
||||
source.setURL("http://url");
|
||||
source.setObserver(&test.styleObserver);
|
||||
|
||||
// Load initial, so the source state will be loaded=true
|
||||
source.loadDescription(test.fileSource);
|
||||
UnassociatedImage rgba({ 1, 1 });
|
||||
rgba.data[0] = 255;
|
||||
rgba.data[1] = 254;
|
||||
rgba.data[2] = 253;
|
||||
rgba.data[3] = 128;
|
||||
|
||||
// Schedule an update
|
||||
test.loop.invoke([&] () {
|
||||
// Update the url
|
||||
source.setImage(std::move(rgba));
|
||||
});
|
||||
|
||||
test.run();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user