Load TileJSON; support inline TileJSON sources
Fixes #412 Refs https://github.com/mapbox/mapbox-gl-style-spec/pull/143
This commit is contained in:
committed by
John Firebaugh
parent
b9c7e1bdd9
commit
0168612ed3
@@ -25,7 +25,9 @@ struct box;
|
||||
|
||||
class Source : public std::enable_shared_from_this<Source>, private util::noncopyable {
|
||||
public:
|
||||
Source(SourceInfo info, const std::string &access_token = "");
|
||||
Source(SourceInfo& info);
|
||||
|
||||
void load(Map &map);
|
||||
|
||||
bool update(Map &map);
|
||||
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
|
||||
@@ -38,11 +40,6 @@ public:
|
||||
std::forward_list<Tile::ID> getIDs() const;
|
||||
void updateClipIDs(const std::map<Tile::ID, ClipID> &mapping);
|
||||
|
||||
static std::string normalizeSourceURL(const std::string &url, const std::string &access_token);
|
||||
|
||||
public:
|
||||
const SourceInfo info;
|
||||
|
||||
private:
|
||||
bool findLoadedChildren(const Tile::ID& id, int32_t maxCoveringZoom, std::forward_list<Tile::ID>& retain);
|
||||
bool findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::forward_list<Tile::ID>& retain);
|
||||
@@ -55,7 +52,9 @@ private:
|
||||
|
||||
double getZoom(const TransformState &state) const;
|
||||
|
||||
private:
|
||||
SourceInfo& info;
|
||||
bool loaded = false;
|
||||
|
||||
// Stores the time when this source was most recently updated.
|
||||
timestamp updated = 0;
|
||||
|
||||
|
||||
@@ -67,9 +67,6 @@ protected:
|
||||
public:
|
||||
const SourceInfo &source;
|
||||
|
||||
// Request-related information.
|
||||
const std::string url;
|
||||
|
||||
protected:
|
||||
std::weak_ptr<platform::Request> req;
|
||||
std::string data;
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#include <mbgl/style/types.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -11,27 +15,32 @@ class Source;
|
||||
|
||||
class SourceInfo {
|
||||
public:
|
||||
const SourceType type;
|
||||
const std::string url;
|
||||
const uint32_t tile_size;
|
||||
const int32_t min_zoom;
|
||||
const int32_t max_zoom;
|
||||
SourceType type = SourceType::Vector;
|
||||
std::string url;
|
||||
std::vector<std::string> tiles;
|
||||
uint16_t tile_size = 512;
|
||||
uint16_t min_zoom = 0;
|
||||
uint16_t max_zoom = 22;
|
||||
std::string attribution;
|
||||
std::array<float, 3> center = {{0, 0, 0}};
|
||||
std::array<float, 4> bounds = {{-180, -90, 180, 90}};
|
||||
|
||||
SourceInfo(SourceType type = SourceType::Vector, const std::string &url = "",
|
||||
uint32_t tile_size = 512, int32_t min_zoom = 0, int32_t max_zoom = 22)
|
||||
: type(type), url(url), tile_size(tile_size), min_zoom(min_zoom), max_zoom(max_zoom) {}
|
||||
void parseTileJSONProperties(const rapidjson::Value&);
|
||||
};
|
||||
|
||||
|
||||
class StyleSource {
|
||||
class StyleSource : public std::enable_shared_from_this<StyleSource> {
|
||||
public:
|
||||
const SourceInfo info;
|
||||
SourceInfo info;
|
||||
|
||||
bool enabled = false;
|
||||
std::shared_ptr<Source> source;
|
||||
|
||||
StyleSource(const SourceInfo &info) : info(info) {}
|
||||
StyleSource(const SourceInfo &info)
|
||||
: info(info)
|
||||
{}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -469,7 +469,8 @@ void Map::updateSources() {
|
||||
for (const std::shared_ptr<StyleSource> &style_source : activeSources) {
|
||||
if (style_source->enabled) {
|
||||
if (!style_source->source) {
|
||||
style_source->source = std::make_shared<Source>(style_source->info, getAccessToken());
|
||||
style_source->source = std::make_shared<Source>(style_source->info);
|
||||
style_source->source->load(*this);
|
||||
}
|
||||
} else {
|
||||
style_source->source.reset();
|
||||
|
||||
@@ -6,40 +6,73 @@
|
||||
#include <mbgl/util/raster.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/texturepool.hpp>
|
||||
#include <mbgl/util/filesource.hpp>
|
||||
#include <mbgl/util/vec.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
#include <mbgl/map/vector_tile_data.hpp>
|
||||
#include <mbgl/map/raster_tile_data.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
Source::Source(SourceInfo info, const std::string &access_token)
|
||||
: info(
|
||||
info.type,
|
||||
normalizeSourceURL(info.url, access_token),
|
||||
info.tile_size,
|
||||
info.min_zoom,
|
||||
info.max_zoom
|
||||
) {}
|
||||
Source::Source(SourceInfo& info)
|
||||
: info(info)
|
||||
{
|
||||
}
|
||||
|
||||
std::string Source::normalizeSourceURL(const std::string &url, const std::string &access_token) {
|
||||
std::string normalizeURL(const std::string& url, const std::string& access_token) {
|
||||
const std::string t = "mapbox://";
|
||||
if (url.compare(0, t.length(), t) == 0) {
|
||||
if (access_token.empty()) {
|
||||
throw std::runtime_error("You must provide a Mapbox API access token for Mapbox tile sources");
|
||||
}
|
||||
return std::string("https://api.tiles.mapbox.com/v4/") + url.substr(t.length()) + "/{z}/{x}/{y}.vector.pbf?access_token=" + access_token;
|
||||
} else {
|
||||
|
||||
if (url.compare(0, t.length(), t) != 0)
|
||||
return url;
|
||||
|
||||
if (access_token.empty())
|
||||
throw std::runtime_error("You must provide a Mapbox API access token for Mapbox tile sources");
|
||||
|
||||
return std::string("https://api.tiles.mapbox.com/v4/")
|
||||
+ url.substr(t.length())
|
||||
+ ".json?access_token="
|
||||
+ access_token;
|
||||
}
|
||||
|
||||
// Note: This is a separate function that must be called exactly once after creation
|
||||
// The reason this isn't part of the constructor is that calling shared_from_this() in
|
||||
// the constructor fails.
|
||||
void Source::load(Map& map) {
|
||||
if (info.url.empty()) {
|
||||
loaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string url = normalizeURL(info.url, map.getAccessToken());
|
||||
std::shared_ptr<Source> source = shared_from_this();
|
||||
|
||||
map.getFileSource()->load(ResourceType::JSON, url, [source, &map](platform::Response *res) {
|
||||
if (res->code != 200) {
|
||||
Log::Warning(Event::General, "failed to load source TileJSON");
|
||||
return;
|
||||
}
|
||||
|
||||
rapidjson::Document d;
|
||||
d.Parse<0>(res->body.c_str());
|
||||
|
||||
if (d.HasParseError()) {
|
||||
Log::Warning(Event::General, "invalid source TileJSON");
|
||||
return;
|
||||
}
|
||||
|
||||
source->info.parseTileJSONProperties(d);
|
||||
source->loaded = true;
|
||||
|
||||
map.update();
|
||||
});
|
||||
}
|
||||
|
||||
bool Source::update(Map &map) {
|
||||
if (map.getTime() > updated) {
|
||||
if (loaded && map.getTime() > updated) {
|
||||
return updateTiles(map);
|
||||
} else {
|
||||
return false;
|
||||
|
||||
@@ -14,13 +14,6 @@ TileData::TileData(Tile::ID id, Map &map, const SourceInfo &source)
|
||||
state(State::initial),
|
||||
map(map),
|
||||
source(source),
|
||||
url(util::replaceTokens(source.url, [&](const std::string &token) -> std::string {
|
||||
if (token == "z") return std::to_string(id.z);
|
||||
if (token == "x") return std::to_string(id.x);
|
||||
if (token == "y") return std::to_string(id.y);
|
||||
if (token == "ratio") return (map.getState().getPixelRatio() > 1.0 ? "@2x" : "");
|
||||
return "";
|
||||
})),
|
||||
debugBucket(debugFontBuffer) {
|
||||
// Initialize tile debug coordinates
|
||||
const std::string str = util::sprintf<32>("%d/%d/%d", id.z, id.x, id.y);
|
||||
@@ -36,11 +29,23 @@ const std::string TileData::toString() const {
|
||||
}
|
||||
|
||||
void TileData::request() {
|
||||
if (source.tiles.empty())
|
||||
return;
|
||||
|
||||
std::string url = source.tiles[(id.x + id.y) % source.tiles.size()];
|
||||
url = util::replaceTokens(url, [&](const std::string &token) -> std::string {
|
||||
if (token == "z") return std::to_string(id.z);
|
||||
if (token == "x") return std::to_string(id.x);
|
||||
if (token == "y") return std::to_string(id.y);
|
||||
if (token == "ratio") return (map.getState().getPixelRatio() > 1.0 ? "@2x" : "");
|
||||
return "";
|
||||
});
|
||||
|
||||
state = State::loading;
|
||||
|
||||
// Note: Somehow this feels slower than the change to request_http()
|
||||
std::weak_ptr<TileData> weak_tile = shared_from_this();
|
||||
map.getFileSource()->load(ResourceType::Tile, url, [weak_tile](platform::Response *res) {
|
||||
map.getFileSource()->load(ResourceType::Tile, url, [weak_tile, url](platform::Response *res) {
|
||||
std::shared_ptr<TileData> tile = weak_tile.lock();
|
||||
if (!tile || tile->state == State::obsolete) {
|
||||
// noop. Tile is obsolete and we're now just waiting for the refcount
|
||||
@@ -54,7 +59,7 @@ void TileData::request() {
|
||||
tile->reparse();
|
||||
} else {
|
||||
#if defined(DEBUG)
|
||||
fprintf(stderr, "[%s] tile loading failed: %d, %s\n", tile->url.c_str(), res->code, res->error_message.c_str());
|
||||
fprintf(stderr, "[%s] tile loading failed: %d, %s\n", url.c_str(), res->code, res->error_message.c_str());
|
||||
#endif
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <mbgl/style/style_source.hpp>
|
||||
#include <mbgl/style/style_parser.hpp>
|
||||
#include <mbgl/style/style_layer_group.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
@@ -175,21 +176,14 @@ void StyleParser::parseSources(JSVal value) {
|
||||
rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
|
||||
for (; itr != value.MemberEnd(); ++itr) {
|
||||
std::string name { itr->name.GetString(), itr->name.GetStringLength() };
|
||||
SourceType type = SourceType::Vector;
|
||||
std::string url;
|
||||
uint16_t tile_size = 512;
|
||||
int32_t min_zoom = 0;
|
||||
int32_t max_zoom = 22;
|
||||
SourceInfo info;
|
||||
|
||||
parseRenderProperty<SourceTypeClass>(itr->value, type, "type");
|
||||
parseRenderProperty(itr->value, url, "url");
|
||||
if (type == SourceType::Raster) {
|
||||
parseRenderProperty(itr->value, tile_size, "tileSize");
|
||||
}
|
||||
parseRenderProperty(itr->value, min_zoom, "minZoom");
|
||||
parseRenderProperty(itr->value, max_zoom, "maxZoom");
|
||||
parseRenderProperty<SourceTypeClass>(itr->value, info.type, "type");
|
||||
parseRenderProperty(itr->value, info.url, "url");
|
||||
parseRenderProperty(itr->value, info.tile_size, "tileSize");
|
||||
info.parseTileJSONProperties(itr->value);
|
||||
|
||||
sources.emplace(std::move(name), std::make_shared<StyleSource>(SourceInfo { type, url, tile_size, min_zoom, max_zoom }));
|
||||
sources.emplace(std::move(name), std::make_shared<StyleSource>(info));
|
||||
}
|
||||
} else {
|
||||
Log::Warning(Event::ParseStyle, "sources must be an object");
|
||||
|
||||
77
src/style/style_source.cpp
Normal file
77
src/style/style_source.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <mbgl/style/style_source.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
void parse(const rapidjson::Value& value, std::vector<std::string>& target, const char *name) {
|
||||
if (!value.HasMember(name))
|
||||
return;
|
||||
|
||||
const rapidjson::Value& property = value[name];
|
||||
if (!property.IsArray())
|
||||
return;
|
||||
|
||||
for (rapidjson::SizeType i = 0; i < property.Size(); i++)
|
||||
if (!property[i].IsString())
|
||||
return;
|
||||
|
||||
for (rapidjson::SizeType i = 0; i < property.Size(); i++)
|
||||
target.emplace_back(std::string(property[i].GetString(), property[i].GetStringLength()));
|
||||
}
|
||||
|
||||
void parse(const rapidjson::Value& value, std::string& target, const char* name) {
|
||||
if (!value.HasMember(name))
|
||||
return;
|
||||
|
||||
const rapidjson::Value& property = value[name];
|
||||
if (!property.IsString())
|
||||
return;
|
||||
|
||||
target = { property.GetString(), property.GetStringLength() };
|
||||
}
|
||||
|
||||
void parse(const rapidjson::Value& value, uint16_t& target, const char* name) {
|
||||
if (!value.HasMember(name))
|
||||
return;
|
||||
|
||||
const rapidjson::Value& property = value[name];
|
||||
if (!property.IsUint())
|
||||
return;
|
||||
|
||||
unsigned int uint = property.GetUint();
|
||||
if (uint > std::numeric_limits<uint16_t>::max())
|
||||
return;
|
||||
|
||||
target = uint;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void parse(const rapidjson::Value& value, std::array<float, N>& target, const char* name) {
|
||||
if (!value.HasMember(name))
|
||||
return;
|
||||
|
||||
const rapidjson::Value& property = value[name];
|
||||
if (!property.IsArray() || property.Size() != N)
|
||||
return;
|
||||
|
||||
for (rapidjson::SizeType i = 0; i < property.Size(); i++)
|
||||
if (!property[i].IsNumber())
|
||||
return;
|
||||
|
||||
for (rapidjson::SizeType i = 0; i < property.Size(); i++)
|
||||
target[i] = property[i].GetDouble();
|
||||
}
|
||||
|
||||
void SourceInfo::parseTileJSONProperties(const rapidjson::Value& value) {
|
||||
parse(value, tiles, "tiles");
|
||||
parse(value, min_zoom, "minzoom");
|
||||
parse(value, max_zoom, "maxzoom");
|
||||
parse(value, attribution, "attribution");
|
||||
parse(value, center, "center");
|
||||
parse(value, bounds, "bounds");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user