[core] Add ImageSource support to style parsers

This commit is contained in:
Asheem Mamoowala
2017-04-20 12:39:51 -07:00
parent 9068c90fc4
commit 7a010df956
18 changed files with 389 additions and 5 deletions

View File

@@ -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

View 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

View File

@@ -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

View 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

View File

@@ -10,7 +10,8 @@ enum class SourceType : uint8_t {
Raster,
GeoJSON,
Video,
Annotations
Annotations,
Image
};
namespace style {

View File

@@ -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;
}
}

View File

@@ -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");
}
}

View 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

View 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

View 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

View File

@@ -11,6 +11,7 @@ MBGL_DEFINE_ENUM(SourceType, {
{ SourceType::GeoJSON, "geojson" },
{ SourceType::Video, "video" },
{ SourceType::Annotations, "annotations" },
{ SourceType::Image, "image" },
});
MBGL_DEFINE_ENUM(VisibilityType, {

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "center coordinate must be a longitude, latitude pair"]
]
}
}

View File

@@ -0,0 +1,4 @@
{
"version": 8,
"center" : [ 75.123, 181.123]
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "Image coordinates must be an array of four longitude latitude pairs"]
]
}
}

View 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]
]
}
}
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "Image source must have a url value"]
]
}
}

View File

@@ -0,0 +1,9 @@
{
"version": 8,
"center" : [ 75.123, 81.123],
"sources": {
"image": {
"type": "image"
}
}
}

View File

@@ -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();
}