Merge branch 'before-render' into node

This commit is contained in:
Mike Morris
2015-08-25 14:40:40 -04:00
79 changed files with 798 additions and 244 deletions

View File

@@ -1,5 +1,13 @@
# Changelog
## iOS 2.0.0
Repackaging 2.0.0-pre.1 as it contained no issues.
## iOS 2.0.0-pre.1
Repackaging 0.5.1 as the Mapbox iOS SDK 2.0.0 series.
## 0.5.1
### iOS

View File

@@ -274,8 +274,8 @@ By default, the SDK will be installed to `/usr/local/opt/android-sdk`. If you op
echo "export ANDROID_HOME=`brew --prefix android-sdk`" >> .bash_profile
echo "export ANDROID_NDK_PATH=`brew --cellar android-ndk`/r10e" >> .bash_profile
# Replace <path to JDK> with something like /Library/Java/JavaVirtualMachines/jdk1.8.0_31.jdk
echo "export JAVA_HOME=<path to JDK>" >> .bash_profile
# Replace <path> with something like /Library/Java/JavaVirtualMachines/jdk1.8.0_31.jdk/Contents/Home
echo "export JAVA_HOME=<path>" >> .bash_profile
echo "export STUDIO_JDK=$JAVA_HOME" >> .bash_profile
Run:
@@ -283,7 +283,7 @@ Run:
make android
open -a Android\ Studio
You can then open `android/java` in Android Studio via "Import Non-Android Studio Project".
You can then open `android/java` in Android Studio via "import project (Eclipse ADT, Gradle, etc.)".
### Setting up Android emulator

View File

@@ -159,7 +159,11 @@ void NativeMapView::invalidate() {
detach_jni_thread(vm, &env, detach);
}
void NativeMapView::swap() {
void NativeMapView::beforeRender() {
// no-op
}
void NativeMapView::afterRender() {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::swap");
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {

View File

@@ -12,7 +12,7 @@ POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=mapbox
POM_DEVELOPER_NAME=Mapbox
ANDROID_MIN_SDK=8
ANDROID_MIN_SDK=15
ANDROID_BUILD_TARGET_SDK_VERSION=23
ANDROID_BUILD_TOOLS_VERSION=23
ANDROID_BUILD_SDK_VERSION=23

View File

@@ -57,7 +57,6 @@ import com.mapzen.android.lost.api.LocationRequest;
import com.mapzen.android.lost.api.LocationServices;
import com.mapzen.android.lost.api.LostApiClient;
import com.squareup.okhttp.HttpUrl;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -209,7 +208,6 @@ public class MapView extends FrameLayout implements LocationListener {
//
// Common initialization code goes here
@TargetApi(16)
private void initialize(Context context, AttributeSet attrs) {
// Save the context
@@ -236,12 +234,11 @@ public class MapView extends FrameLayout implements LocationListener {
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getMemoryInfo(memoryInfo);
long maxMemory = memoryInfo.availMem;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
long totalMemory = memoryInfo.totalMem;
mNativeMapView = new NativeMapView(this, cachePath, dataPath, apkPath, mScreenDensity, availableProcessors, totalMemory);
} else {
throw new RuntimeException("Need to implement totalMemory on pre-Jelly Bean devices.");
maxMemory = memoryInfo.totalMem;
}
mNativeMapView = new NativeMapView(this, cachePath, dataPath, apkPath, mScreenDensity, availableProcessors, maxMemory);
// Ensure this view is interactable
setClickable(true);
@@ -251,11 +248,7 @@ public class MapView extends FrameLayout implements LocationListener {
requestFocus();
// Register the SurfaceHolder callbacks
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
surfaceView.getHolder().addCallback(new Callbacks2());
} else {
surfaceView.getHolder().addCallback(new Callbacks());
}
surfaceView.getHolder().addCallback(new CallbacksHandler());
// Touch gesture detectors
mGestureDetector = new GestureDetectorCompat(context, new GestureListener());
@@ -751,7 +744,7 @@ public class MapView extends FrameLayout implements LocationListener {
}
// This class handles SurfaceHolder callbacks
private class Callbacks implements SurfaceHolder.Callback {
private class CallbacksHandler implements SurfaceHolder.Callback, SurfaceHolder.Callback2 {
// Called when the native surface buffer has been created
// Must do all EGL/GL ES initialization here
@@ -774,10 +767,6 @@ public class MapView extends FrameLayout implements LocationListener {
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mNativeMapView.resizeFramebuffer(width, height);
}
}
@TargetApi(9)
private class Callbacks2 extends Callbacks implements SurfaceHolder.Callback2 {
// Called when we need to redraw the view
// This is called before our view is first visible to prevent an initial
@@ -843,7 +832,7 @@ public class MapView extends FrameLayout implements LocationListener {
}
// Called when user touches the screen, all positions are absolute
@Override @TargetApi(14)
@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
// Check and ignore non touch or left clicks
if ((android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) && (event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) {
@@ -1396,7 +1385,7 @@ public class MapView extends FrameLayout implements LocationListener {
// Called when the mouse pointer enters or exits the view
// or when it fades in or out due to movement
@Override @TargetApi(14)
@Override
public boolean onHoverEvent(@NonNull MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_HOVER_ENTER:
@@ -1677,7 +1666,6 @@ public class MapView extends FrameLayout implements LocationListener {
*/
@Override
public void onLocationChanged(Location location) {
Log.i(TAG, "onLocationChanged(): " + location);
updateLocation(location);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -47,7 +47,7 @@ android {
defaultConfig {
applicationId "com.mapbox.mapboxgl.testapp"
minSdkVersion 8
minSdkVersion 15
targetSdkVersion 23
versionCode 4
versionName "0.1.3"

View File

@@ -19,11 +19,11 @@
<string name="styleDark">Dark</string>
<string name="styleSatellite">Satellite</string>
<string name="styleURLMapboxStreets">asset://styles/mapbox-streets-v7.json</string>
<string name="styleURLEmerald">asset://styles/emerald-v7.json</string>
<string name="styleURLLight">asset://styles/light-v7.json</string>
<string name="styleURLDark">asset://styles/dark-v7.json</string>
<string name="styleURLSatellite">asset://styles/satellite-v7.json</string>
<string name="styleURLMapboxStreets">asset://styles/streets-v8.json</string>
<string name="styleURLEmerald">asset://styles/emerald-v8.json</string>
<string name="styleURLLight">asset://styles/light-v8.json</string>
<string name="styleURLDark">asset://styles/dark-v8.json</string>
<string name="styleURLSatellite">asset://styles/satellite-v8.json</string>
<string-array name="outdoors_class_list">
<item>Day</item>

View File

@@ -27,7 +27,8 @@ public:
void deactivate() override;
void notify() override;
void invalidate() override;
void swap() override;
void beforeRender() override;
void afterRender() override;
void notifyMapChange(mbgl::MapChange) override;

View File

@@ -62,8 +62,11 @@ public:
// (map->renderSync() from the main thread must be called as a result of this)
virtual void invalidate() = 0;
// Called from the render thread before the render begins.
virtual void beforeRender() = 0;
// Called from the render thread after the render is complete.
virtual void swap() = 0;
virtual void afterRender() = 0;
// Reads the pixel data from the current framebuffer. If your View implementation
// doesn't support reading from the framebuffer, return a null pointer.

View File

@@ -24,7 +24,8 @@ public:
void deactivate() override;
void notify() override;
void invalidate() override;
void swap() override;
void beforeRender() override;
void afterRender() override;
static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods);
static void onScroll(GLFWwindow *window, double xoffset, double yoffset);

View File

@@ -37,9 +37,11 @@ public:
void deactivate() override;
void notify() override;
void invalidate() override;
void swap() override;
void beforeRender() override;
void afterRender() override;
std::unique_ptr<StillImage> readStillImage() override;
void resizeFramebuffer();
void resize(uint16_t width, uint16_t height);
private:
@@ -52,6 +54,7 @@ private:
std::shared_ptr<HeadlessDisplay> display;
const float pixelRatio;
std::array<uint16_t, 2> dimensions;
bool needsResize;
#if MBGL_USE_CGL
CGLContextObj glContext = nullptr;

View File

@@ -13,8 +13,8 @@ struct Resource {
Source,
Tile,
Glyphs,
JSON,
Image
SpriteImage,
SpriteJSON
};
const Kind kind;

View File

@@ -45,6 +45,20 @@ struct LineProperties {
}
};
struct CircleProperties {
inline CircleProperties() {}
float radius = 5.0f;
Color color = {{ 0, 0, 0, 1 }};
float opacity = 1.0f;
std::array<float, 2> translate = {{ 0, 0 }};
TranslateAnchorType translateAnchor = TranslateAnchorType::Map;
float blur = 0;
inline bool isVisible() const {
return radius > 0 && color[3] > 0 && opacity > 0;
}
};
struct SymbolProperties {
inline SymbolProperties() {}
@@ -100,6 +114,7 @@ struct BackgroundProperties {
typedef mapbox::util::variant<
FillProperties,
LineProperties,
CircleProperties,
SymbolProperties,
RasterProperties,
BackgroundProperties,

View File

@@ -27,6 +27,7 @@ enum class StyleLayerType : uint8_t {
Unknown,
Fill,
Line,
Circle,
Symbol,
Raster,
Background
@@ -36,6 +37,7 @@ MBGL_DEFINE_ENUM_CLASS(StyleLayerTypeClass, StyleLayerType, {
{ StyleLayerType::Unknown, "unknown" },
{ StyleLayerType::Fill, "fill" },
{ StyleLayerType::Line, "line" },
{ StyleLayerType::Circle, "circle" },
{ StyleLayerType::Symbol, "symbol" },
{ StyleLayerType::Raster, "raster" },
{ StyleLayerType::Background, "background" },

View File

@@ -1,19 +1,19 @@
Pod::Spec.new do |m|
m.name = 'Mapbox-iOS-SDK'
m.version = '2.0.0-pre.1.symbols'
m.version = '2.0.0-symbols'
m.summary = 'Open source vector map solution for iOS with full styling capabilities.'
m.description = 'Open source OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa bindings.'
m.homepage = 'https://www.mapbox.com/mapbox-gl-ios/'
m.homepage = 'https://www.mapbox.com/ios-sdk/'
m.license = 'BSD'
m.author = { 'Mapbox' => 'mobile@mapbox.com' }
m.screenshot = 'https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/ios/screenshot.png'
m.social_media_url = 'https://twitter.com/mapbox'
m.documentation_url = 'https://www.mapbox.com/mapbox-gl-ios'
m.documentation_url = 'https://www.mapbox.com/ios-sdk/'
m.source = {
:http => "http://mapbox.s3.amazonaws.com/mapbox-gl-native/ios/builds/mapbox-gl-ios-#{m.version.to_s}.zip",
:http => "http://mapbox.s3.amazonaws.com/mapbox-gl-native/ios/builds/mapbox-ios-sdk-#{m.version.to_s}.zip",
:flatten => true
}

View File

@@ -9,17 +9,14 @@
static UIColor *const kTintColor = [UIColor colorWithRed:0.120 green:0.550 blue:0.670 alpha:1.000];
static NSArray *const kStyleNames = @[
@"Mapbox Streets",
@"Streets",
@"Emerald",
@"Light",
@"Dark",
@"Bright",
@"Basic",
@"Outdoors",
@"Satellite",
];
static NSString *const kStyleVersion = @"7";
static NSUInteger const kStyleVersion = 8;
@interface MBXViewController () <UIActionSheetDelegate, MGLMapViewDelegate>
@@ -301,9 +298,9 @@ mbgl::Settings_NSUserDefaults *settings = nullptr;
}
self.mapView.styleURL = [NSURL URLWithString:
[NSString stringWithFormat:@"asset://styles/%@-v%@.json",
[[styleName lowercaseString] stringByReplacingOccurrencesOfString:@" " withString:@"-"],
kStyleVersion]];
[NSString stringWithFormat:@"asset://styles/%@-v%lu.json",
[styleName lowercaseString],
(unsigned long)kStyleVersion]];
[titleButton setTitle:styleName forState:UIControlStateNormal];
}

View File

@@ -32,7 +32,7 @@
{
[super viewDidLoad];
NSURL* url = [[NSURL alloc] initWithString:@"asset://styles/mapbox-streets-v7.json"];
NSURL* url = [[NSURL alloc] initWithString:@"asset://styles/streets-v8.json"];
self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds styleURL:url];
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.mapView.delegate = self;

View File

@@ -21,7 +21,7 @@
{
[super viewDidLoad];
NSURL* url = [[NSURL alloc] initWithString:@"asset://styles/mapbox-streets-v7.json"];
NSURL* url = [[NSURL alloc] initWithString:@"asset://styles/streets-v8.json"];
self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds accessToken:nil styleURL:url];
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.mapView.delegate = self;

View File

@@ -4,11 +4,11 @@ namespace mbgl {
namespace util {
const std::vector<std::pair<std::string, std::string>> defaultStyles = {
{ "asset://styles/mapbox-streets-v7.json", "Mapbox Streets" },
{ "asset://styles/emerald-v7.json", "Emerald" },
{ "asset://styles/light-v7.json", "Light" },
{ "asset://styles/dark-v7.json", "Dark" },
{ "asset://styles/satellite-v7.json", "Satellite" }
{ "asset://styles/streets-v8.json", "Mapbox Streets" },
{ "asset://styles/emerald-v8.json", "Emerald" },
{ "asset://styles/light-v8.json", "Light" },
{ "asset://styles/dark-v8.json", "Dark" },
{ "asset://styles/satellite-v8.json", "Satellite" }
};
} // end namespace util

View File

@@ -386,7 +386,11 @@ void GLFWView::invalidate() {
glfwPostEmptyEvent();
}
void GLFWView::swap() {
void GLFWView::beforeRender() {
// no-op
}
void GLFWView::afterRender() {
glfwSwapBuffers(window);
}

View File

@@ -114,10 +114,10 @@ bool HeadlessView::isActive() {
return std::this_thread::get_id() == thread;
}
void HeadlessView::resize(const uint16_t width, const uint16_t height) {
activate();
void HeadlessView::resizeFramebuffer() {
assert(isActive());
dimensions = {{ width, height }};
if (!needsResize) return;
clearBuffers();
@@ -158,7 +158,12 @@ void HeadlessView::resize(const uint16_t width, const uint16_t height) {
throw std::runtime_error(error);
}
deactivate();
needsResize = false;
}
void HeadlessView::resize(const uint16_t width, const uint16_t height) {
dimensions = {{ width, height }};
needsResize = true;
}
std::unique_ptr<StillImage> HeadlessView::readStillImage() {
@@ -293,7 +298,11 @@ void HeadlessView::invalidate() {
// no-op
}
void HeadlessView::swap() {
void HeadlessView::beforeRender() {
resizeFramebuffer();
}
void HeadlessView::afterRender() {
// no-op
}

View File

@@ -217,7 +217,7 @@ void SQLiteCache::Impl::put(const Resource& resource, std::shared_ptr<const Resp
putStmt->bind(6 /* expires */, response->expires);
std::string data;
if (resource.kind != Resource::Image) {
if (resource.kind != Resource::SpriteImage) {
// Do not compress images, since they are typically compressed already.
data = util::compress(response->data);
}

View File

@@ -39,10 +39,11 @@
class MBGLView;
NSString *const MGLDefaultStyleName = @"mapbox-streets";
NSString *const MGLStyleVersion = @"7";
NSString *const MGLDefaultStyleName = @"streets";
NSString *const MGLDefaultStyleMarkerSymbolName = @"default_marker";
NSString *const MGLMapboxSetupDocumentationURLDisplayString = @"mapbox.com/guides/first-steps-gl-ios";
NSString *const MGLMapboxSetupDocumentationURLDisplayString = @"mapbox.com/guides/first-steps-ios-sdk";
NSUInteger const MGLStyleVersion = 8;
const NSTimeInterval MGLAnimationDuration = 0.3;
const CGSize MGLAnnotationUpdateViewportOutset = {150, 150};
@@ -190,9 +191,9 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
if ( ! styleURL)
{
styleURL = MGLURLForBundledStyleNamed([NSString stringWithFormat:@"%@-v%@",
styleURL = MGLURLForBundledStyleNamed([NSString stringWithFormat:@"%@-v%lu",
MGLDefaultStyleName.lowercaseString,
MGLStyleVersion]);
(unsigned long)MGLStyleVersion]);
}
if ( ! [styleURL scheme])
@@ -2942,7 +2943,12 @@ class MBGLView : public mbgl::View
waitUntilDone:NO];
}
void swap() override
void beforeRender() override
{
// no-op
}
void afterRender() override
{
// no-op
}

View File

@@ -102,7 +102,7 @@ cp -pv LICENSE.md "${OUTPUT}/static"
mkdir -p "${OUTPUT}/static/${NAME}.bundle"
cp -pv platform/ios/resources/* "${OUTPUT}/static/${NAME}.bundle"
mkdir -p "${OUTPUT}/static/${NAME}.bundle/styles"
cp -pv styles/styles/{dark,emerald,light,mapbox-streets,satellite}-v7.json "${OUTPUT}/static/${NAME}.bundle/styles"
cp -pv styles/styles/{dark,emerald,light,streets,satellite}-v8.json "${OUTPUT}/static/${NAME}.bundle/styles"
step "Creating API Docs..."
if [ -z `which appledoc` ]; then
@@ -111,7 +111,7 @@ if [ -z `which appledoc` ]; then
fi
DOCS_OUTPUT="${OUTPUT}/static/Docs"
git fetch --tags
DOCS_VERSION=$( git tag --sort -v:refname | grep -v '\-rc.' | sed -n '1p' | sed 's/^v//' )
DOCS_VERSION=$( git tag | sed 's/^ios-//' | sort -r | grep -v '\-rc.' | grep -v '\-pre.' | sed -n '1p' | sed 's/^v//' )
rm -rf /tmp/mbgl
mkdir -p /tmp/mbgl/
README=/tmp/mbgl/README.md

View File

@@ -0,0 +1,13 @@
#include <mbgl/geometry/circle_buffer.hpp>
#include <mbgl/platform/gl.hpp>
#include <climits>
using namespace mbgl;
void CircleVertexBuffer::add(vertex_type x, vertex_type y, float ex, float ey) {
vertex_type *vertices = static_cast<vertex_type *>(addElement());
vertices[0] = (x * 2) + ((ex + 1) / 2);
vertices[1] = (y * 2) + ((ey + 1) / 2);
}

View File

@@ -0,0 +1,27 @@
#ifndef MBGL_GEOMETRY_CIRCLE_BUFFER
#define MBGL_GEOMETRY_CIRCLE_BUFFER
#include <mbgl/geometry/buffer.hpp>
namespace mbgl {
class CircleVertexBuffer : public Buffer<
4 // 2 bytes per short * 4 of them.
> {
public:
typedef int16_t vertex_type;
/*
* Add a vertex to this buffer
*
* @param {number} x vertex position
* @param {number} y vertex position
* @param {number} ex extrude normal
* @param {number} ey extrude normal
*/
void add(vertex_type x, vertex_type y, float ex, float ey);
};
}
#endif // MBGL_GEOMETRY_CIRCLE_BUFFER

View File

@@ -326,6 +326,8 @@ bool MapContext::renderSync(const TransformState& state, const FrameData& frame)
return false;
}
view.beforeRender();
transformState = state;
// Cleanup OpenGL objects that we abandoned since the last render call.
@@ -345,7 +347,7 @@ bool MapContext::renderSync(const TransformState& state, const FrameData& frame)
callback = nullptr;
}
view.swap();
view.afterRender();
viewInvalidated = false;
data.setNeedsRepaint(style->hasTransitions() || painter->needsAnimation());

View File

@@ -9,6 +9,7 @@
#include <mbgl/util/raster.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/mapbox.hpp>
#include <rapidjson/document.h>
@@ -49,7 +50,7 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_)
loader = std::make_unique<Loader>();
FileSource* fs = util::ThreadContext::getFileSource();
loader->jsonRequest = fs->request({ Resource::Kind::JSON, jsonURL }, util::RunLoop::getLoop(),
loader->jsonRequest = fs->request({ Resource::Kind::SpriteJSON, jsonURL }, util::RunLoop::getLoop(),
[this, jsonURL](const Response& res) {
loader->jsonRequest = nullptr;
if (res.status == Response::Successful) {
@@ -65,7 +66,7 @@ Sprite::Sprite(const std::string& baseUrl, float pixelRatio_)
});
loader->spriteRequest =
fs->request({ Resource::Kind::Image, spriteURL }, util::RunLoop::getLoop(),
fs->request({ Resource::Kind::SpriteImage, spriteURL }, util::RunLoop::getLoop(),
[this, spriteURL](const Response& res) {
loader->spriteRequest = nullptr;
if (res.status == Response::Successful) {

View File

@@ -5,6 +5,7 @@
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
#include <mbgl/renderer/line_bucket.hpp>
#include <mbgl/renderer/circle_bucket.hpp>
#include <mbgl/renderer/symbol_bucket.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/constants.hpp>
@@ -47,8 +48,8 @@ Bucket* TileWorker::getBucket(const StyleLayer& layer) const {
TileParseResult TileWorker::parse(const GeometryTile& geometryTile) {
partialParse = false;
for (const auto& layer : layers) {
parseLayer(*layer, geometryTile);
for (auto i = layers.rbegin(); i != layers.rend(); i++) {
parseLayer(**i, geometryTile);
}
return partialParse ? TileData::State::partial : TileData::State::parsed;
@@ -57,8 +58,8 @@ TileParseResult TileWorker::parse(const GeometryTile& geometryTile) {
void TileWorker::redoPlacement(float angle, bool collisionDebug) {
collision->reset(angle, 0);
collision->setDebug(collisionDebug);
for (const auto& layer_desc : layers) {
auto bucket = getBucket(*layer_desc);
for (auto i = layers.rbegin(); i != layers.rend(); i++) {
auto bucket = getBucket(**i);
if (bucket) {
bucket->placeFeatures();
}
@@ -139,15 +140,22 @@ void TileWorker::parseLayer(const StyleLayer& layer, const GeometryTile& geometr
std::unique_ptr<Bucket> bucket;
if (styleBucket.type == StyleLayerType::Fill) {
switch (styleBucket.type) {
case StyleLayerType::Fill:
bucket = createFillBucket(*geometryLayer, styleBucket);
} else if (styleBucket.type == StyleLayerType::Line) {
break;
case StyleLayerType::Line:
bucket = createLineBucket(*geometryLayer, styleBucket);
} else if (styleBucket.type == StyleLayerType::Symbol) {
break;
case StyleLayerType::Circle:
bucket = createCircleBucket(*geometryLayer, styleBucket);
break;
case StyleLayerType::Symbol:
bucket = createSymbolBucket(*geometryLayer, styleBucket);
} else if (styleBucket.type == StyleLayerType::Raster) {
break;
case StyleLayerType::Raster:
return;
} else {
default:
Log::Warning(Event::ParseTile, "unknown bucket render type for layer '%s' (source layer '%s')",
styleBucket.name.c_str(), styleBucket.source_layer.c_str());
}
@@ -203,6 +211,17 @@ std::unique_ptr<Bucket> TileWorker::createLineBucket(const GeometryTileLayer& la
return bucket->hasData() ? std::move(bucket) : nullptr;
}
std::unique_ptr<Bucket> TileWorker::createCircleBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
auto bucket = std::make_unique<CircleBucket>(circleVertexBuffer,
triangleElementsBuffer);
// Circle does not have layout properties to apply.
addBucketGeometries(bucket, layer, bucket_desc.filter);
return bucket->hasData() ? std::move(bucket) : nullptr;
}
std::unique_ptr<Bucket> TileWorker::createSymbolBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
auto bucket = std::make_unique<SymbolBucket>(*collision, id.overscaling);
@@ -215,14 +234,13 @@ std::unique_ptr<Bucket> TileWorker::createSymbolBucket(const GeometryTileLayer&
layout.icon.rotation_alignment = RotationAlignmentType::Map;
layout.text.rotation_alignment = RotationAlignmentType::Map;
};
applyLayoutProperty(PropertyKey::SymbolMinDistance, bucket_desc.layout, layout.min_distance, z);
applyLayoutProperty(PropertyKey::SymbolSpacing, bucket_desc.layout, layout.spacing, z);
applyLayoutProperty(PropertyKey::SymbolAvoidEdges, bucket_desc.layout, layout.avoid_edges, z);
applyLayoutProperty(PropertyKey::IconAllowOverlap, bucket_desc.layout, layout.icon.allow_overlap, z);
applyLayoutProperty(PropertyKey::IconIgnorePlacement, bucket_desc.layout, layout.icon.ignore_placement, z);
applyLayoutProperty(PropertyKey::IconOptional, bucket_desc.layout, layout.icon.optional, z);
applyLayoutProperty(PropertyKey::IconRotationAlignment, bucket_desc.layout, layout.icon.rotation_alignment, z);
applyLayoutProperty(PropertyKey::IconMaxSize, bucket_desc.layout, layout.icon.max_size, z);
applyLayoutProperty(PropertyKey::IconImage, bucket_desc.layout, layout.icon.image, z);
applyLayoutProperty(PropertyKey::IconPadding, bucket_desc.layout, layout.icon.padding, z);
applyLayoutProperty(PropertyKey::IconRotate, bucket_desc.layout, layout.icon.rotate, z);
@@ -232,7 +250,6 @@ std::unique_ptr<Bucket> TileWorker::createSymbolBucket(const GeometryTileLayer&
applyLayoutProperty(PropertyKey::TextRotationAlignment, bucket_desc.layout, layout.text.rotation_alignment, z);
applyLayoutProperty(PropertyKey::TextField, bucket_desc.layout, layout.text.field, z);
applyLayoutProperty(PropertyKey::TextFont, bucket_desc.layout, layout.text.font, z);
applyLayoutProperty(PropertyKey::TextMaxSize, bucket_desc.layout, layout.text.max_size, z);
applyLayoutProperty(PropertyKey::TextMaxWidth, bucket_desc.layout, layout.text.max_width, z);
applyLayoutProperty(PropertyKey::TextLineHeight, bucket_desc.layout, layout.text.line_height, z);
applyLayoutProperty(PropertyKey::TextLetterSpacing, bucket_desc.layout, layout.text.letter_spacing, z);
@@ -248,6 +265,11 @@ std::unique_ptr<Bucket> TileWorker::createSymbolBucket(const GeometryTileLayer&
applyLayoutProperty(PropertyKey::TextOffset, bucket_desc.layout, layout.text.offset, z);
applyLayoutProperty(PropertyKey::TextAllowOverlap, bucket_desc.layout, layout.text.allow_overlap, z);
applyLayoutProperty(PropertyKey::IconSize, bucket_desc.layout, layout.icon.size, z + 1);
applyLayoutProperty(PropertyKey::IconSize, bucket_desc.layout, layout.icon.max_size, 18);
applyLayoutProperty(PropertyKey::TextSize, bucket_desc.layout, layout.text.size, z + 1);
applyLayoutProperty(PropertyKey::TextSize, bucket_desc.layout, layout.text.max_size, 18);
if (bucket->needsDependencies(layer, bucket_desc.filter, *style.glyphStore, *style.sprite)) {
partialParse = true;
}

View File

@@ -6,6 +6,7 @@
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/fill_buffer.hpp>
#include <mbgl/geometry/line_buffer.hpp>
#include <mbgl/geometry/circle_buffer.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/style/filter_expression.hpp>
@@ -52,6 +53,7 @@ private:
std::unique_ptr<Bucket> createFillBucket(const GeometryTileLayer&, const StyleBucket&);
std::unique_ptr<Bucket> createLineBucket(const GeometryTileLayer&, const StyleBucket&);
std::unique_ptr<Bucket> createCircleBucket(const GeometryTileLayer&, const StyleBucket&);
std::unique_ptr<Bucket> createSymbolBucket(const GeometryTileLayer&, const StyleBucket&);
template <class Bucket>
@@ -68,6 +70,7 @@ private:
FillVertexBuffer fillVertexBuffer;
LineVertexBuffer lineVertexBuffer;
CircleVertexBuffer circleVertexBuffer;
TriangleElementsBuffer triangleElementsBuffer;
LineElementsBuffer lineElementsBuffer;

View File

@@ -0,0 +1,92 @@
#include <mbgl/renderer/circle_bucket.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/shader/circle_shader.hpp>
using namespace mbgl;
CircleBucket::CircleBucket(CircleVertexBuffer& vertexBuffer,
TriangleElementsBuffer& elementsBuffer)
: vertexBuffer_(vertexBuffer)
, elementsBuffer_(elementsBuffer)
, vertexStart_(vertexBuffer_.index())
, elementsStart_(elementsBuffer_.index()) {
}
CircleBucket::~CircleBucket() {
// Do not remove. header file only contains forward definitions to unique pointers.
}
void CircleBucket::upload() {
vertexBuffer_.upload();
elementsBuffer_.upload();
uploaded = true;
}
void CircleBucket::render(Painter& painter,
const StyleLayer& layer_desc,
const TileID& id,
const mat4& matrix) {
painter.renderCircle(*this, layer_desc, id, matrix);
}
bool CircleBucket::hasData() const {
return !triangleGroups_.empty();
}
void CircleBucket::addGeometry(const GeometryCollection& geometryCollection) {
for (auto& circle : geometryCollection) {
for(auto & geometry : circle) {
auto x = geometry.x;
auto y = geometry.y;
// this geometry will be of the Point type, and we'll derive
// two triangles from it.
//
// ┌─────────┐
// │ 4 3 │
// │ │
// │ 1 2 │
// └─────────┘
//
vertexBuffer_.add(x, y, -1, -1); // 1
vertexBuffer_.add(x, y, 1, -1); // 2
vertexBuffer_.add(x, y, 1, 1); // 3
vertexBuffer_.add(x, y, -1, 1); // 4
if (!triangleGroups_.size() || (triangleGroups_.back()->vertex_length + 4 > 65535)) {
// Move to a new group because the old one can't hold the geometry.
triangleGroups_.emplace_back(std::make_unique<TriangleGroup>());
}
TriangleGroup& group = *triangleGroups_.back();
auto index = group.vertex_length;
// 1, 2, 3
// 1, 4, 3
elementsBuffer_.add(index, index + 1, index + 2);
elementsBuffer_.add(index, index + 3, index + 2);
group.vertex_length += 4;
group.elements_length += 2;
}
}
}
void CircleBucket::drawCircles(CircleShader& shader) {
char* vertexIndex = BUFFER_OFFSET(vertexStart_ * vertexBuffer_.itemSize);
char* elementsIndex = BUFFER_OFFSET(elementsStart_ * elementsBuffer_.itemSize);
for (auto& group : triangleGroups_) {
assert(group);
if (!group->elements_length) continue;
group->array[0].bind(shader, vertexBuffer_, elementsBuffer_, vertexIndex);
MBGL_CHECK_ERROR(glDrawElements(GL_TRIANGLES, group->elements_length * 3, GL_UNSIGNED_SHORT, elementsIndex));
vertexIndex += group->vertex_length * vertexBuffer_.itemSize;
elementsIndex += group->elements_length * elementsBuffer_.itemSize;
}
}

View File

@@ -0,0 +1,46 @@
#ifndef MBGL_RENDERER_CIRCLE_BUCKET
#define MBGL_RENDERER_CIRCLE_BUCKET
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/map/geometry_tile.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/circle_buffer.hpp>
#include <mbgl/style/style_bucket.hpp>
#include <mbgl/style/style_layout.hpp>
namespace mbgl {
class CircleVertexBuffer;
class CircleShader;
class CircleBucket : public Bucket {
using TriangleGroup = ElementGroup<3>;
public:
CircleBucket(CircleVertexBuffer &vertexBuffer, TriangleElementsBuffer &elementsBuffer);
~CircleBucket() override;
void upload() override;
void render(Painter&, const StyleLayer&, const TileID&, const mat4&) override;
bool hasData() const;
void addGeometry(const GeometryCollection&);
void drawCircles(CircleShader& shader);
private:
CircleVertexBuffer& vertexBuffer_;
TriangleElementsBuffer& elementsBuffer_;
const size_t vertexStart_;
const size_t elementsStart_;
std::vector<std::unique_ptr<TriangleGroup>> triangleGroups_;
};
} // namespace mbgl
#endif // MBGL_RENDERER_CIRCLE_BUCKET

View File

@@ -27,6 +27,7 @@
#include <mbgl/shader/sdf_shader.hpp>
#include <mbgl/shader/dot_shader.hpp>
#include <mbgl/shader/box_shader.hpp>
#include <mbgl/shader/circle_shader.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/mat3.hpp>
@@ -66,6 +67,7 @@ void Painter::setup() {
assert(sdfGlyphShader);
assert(sdfIconShader);
assert(dotShader);
assert(circleShader);
// Blending
@@ -100,6 +102,7 @@ void Painter::setupShaders() {
if (!sdfIconShader) sdfIconShader = std::make_unique<SDFIconShader>();
if (!dotShader) dotShader = std::make_unique<DotShader>();
if (!collisionBoxShader) collisionBoxShader = std::make_unique<CollisionBoxShader>();
if (!circleShader) circleShader = std::make_unique<CircleShader>();
}
void Painter::resize() {

View File

@@ -38,6 +38,7 @@ struct FrameData;
class DebugBucket;
class FillBucket;
class LineBucket;
class CircleBucket;
class SymbolBucket;
class RasterBucket;
@@ -50,6 +51,7 @@ class LineShader;
class LinejoinShader;
class LineSDFShader;
class LinepatternShader;
class CircleShader;
class PatternShader;
class IconShader;
class RasterShader;
@@ -100,6 +102,7 @@ public:
void renderDebugText(DebugBucket& bucket, const mat4 &matrix);
void renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const TileID& id, const mat4 &matrix);
void renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const TileID& id, const mat4 &matrix);
void renderCircle(CircleBucket& bucket, const StyleLayer &layer_desc, const TileID& id, const mat4 &matrix);
void renderSymbol(SymbolBucket& bucket, const StyleLayer &layer_desc, const TileID& id, const mat4 &matrix);
void renderRaster(RasterBucket& bucket, const StyleLayer &layer_desc, const TileID& id, const mat4 &matrix);
void renderBackground(const StyleLayer &layer_desc);
@@ -222,6 +225,7 @@ public:
std::unique_ptr<SDFIconShader> sdfIconShader;
std::unique_ptr<DotShader> dotShader;
std::unique_ptr<CollisionBoxShader> collisionBoxShader;
std::unique_ptr<CircleShader> circleShader;
StaticVertexBuffer backgroundBuffer = {
{ -1, -1 }, { 1, -1 },

View File

@@ -0,0 +1,49 @@
#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/circle_bucket.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/style_layout.hpp>
#include <mbgl/map/sprite.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/map/map_data.hpp>
#include <mbgl/shader/circle_shader.hpp>
using namespace mbgl;
void Painter::renderCircle(CircleBucket& bucket,
const StyleLayer& layer_desc,
const TileID& id,
const mat4& matrix) {
// Abort early.
if (pass == RenderPass::Opaque) return;
config.stencilTest = false;
const CircleProperties& properties = layer_desc.getProperties<CircleProperties>();
mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor);
Color color = properties.color;
color[0] *= properties.opacity;
color[1] *= properties.opacity;
color[2] *= properties.opacity;
color[3] *= properties.opacity;
// Antialiasing factor: this is a minimum blur distance that serves as
// a faux-antialiasing for the circle. since blur is a ratio of the circle's
// size and the intent is to keep the blur at roughly 1px, the two
// are inversely related.
float antialiasing = 1 / data.pixelRatio / properties.radius;
useProgram(circleShader->program);
circleShader->u_matrix = vtxMatrix;
circleShader->u_exmatrix = projMatrix;
circleShader->u_color = color;
circleShader->u_blur = std::max(properties.blur, antialiasing);
circleShader->u_size = properties.radius;
bucket.drawCircles(*circleShader);
}

View File

@@ -49,7 +49,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
sdfShader.u_texsize = texsize;
// adjust min/max zooms for variable font sies
float zoomAdjust = std::log(fontSize / bucketProperties.max_size) / std::log(2);
float zoomAdjust = std::log(fontSize / bucketProperties.size) / std::log(2);
sdfShader.u_zoom = (state.getNormalizedZoom() - zoomAdjust) * 10; // current zoom level
@@ -69,7 +69,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
// We're drawing in the translucent pass which is bottom-to-top, so we need
// to draw the halo first.
if (styleProperties.halo_color[3] > 0.0f) {
if (styleProperties.halo_color[3] > 0.0f && styleProperties.halo_width > 0.0f) {
sdfShader.u_gamma = styleProperties.halo_blur * blurOffset / fontScale / sdfPx + gamma;
if (styleProperties.opacity < 1.0f) {
@@ -157,8 +157,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
? state.getAngle()
: 0;
// If layerStyle.size > bucket.info.fontSize then labels may collide
const float fontSize = properties.icon.size != 0 ? properties.icon.size : layout.icon.max_size;
const float fontSize = properties.icon.size;
const float fontScale = fontSize / 1.0f;
spriteAtlas->bind(state.isChanging() || layout.placement == PlacementType::Line || angleOffset != 0 || fontScale != 1 || sdf);
@@ -191,7 +190,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
iconShader->u_texsize = {{ float(spriteAtlas->getWidth()) / 4.0f, float(spriteAtlas->getHeight()) / 4.0f }};
// adjust min/max zooms for variable font sies
float zoomAdjust = std::log(fontSize / layout.icon.max_size) / std::log(2);
float zoomAdjust = std::log(fontSize / layout.icon.size) / std::log(2);
iconShader->u_zoom = (state.getNormalizedZoom() - zoomAdjust) * 10; // current zoom level
iconShader->u_fadedist = 0 * 10;

View File

@@ -274,10 +274,11 @@ void SymbolBucket::addFeature(const std::vector<std::vector<Coordinate>> &lines,
const float minScale = 0.5f;
const float glyphSize = 24.0f;
const float fontScale = layout.text.max_size / glyphSize;
const float fontScale = layout.text.size / glyphSize;
const float textBoxScale = collision.tilePixelRatio * fontScale;
const float iconBoxScale = collision.tilePixelRatio * layout.icon.max_size;
const float symbolSpacing = collision.tilePixelRatio * layout.min_distance;
const float textMaxBoxScale = collision.tilePixelRatio * layout.text.max_size / glyphSize;
const float iconBoxScale = collision.tilePixelRatio * layout.icon.size;
const float symbolSpacing = collision.tilePixelRatio * layout.spacing;
const bool avoidEdges = layout.avoid_edges && layout.placement != PlacementType::Line;
const float textPadding = layout.text.padding * collision.tilePixelRatio;
const float iconPadding = layout.icon.padding * collision.tilePixelRatio;
@@ -302,7 +303,7 @@ void SymbolBucket::addFeature(const std::vector<std::vector<Coordinate>> &lines,
// Calculate the anchor points around which you want to place labels
Anchors anchors = isLine ?
getAnchors(line, symbolSpacing, textMaxAngle, shapedText.left, shapedText.right, shapedIcon.left, shapedIcon.right, glyphSize, textBoxScale, overscaling) :
getAnchors(line, symbolSpacing, textMaxAngle, shapedText.left, shapedText.right, shapedIcon.left, shapedIcon.right, glyphSize, textMaxBoxScale, overscaling) :
Anchors({ Anchor(float(line[0].x), float(line[0].y), 0, minScale) });
// For each potential label, create the placement features used to check for collisions, and the quads use for rendering.

View File

@@ -0,0 +1,10 @@
uniform vec4 u_color;
uniform float u_blur;
uniform float u_size;
varying vec2 v_extrude;
void main() {
float t = smoothstep(1.0 - u_blur, 1.0, length(v_extrude));
gl_FragColor = u_color * (1.0 - t);
}

View File

@@ -0,0 +1,23 @@
// set by gl_util
uniform float u_size;
attribute vec2 a_pos;
uniform mat4 u_matrix;
uniform mat4 u_exmatrix;
varying vec2 v_extrude;
void main(void) {
// unencode the extrusion vector that we snuck into the a_pos vector
v_extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0);
vec4 extrude = u_exmatrix * vec4(v_extrude * u_size, 0, 0);
// multiply a_pos by 0.5, since we had it * 2 in order to sneak
// in extrusion data
gl_Position = u_matrix * vec4(a_pos * 0.5, 0, 1);
// gl_Position is divided by gl_Position.w after this shader runs.
// Multiply the extrude by it so that it isn't affected by it.
gl_Position += extrude * gl_Position.w;
}

View File

@@ -0,0 +1,21 @@
#include <mbgl/shader/circle_shader.hpp>
#include <mbgl/shader/shaders.hpp>
#include <mbgl/platform/gl.hpp>
#include <cstdio>
using namespace mbgl;
CircleShader::CircleShader()
: Shader(
"circle",
shaders[CIRCLE_SHADER].vertex,
shaders[CIRCLE_SHADER].fragment
) {
a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
}
void CircleShader::bind(char *offset) {
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 4, offset));
}

View File

@@ -0,0 +1,27 @@
#ifndef MBGL_SHADER_CIRCLE_SHADER
#define MBGL_SHADER_CIRCLE_SHADER
#include <mbgl/shader/shader.hpp>
#include <mbgl/shader/uniform.hpp>
namespace mbgl {
class CircleShader : public Shader {
public:
CircleShader();
void bind(char *offset);
UniformMatrix<4> u_matrix = {"u_matrix", *this};
UniformMatrix<4> u_exmatrix = {"u_exmatrix", *this};
Uniform<std::array<float, 4>> u_color = {"u_color", *this};
Uniform<float> u_size = {"u_size", *this};
Uniform<float> u_blur = {"u_blur", *this};
private:
int32_t a_pos = -1;
};
}
#endif // MBGL_SHADER_CIRCLE_SHADER

View File

@@ -55,6 +55,11 @@ Request* DefaultFileSource::request(const Resource& resource,
url = util::mapbox::normalizeGlyphsURL(resource.url, accessToken);
break;
case Resource::Kind::SpriteImage:
case Resource::Kind::SpriteJSON:
url = util::mapbox::normalizeSpriteURL(resource.url, accessToken);
break;
default:
url = resource.url;
}

View File

@@ -21,6 +21,13 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::LineGapWidth, defaultStyleProperties<LineProperties>().gap_width },
{ PropertyKey::LineBlur, defaultStyleProperties<LineProperties>().blur },
{ PropertyKey::CircleRadius, defaultStyleProperties<CircleProperties>().radius },
{ PropertyKey::CircleColor, defaultStyleProperties<CircleProperties>().color },
{ PropertyKey::CircleOpacity, defaultStyleProperties<CircleProperties>().opacity },
{ PropertyKey::CircleTranslate, defaultStyleProperties<CircleProperties>().translate },
{ PropertyKey::CircleTranslateAnchor, defaultStyleProperties<CircleProperties>().translateAnchor },
{ PropertyKey::CircleBlur, defaultStyleProperties<CircleProperties>().blur },
{ PropertyKey::IconOpacity, defaultStyleProperties<SymbolProperties>().icon.opacity },
{ PropertyKey::IconSize, defaultStyleProperties<SymbolProperties>().icon.size },
{ PropertyKey::IconColor, defaultStyleProperties<SymbolProperties>().icon.color },
@@ -56,14 +63,13 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::LineRoundLimit, defaultStyleLayout<StyleLayoutLine>().round_limit },
{ PropertyKey::SymbolPlacement, defaultStyleLayout<StyleLayoutSymbol>().placement },
{ PropertyKey::SymbolMinDistance, defaultStyleLayout<StyleLayoutSymbol>().min_distance },
{ PropertyKey::SymbolSpacing, defaultStyleLayout<StyleLayoutSymbol>().spacing },
{ PropertyKey::SymbolAvoidEdges, defaultStyleLayout<StyleLayoutSymbol>().avoid_edges },
{ PropertyKey::IconAllowOverlap, defaultStyleLayout<StyleLayoutSymbol>().icon.allow_overlap },
{ PropertyKey::IconIgnorePlacement, defaultStyleLayout<StyleLayoutSymbol>().icon.ignore_placement },
{ PropertyKey::IconOptional, defaultStyleLayout<StyleLayoutSymbol>().icon.optional },
{ PropertyKey::IconRotationAlignment, defaultStyleLayout<StyleLayoutSymbol>().icon.rotation_alignment },
{ PropertyKey::IconMaxSize, defaultStyleLayout<StyleLayoutSymbol>().icon.max_size },
{ PropertyKey::IconImage, defaultStyleLayout<StyleLayoutSymbol>().icon.image },
{ PropertyKey::IconRotate, defaultStyleLayout<StyleLayoutSymbol>().icon.rotate },
{ PropertyKey::IconPadding, defaultStyleLayout<StyleLayoutSymbol>().icon.padding },
@@ -73,7 +79,6 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::TextRotationAlignment, defaultStyleLayout<StyleLayoutSymbol>().text.rotation_alignment },
{ PropertyKey::TextField, defaultStyleLayout<StyleLayoutSymbol>().text.field },
{ PropertyKey::TextFont, defaultStyleLayout<StyleLayoutSymbol>().text.font },
{ PropertyKey::TextMaxSize, defaultStyleLayout<StyleLayoutSymbol>().text.max_size },
{ PropertyKey::TextMaxWidth, defaultStyleLayout<StyleLayoutSymbol>().text.max_width },
{ PropertyKey::TextLineHeight, defaultStyleLayout<StyleLayoutSymbol>().text.line_height },
{ PropertyKey::TextLetterSpacing, defaultStyleLayout<StyleLayoutSymbol>().text.letter_spacing },

View File

@@ -27,8 +27,15 @@ enum class PropertyKey {
LineMiterLimit,
LineRoundLimit,
CircleRadius,
CircleColor,
CircleOpacity,
CircleTranslate,
CircleTranslateAnchor,
CircleBlur,
SymbolPlacement,
SymbolMinDistance,
SymbolSpacing,
SymbolAvoidEdges,
IconOpacity,
@@ -44,7 +51,6 @@ enum class PropertyKey {
IconIgnorePlacement,
IconOptional,
IconRotationAlignment,
IconMaxSize,
IconImage,
IconOffset,
IconPadding,
@@ -52,7 +58,6 @@ enum class PropertyKey {
IconKeepUpright,
TextOpacity,
TextSize,
TextColor,
TextHaloColor,
TextHaloWidth,
@@ -63,7 +68,7 @@ enum class PropertyKey {
TextRotationAlignment,
TextField,
TextFont,
TextMaxSize,
TextSize,
TextMaxWidth,
TextLineHeight,
TextLetterSpacing,

View File

@@ -19,6 +19,8 @@ bool StyleLayer::isVisible() const {
return getProperties<FillProperties>().isVisible();
case StyleLayerType::Line:
return getProperties<LineProperties>().isVisible();
case StyleLayerType::Circle:
return getProperties<CircleProperties>().isVisible();
case StyleLayerType::Symbol:
return getProperties<SymbolProperties>().isVisible();
case StyleLayerType::Raster:
@@ -208,12 +210,23 @@ void StyleLayer::applyStyleProperties<LineProperties>(const float z, const TimeP
applyStyleProperty(PropertyKey::LineWidth, line.dash_line_width, std::floor(z), now, zoomHistory);
}
template <>
void StyleLayer::applyStyleProperties<CircleProperties>(const float z, const TimePoint& now, const ZoomHistory &zoomHistory) {
properties.set<CircleProperties>();
CircleProperties& circle = properties.get<CircleProperties>();
applyTransitionedStyleProperty(PropertyKey::CircleRadius, circle.radius, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::CircleColor, circle.color, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::CircleOpacity, circle.opacity, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::CircleTranslate, circle.translate, z, now, zoomHistory);
applyStyleProperty(PropertyKey::CircleTranslateAnchor, circle.translateAnchor, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::CircleBlur, circle.blur, z, now, zoomHistory);
}
template <>
void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const TimePoint& now, const ZoomHistory &zoomHistory) {
properties.set<SymbolProperties>();
SymbolProperties &symbol = properties.get<SymbolProperties>();
applyTransitionedStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::IconSize, symbol.icon.size, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::IconColor, symbol.icon.color, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::IconHaloColor, symbol.icon.halo_color, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::IconHaloWidth, symbol.icon.halo_width, z, now, zoomHistory);
@@ -222,13 +235,24 @@ void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const Tim
applyStyleProperty(PropertyKey::IconTranslateAnchor, symbol.icon.translate_anchor, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::TextOpacity, symbol.text.opacity, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::TextSize, symbol.text.size, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::TextColor, symbol.text.color, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::TextHaloColor, symbol.text.halo_color, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::TextHaloWidth, symbol.text.halo_width, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::TextHaloBlur, symbol.text.halo_blur, z, now, zoomHistory);
applyTransitionedStyleProperty(PropertyKey::TextTranslate, symbol.text.translate, z, now, zoomHistory);
applyStyleProperty(PropertyKey::TextTranslateAnchor, symbol.text.translate_anchor, z, now, zoomHistory);
// text-size and icon-size are layout properties but they also need to be evaluated as paint properties:
auto it = bucket->layout.properties.find(PropertyKey::IconSize);
if (it != bucket->layout.properties.end()) {
const PropertyEvaluator<float> evaluator(z, zoomHistory);
symbol.icon.size = mapbox::util::apply_visitor(evaluator, it->second);
}
it = bucket->layout.properties.find(PropertyKey::TextSize);
if (it != bucket->layout.properties.end()) {
const PropertyEvaluator<float> evaluator(z, zoomHistory);
symbol.text.size = mapbox::util::apply_visitor(evaluator, it->second);
}
}
template <>
@@ -259,6 +283,7 @@ void StyleLayer::updateProperties(float z, const TimePoint& now, ZoomHistory &zo
switch (type) {
case StyleLayerType::Fill: applyStyleProperties<FillProperties>(z, now, zoomHistory); break;
case StyleLayerType::Line: applyStyleProperties<LineProperties>(z, now, zoomHistory); break;
case StyleLayerType::Circle: applyStyleProperties<CircleProperties>(z, now, zoomHistory); break;
case StyleLayerType::Symbol: applyStyleProperties<SymbolProperties>(z, now, zoomHistory); break;
case StyleLayerType::Raster: applyStyleProperties<RasterProperties>(z, now, zoomHistory); break;
case StyleLayerType::Background: applyStyleProperties<BackgroundProperties>(z, now, zoomHistory); break;

View File

@@ -42,7 +42,7 @@ public:
StyleLayoutSymbol& operator=(const StyleLayoutSymbol &) = delete;
PlacementType placement = PlacementType::Point;
float min_distance = 250.0f;
float spacing = 250.0f;
bool avoid_edges = false;
struct {
@@ -50,6 +50,7 @@ public:
bool ignore_placement = false;
bool optional = false;
RotationAlignmentType rotation_alignment = RotationAlignmentType::Viewport;
float size = 1.0f;
float max_size = 1.0f;
std::string image;
float rotate = 0.0f;
@@ -62,6 +63,7 @@ public:
RotationAlignmentType rotation_alignment = RotationAlignmentType::Viewport;
std::string field;
std::string font = "Open Sans Regular, Arial Unicode MS Regular";
float size = 16.0f;
float max_size = 16.0f;
float max_width = 15.0f /* em */;
float line_height = 1.2f /* em */;

View File

@@ -27,8 +27,11 @@ StyleParser::StyleParser(MapData& data_)
}
void StyleParser::parse(JSVal document) {
if (document.HasMember("constants")) {
parseConstants(document["constants"]);
if (document.HasMember("version")) {
version = document["version"].GetInt();
if (version != 8) {
Log::Warning(Event::ParseStyle, "current renderer implementation only supports style spec version 8; using an outdated style will cause rendering errors");
}
}
if (document.HasMember("sources")) {
@@ -89,37 +92,11 @@ void StyleParser::parse(JSVal document) {
}
}
void StyleParser::parseConstants(JSVal value) {
if (value.IsObject()) {
rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
for (; itr != value.MemberEnd(); ++itr) {
std::string name { itr->name.GetString(), itr->name.GetStringLength() };
// Discard constants that don't start with an @ sign.
if (name.length() && name[0] == '@') {
constants.emplace(std::move(name), &itr->value);
}
}
} else {
Log::Warning(Event::ParseStyle, "constants must be an object");
}
}
JSVal StyleParser::replaceConstant(JSVal value) {
if (value.IsString()) {
auto it = constants.find({ value.GetString(), value.GetStringLength() });
if (it != constants.end()) {
return *it->second;
}
}
return value;
}
#pragma mark - Parse Render Properties
template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, bool &target, const char *name) {
if (value.HasMember(name)) {
JSVal property = replaceConstant(value[name]);
JSVal property = value[name];
if (property.IsBool()) {
target = property.GetBool();
return StyleParserSuccess;
@@ -133,7 +110,7 @@ template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, boo
template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, std::string &target, const char *name) {
if (value.HasMember(name)) {
JSVal property = replaceConstant(value[name]);
JSVal property = value[name];
if (property.IsString()) {
target = { property.GetString(), property.GetStringLength() };
return StyleParserSuccess;
@@ -146,7 +123,7 @@ template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, std
template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, float &target, const char *name) {
if (value.HasMember(name)) {
JSVal property = replaceConstant(value[name]);
JSVal property = value[name];
if (property.IsNumber()) {
target = property.GetDouble();
return StyleParserSuccess;
@@ -159,7 +136,7 @@ template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, flo
template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, uint16_t &target, const char *name) {
if (value.HasMember(name)) {
JSVal property = replaceConstant(value[name]);
JSVal property = value[name];
if (property.IsUint()) {
unsigned int int_value = property.GetUint();
if (int_value > std::numeric_limits<uint16_t>::max()) {
@@ -178,7 +155,7 @@ template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, uin
template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, int32_t &target, const char *name) {
if (value.HasMember(name)) {
JSVal property = replaceConstant(value[name]);
JSVal property = value[name];
if (property.IsInt()) {
target = property.GetInt();
return StyleParserSuccess;
@@ -191,7 +168,7 @@ template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, int
template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, vec2<float> &target, const char *name) {
if (value.HasMember(name)) {
JSVal property = replaceConstant(value[name]);
JSVal property = value[name];
if (property.IsArray()) {
if (property.Size() >= 2) {
target.x = property[(rapidjson::SizeType)0].GetDouble();
@@ -210,7 +187,7 @@ template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, vec
template<typename Parser, typename T>
StyleParser::Status StyleParser::parseRenderProperty(JSVal value, T &target, const char *name) {
if (value.HasMember(name)) {
JSVal property = replaceConstant(value[name]);
JSVal property = value[name];
if (property.IsString()) {
target = Parser({ property.GetString(), property.GetStringLength() });
return StyleParserSuccess;
@@ -270,7 +247,7 @@ StyleParser::Result<std::vector<float>> StyleParser::parseFloatArray(JSVal value
std::vector<float> vec;
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
JSVal part = replaceConstant(value[i]);
JSVal part = value[i];
if (!part.IsNumber()) {
Log::Warning(Event::ParseStyle, "dasharray value must be an array of numbers");
return Result<std::vector<float>> { StyleParserFailure, std::vector<float>() };
@@ -297,9 +274,8 @@ StyleParser::Result<std::array<float, 2>> StyleParser::parseProperty(JSVal value
template <>
StyleParser::Result<float> StyleParser::parseProperty(JSVal value, const char* property_name) {
JSVal rvalue = replaceConstant(value);
if (rvalue.IsNumber()) {
return Result<float> { StyleParserSuccess, rvalue.GetDouble() };
if (value.IsNumber()) {
return Result<float> { StyleParserSuccess, value.GetDouble() };
} else {
Log::Warning(Event::ParseStyle, "value of '%s' must be a number, or a number function", property_name);
return Result<float> { StyleParserFailure, 0.0f };
@@ -308,23 +284,20 @@ StyleParser::Result<float> StyleParser::parseProperty(JSVal value, const char* p
template <>
StyleParser::Result<Color> StyleParser::parseProperty(JSVal value, const char*) {
JSVal rvalue = replaceConstant(value);
return Result<Color> { StyleParserSuccess, parseColor(rvalue) };
return Result<Color> { StyleParserSuccess, parseColor(value) };
}
template <>
StyleParser::Result<Faded<std::vector<float>>> StyleParser::parseProperty(JSVal value, const char*) {
Faded<std::vector<float>> parsed;
JSVal rvalue = replaceConstant(value);
parsed.to = std::get<1>(parseFloatArray(rvalue));
parsed.to = std::get<1>(parseFloatArray(value));
return Result<Faded<std::vector<float>>> { StyleParserSuccess, parsed };
}
template <>
StyleParser::Result<Faded<std::string>> StyleParser::parseProperty(JSVal value, const char *property_name) {
JSVal rvalue = replaceConstant(value);
Faded<std::string> parsed;
if (rvalue.IsString()) {
if (value.IsString()) {
parsed.to = { value.GetString(), value.GetStringLength() };
return Result<Faded<std::string>> { StyleParserSuccess, parsed };
} else {
@@ -357,7 +330,7 @@ StyleParser::Result<std::vector<std::pair<float, T>>> StyleParser::parseStops(JS
return Result<std::vector<std::pair<float, T>>> { StyleParserFailure, {}};
}
stops.emplace_back(z.GetDouble(), std::get<1>(parseProperty<T>(replaceConstant(stop[rapidjson::SizeType(1)]), property_name)));
stops.emplace_back(z.GetDouble(), std::get<1>(parseProperty<T>(stop[rapidjson::SizeType(1)], property_name)));
} else {
Log::Warning(Event::ParseStyle, "function argument must be a numeric value");
return Result<std::vector<std::pair<float, T>>> { StyleParserFailure, {}};
@@ -448,7 +421,7 @@ StyleParser::Status StyleParser::parseOptionalProperty(const char *property_name
if (!value.HasMember(property_name)) {
return StyleParserFailure;
} else {
return setProperty<T>(replaceConstant(value[property_name]), property_name, key, klass);
return setProperty<T>(value[property_name], property_name, key, klass);
}
}
@@ -458,32 +431,38 @@ StyleParser::Status StyleParser::parseOptionalProperty(const char *property_name
return StyleParserFailure;
} else {
if (value.HasMember(transition_name)) {
return setProperty<T>(replaceConstant(value[property_name]), property_name, key, klass, value[transition_name]);
return setProperty<T>(value[property_name], property_name, key, klass, value[transition_name]);
} else {
JSVal val = JSVal(rapidjson::kObjectType);
return setProperty<T>(replaceConstant(value[property_name]), property_name, key, klass, val);
return setProperty<T>(value[property_name], property_name, key, klass, val);
}
}
}
std::string normalizeFontStack(const std::string &name) {
namespace algo = boost::algorithm;
std::vector<std::string> parts;
algo::split(parts, name, algo::is_any_of(","), algo::token_compress_on);
std::for_each(parts.begin(), parts.end(), [](std::string& str) { algo::trim(str); });
return algo::join(parts, ", ");
}
template<> StyleParser::Result<std::string> StyleParser::parseProperty(JSVal value, const char *property_name) {
if (!value.IsString()) {
if (std::string { "text-font" } == property_name) {
if (!value.IsArray()) {
Log::Warning(Event::ParseStyle, "value of '%s' must be an array of strings", property_name);
return Result<std::string> { StyleParserFailure, std::string() };
} else {
std::string result = "";
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
JSVal stop = value[i];
if (stop.IsString()) {
result += stop.GetString();
if (i < value.Size()-1) {
result += ",";
}
} else {
Log::Warning(Event::ParseStyle, "text-font members must be strings");
return Result<std::string> { StyleParserFailure, {} };
}
}
return Result<std::string> { StyleParserSuccess, result };
}
} else if (!value.IsString()) {
Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name);
return Result<std::string> { StyleParserFailure, std::string() };
}
if (std::string { "text-font" } == property_name) {
return Result<std::string> {
StyleParserSuccess, normalizeFontStack({ value.GetString(), value.GetStringLength() })
};
} else {
return Result<std::string> { StyleParserSuccess, { value.GetString(), value.GetStringLength() } };
}
@@ -767,7 +746,7 @@ void StyleParser::parseLayer(std::pair<JSVal, util::ptr<StyleLayer>> &pair) {
if (value.HasMember("ref")) {
// This layer is referencing another layer. Inherit the bucket from that layer, if we
// already parsed it.
parseReference(replaceConstant(value["ref"]), layer);
parseReference(value["ref"], layer);
} else {
// Otherwise, parse the source/source-layer/filter/render keys to form the bucket.
parseBucket(value, layer);
@@ -782,10 +761,10 @@ void StyleParser::parsePaints(JSVal value, std::map<ClassID, ClassProperties> &p
const std::string name { itr->name.GetString(), itr->name.GetStringLength() };
if (name == "paint") {
parsePaint(replaceConstant(itr->value), paints[ClassID::Default]);
parsePaint(itr->value, paints[ClassID::Default]);
} else if (name.compare(0, 6, "paint.") == 0 && name.length() > 6) {
const ClassID class_id = ClassDictionary::Get().lookup(name.substr(6));
parsePaint(replaceConstant(itr->value), paints[class_id]);
parsePaint(itr->value, paints[class_id]);
}
}
}
@@ -803,7 +782,7 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
parseOptionalProperty<Function<std::array<float, 2>>>("fill-translate", Key::FillTranslate, klass, value);
parseOptionalProperty<PropertyTransition>("fill-translate-transition", Key::FillTranslate, klass, value);
parseOptionalProperty<Function<TranslateAnchorType>>("fill-translate-anchor", Key::FillTranslateAnchor, klass, value);
parseOptionalProperty<PiecewiseConstantFunction<Faded<std::string>>>("fill-image", Key::FillImage, klass, value, "fill-image-transition");
parseOptionalProperty<PiecewiseConstantFunction<Faded<std::string>>>("fill-pattern", Key::FillImage, klass, value, "fill-pattern-transition");
parseOptionalProperty<Function<float>>("line-opacity", Key::LineOpacity, klass, value);
parseOptionalProperty<PropertyTransition>("line-opacity-transition", Key::LineOpacity, klass, value);
@@ -819,7 +798,14 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
parseOptionalProperty<Function<float>>("line-blur", Key::LineBlur, klass, value);
parseOptionalProperty<PropertyTransition>("line-blur-transition", Key::LineBlur, klass, value);
parseOptionalProperty<PiecewiseConstantFunction<Faded<std::vector<float>>>>("line-dasharray", Key::LineDashArray, klass, value, "line-dasharray-transition");
parseOptionalProperty<PiecewiseConstantFunction<Faded<std::string>>>("line-image", Key::LineImage, klass, value, "line-image-transition");
parseOptionalProperty<PiecewiseConstantFunction<Faded<std::string>>>("line-pattern", Key::LineImage, klass, value, "line-pattern-transition");
parseOptionalProperty<Function<float>>("circle-radius", Key::CircleRadius, klass, value);
parseOptionalProperty<Function<Color>>("circle-color", Key::CircleColor, klass, value);
parseOptionalProperty<Function<float>>("circle-opacity", Key::CircleOpacity, klass, value);
parseOptionalProperty<Function<std::array<float,2>>>("circle-translate", Key::CircleTranslate, klass, value);
parseOptionalProperty<Function<TranslateAnchorType>>("circle-translate-anchor", Key::CircleTranslateAnchor, klass, value);
parseOptionalProperty<Function<float>>("circle-blur", Key::CircleBlur, klass, value);
parseOptionalProperty<Function<float>>("icon-opacity", Key::IconOpacity, klass, value);
parseOptionalProperty<PropertyTransition>("icon-opacity-transition", Key::IconOpacity, klass, value);
@@ -869,7 +855,7 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
parseOptionalProperty<Function<float>>("background-opacity", Key::BackgroundOpacity, klass, value);
parseOptionalProperty<Function<Color>>("background-color", Key::BackgroundColor, klass, value);
parseOptionalProperty<PiecewiseConstantFunction<Faded<std::string>>>("background-image", Key::BackgroundImage, klass, value, "background-image-transition");
parseOptionalProperty<PiecewiseConstantFunction<Faded<std::string>>>("background-pattern", Key::BackgroundImage, klass, value, "background-pattern-transition");
}
void StyleParser::parseLayout(JSVal value, util::ptr<StyleBucket> &bucket) {
@@ -883,13 +869,13 @@ void StyleParser::parseLayout(JSVal value, util::ptr<StyleBucket> &bucket) {
parseOptionalProperty<Function<float>>("line-round-limit", Key::LineRoundLimit, bucket->layout, value);
parseOptionalProperty<Function<PlacementType>>("symbol-placement", Key::SymbolPlacement, bucket->layout, value);
parseOptionalProperty<Function<float>>("symbol-min-distance", Key::SymbolMinDistance, bucket->layout, value);
parseOptionalProperty<Function<float>>("symbol-spacing", Key::SymbolSpacing, bucket->layout, value);
parseOptionalProperty<Function<bool>>("symbol-avoid-edges", Key::SymbolAvoidEdges, bucket->layout, value);
parseOptionalProperty<Function<bool>>("icon-allow-overlap", Key::IconAllowOverlap, bucket->layout, value);
parseOptionalProperty<Function<bool>>("icon-ignore-placement", Key::IconIgnorePlacement, bucket->layout, value);
parseOptionalProperty<Function<bool>>("icon-optional", Key::IconOptional, bucket->layout, value);
parseOptionalProperty<Function<RotationAlignmentType>>("icon-rotation-alignment", Key::IconRotationAlignment, bucket->layout, value);
parseOptionalProperty<Function<float>>("icon-max-size", Key::IconMaxSize, bucket->layout, value);
parseOptionalProperty<Function<float>>("icon-size", Key::IconSize, bucket->layout, value);
parseOptionalProperty<Function<std::string>>("icon-image", Key::IconImage, bucket->layout, value);
parseOptionalProperty<Function<float>>("icon-rotate", Key::IconRotate, bucket->layout, value);
parseOptionalProperty<Function<float>>("icon-padding", Key::IconPadding, bucket->layout, value);
@@ -898,7 +884,7 @@ void StyleParser::parseLayout(JSVal value, util::ptr<StyleBucket> &bucket) {
parseOptionalProperty<Function<RotationAlignmentType>>("text-rotation-alignment", Key::TextRotationAlignment, bucket->layout, value);
parseOptionalProperty<Function<std::string>>("text-field", Key::TextField, bucket->layout, value);
parseOptionalProperty<Function<std::string>>("text-font", Key::TextFont, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-max-size", Key::TextMaxSize, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-size", Key::TextSize, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-max-width", Key::TextMaxWidth, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-line-height", Key::TextLineHeight, bucket->layout, value);
parseOptionalProperty<Function<float>>("text-letter-spacing", Key::TextLetterSpacing, bucket->layout, value);
@@ -948,7 +934,7 @@ void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) {
bucket->name = layer->id;
if (value.HasMember("source")) {
JSVal value_source = replaceConstant(value["source"]);
JSVal value_source = value["source"];
if (value_source.IsString()) {
bucket->source = { value_source.GetString(), value_source.GetStringLength() };
auto source_it = sourcesMap.find(bucket->source);
@@ -961,7 +947,7 @@ void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) {
}
if (value.HasMember("source-layer")) {
JSVal value_source_layer = replaceConstant(value["source-layer"]);
JSVal value_source_layer = value["source-layer"];
if (value_source_layer.IsString()) {
bucket->source_layer = { value_source_layer.GetString(), value_source_layer.GetStringLength() };
} else {
@@ -970,13 +956,11 @@ void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) {
}
if (value.HasMember("filter")) {
JSVal value_filter = replaceConstant(value["filter"]);
bucket->filter = parseFilterExpression(value_filter);
bucket->filter = parseFilterExpression(value["filter"]);
}
if (value.HasMember("layout")) {
JSVal value_render = replaceConstant(value["layout"]);
parseLayout(value_render, bucket);
parseLayout(value["layout"], bucket);
}
if (value.HasMember("minzoom")) {

View File

@@ -52,9 +52,6 @@ public:
}
private:
void parseConstants(JSVal value);
JSVal replaceConstant(JSVal value);
void parseSources(JSVal value);
void parseLayers(JSVal value);
void parseLayer(std::pair<JSVal, util::ptr<StyleLayer>> &pair);
@@ -101,7 +98,7 @@ private:
FilterExpression parseFilter(JSVal);
private:
std::unordered_map<std::string, const rapidjson::Value *> constants;
std::uint8_t version;
std::vector<std::unique_ptr<Source>> sources;
std::vector<util::ptr<StyleLayer>> layers;

View File

@@ -5,6 +5,7 @@ namespace mbgl {
template<> const FillProperties &defaultStyleProperties() { static const FillProperties p; return p; }
template<> const LineProperties &defaultStyleProperties() { static const LineProperties p; return p; }
template<> const CircleProperties &defaultStyleProperties() { static const CircleProperties p; return p; }
template<> const SymbolProperties &defaultStyleProperties() { static const SymbolProperties p; return p; }
template<> const RasterProperties &defaultStyleProperties() { static const RasterProperties p; return p; }
template<> const BackgroundProperties &defaultStyleProperties() { static const BackgroundProperties p; return p; }

View File

@@ -1,74 +1,110 @@
#include <mbgl/util/mapbox.hpp>
#include <stdexcept>
#include <vector>
#include <iostream>
namespace mbgl {
namespace util {
namespace mapbox {
const std::string mapbox = "mapbox://";
const std::string protocol = "mapbox://";
const std::string baseURL = "https://api.mapbox.com/";
std::string normalizeURL(const std::string& url, const std::string& pathPrefix, const std::string& accessToken) {
if (accessToken.empty())
throw std::runtime_error("You must provide a Mapbox API access token for Mapbox tile sources");
bool isMapboxURL(const std::string& url) {
return url.compare(0, protocol.length(), protocol) == 0;
}
return std::string("https://api.tiles.mapbox.com")
+ pathPrefix
+ url.substr(mapbox.length())
+ "?access_token="
+ accessToken;
std::vector<std::string> getMapboxURLPathname(const std::string& url) {
std::vector<std::string> pathname;
std::size_t startIndex = protocol.length();
while (startIndex < url.length()) {
std::size_t endIndex = url.find("/", startIndex);
if (endIndex == std::string::npos) {
endIndex = url.find_first_of("?#");
}
if (endIndex == std::string::npos) {
endIndex = url.length();
}
pathname.push_back(url.substr(startIndex, endIndex - startIndex));
startIndex = endIndex + 1;
}
return pathname;
}
std::string normalizeSourceURL(const std::string& url, const std::string& accessToken) {
if (url.compare(0, mapbox.length(), mapbox) != 0)
if (!isMapboxURL(url)) {
return url;
}
std::string result = normalizeURL(url + ".json", "/v4/", accessToken);
if (accessToken.empty()) {
throw std::runtime_error("You must provide a Mapbox API access token for Mapbox tile sources");
}
// TileJSON requests need a secure flag appended to their URLs so
// that the server knows to send SSL-ified resource references.
result += "&secure";
return result;
return baseURL + "v4/" + url.substr(protocol.length()) + ".json?access_token=" + accessToken + "&secure";
}
std::string normalizeStyleURL(const std::string& url, const std::string& accessToken) {
if (url.compare(0, mapbox.length(), mapbox) != 0)
if (!isMapboxURL(url)) {
return url;
}
const std::string user = url.substr(mapbox.length(), url.find('.') - mapbox.length());
std::vector<std::string> pathname = getMapboxURLPathname(url);
std::string user = pathname[1];
std::string id = pathname[2];
bool isDraft = pathname.size() > 3;
return baseURL + "styles/v1/" + user + "/" + id + (isDraft ? "/draft" : "") + "?access_token=" + accessToken;
}
return normalizeURL(url, "/styles/v1/" + user + "/", accessToken);
std::string normalizeSpriteURL(const std::string& url, const std::string& accessToken) {
if (!isMapboxURL(url)) {
return url;
}
std::vector<std::string> pathname = getMapboxURLPathname(url);
std::string user = pathname[1];
bool isDraft = pathname.size() > 3;
std::string id, extension;
if (isDraft) {
size_t index = pathname[3].find_first_of("@.");
id = pathname[2];
extension = pathname[3].substr(index);
} else {
size_t index = pathname[2].find_first_of("@.");
id = pathname[2].substr(0, index);
extension = pathname[2].substr(index);
}
return baseURL + "styles/v1/" + user + "/" + id + "/" + (isDraft ? "draft/" : "") + "sprite" + extension + "?access_token=" + accessToken;
}
std::string normalizeGlyphsURL(const std::string& url, const std::string& accessToken) {
if (url.compare(0, mapbox.length(), mapbox) != 0) {
if (!isMapboxURL(url)) {
return url;
}
const std::string fontstack = "mapbox://fontstack/";
std::vector<std::string> pathname = getMapboxURLPathname(url);
std::string user = pathname[1];
std::string fontstack = pathname[2];
std::string range = pathname[3];
if (url.compare(0, fontstack.length(), fontstack) == 0) {
return normalizeURL(url, "/v4/", accessToken);
}
return normalizeURL(url, "/", accessToken);
return baseURL + "fonts/v1/" + user + "/" + fontstack + "/" + range + "?access_token=" + accessToken;
}
std::string normalizeTileURL(const std::string& url, const std::string& sourceURL, SourceType sourceType) {
if (sourceURL.empty() || sourceURL.compare(0, mapbox.length(), mapbox) != 0 ||
sourceType != SourceType::Raster) {
if (sourceURL.empty() || !isMapboxURL(sourceURL) || sourceType != SourceType::Raster) {
return url;
}
std::string::size_type queryIdx = url.rfind("?");
// Trim off the right end but never touch anything before the extension dot.
std::string urlSansParams((queryIdx == std::string::npos) ? url : url.substr(0, queryIdx));
while (!urlSansParams.empty() && isdigit(urlSansParams.back())) {
urlSansParams.pop_back();
}
std::string::size_type basenameIdx = url.rfind("/", queryIdx);
std::string::size_type extensionIdx = url.rfind(".", queryIdx);
if (basenameIdx == std::string::npos || extensionIdx == std::string::npos ||
@@ -76,7 +112,7 @@ std::string normalizeTileURL(const std::string& url, const std::string& sourceUR
// No file extension: probably not a file name we can tack a ratio onto.
return url;
}
std::string normalizedURL(url);
normalizedURL.insert(extensionIdx, "{ratio}");
return normalizedURL;

View File

@@ -10,6 +10,7 @@ namespace mapbox {
std::string normalizeSourceURL(const std::string& url, const std::string& accessToken);
std::string normalizeStyleURL(const std::string& url, const std::string& accessToken);
std::string normalizeSpriteURL(const std::string& url, const std::string& accessToken);
std::string normalizeGlyphsURL(const std::string& url, const std::string& accessToken);
std::string normalizeTileURL(const std::string& url, const std::string& sourceURL, SourceType sourceType);

2
styles

Submodule styles updated: 44bdcec816...19dde3cb1c

View File

@@ -1,5 +1,5 @@
{
"version": 7,
"version": 8,
"name": "Water",
"sources": {
"mapbox": {

View File

@@ -1,5 +1,5 @@
{
"version": 7,
"version": 8,
"name": "POIs",
"sources": {
"mapbox": {

View File

@@ -20,7 +20,8 @@ public:
void deactivate() override {};
void notify() override {};
void invalidate() override {}
void swap() override {}
void beforeRender() override {}
void afterRender() override {}
};
}

View File

@@ -1,5 +1,5 @@
{
"version": 1,
"version": 8,
"name": "Test",
"sources": {
"vectorsource": {
@@ -20,7 +20,7 @@
"source": "vectorsource",
"source-layer": "road_label",
"layout": {
"text-font": "Open Sans Regular, Arial Unicode MS Regular",
"text-font": ["Open Sans Regular", "Arial Unicode MS Regular"],
"text-field": "{name_en}"
}
}, {
@@ -29,7 +29,7 @@
"source": "vectorsource",
"source-layer": "poi_label",
"layout": {
"text-font": "Open Sans Regular, Arial Unicode MS Regular",
"text-font": ["Open Sans Regular", "Arial Unicode MS Regular"],
"icon-image": "{maki}_icon"
}
}]

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "value of 'circle-blur' must be a number, or a number function"]
]
}
}

View File

@@ -0,0 +1,18 @@
{
"version": 8,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxzoom": 14
}
},
"layers": [{
"id": "circle_blur_example",
"type": "circle",
"source": "mapbox",
"paint": {
"circle-blur": null
}
}]
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "color value must be a string"]
]
}
}

View File

@@ -0,0 +1,18 @@
{
"version": 8,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxzoom": 14
}
},
"layers": [{
"id": "circle_color_example",
"type": "circle",
"source": "mapbox",
"paint": {
"circle-color": null
}
}]
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "value of 'circle-opacity' must be a number, or a number function"]
]
}
}

View File

@@ -0,0 +1,18 @@
{
"version": 8,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxzoom": 14
}
},
"layers": [{
"id": "circle_opacity_example",
"type": "circle",
"source": "mapbox",
"paint": {
"circle-opacity": null
}
}]
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "value of 'circle-radius' must be a number, or a number function"]
]
}
}

View File

@@ -0,0 +1,18 @@
{
"version": 8,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxzoom": 14
}
},
"layers": [{
"id": "circle_radius_example",
"type": "circle",
"source": "mapbox",
"paint": {
"circle-radius": null
}
}]
}

View File

@@ -1,5 +1,5 @@
{
"version": 6,
"version": 8,
"constants": {
"@land": "r44,239,225)",
"@snow": "f4f8foNGbjf#",

View File

@@ -1,5 +1,5 @@
{
"version": 6,
"version": 8,
"sources": {
"mapbox": {
"type": "vector",

View File

@@ -1,5 +1,5 @@
{
"version": 6,
"version": 8,
"sources": {
"mapbox": {
"type": "vector",
@@ -31,7 +31,7 @@
"source-layer": "waterway",
"layout": {
"text-font": {
"stops": [[0, "Open Sans Regular"]]
"stops": [[0, ["Open Sans Regular"]]]
},
"text-keep-upright": {
"stops": [[0, false], [10, true]]

View File

@@ -1,5 +1,5 @@
{
"version": 6,
"version": 8,
"sources": {
"mapbox": {
"type": "vector",

View File

@@ -1,5 +1,5 @@
{
"version": 6,
"version": 8,
"sources": {
"mapbox": {
"type": "vector",

View File

@@ -1,5 +1,5 @@
{
"version": 6,
"version": 8,
"sources": {
"mapbox": {
"type": "vector",

View File

@@ -1,5 +1,5 @@
{
"version": 6,
"version": 8,
"sources": {
"mapbox": {
"type": "vector",

View File

@@ -1,5 +1,5 @@
{
"version": 6,
"version": 8,
"sources": {
"mapbox": {
"type": "vector",

View File

@@ -1,5 +1,5 @@
{
"version": 6,
"version": 8,
"sources": {
"mapbox": {
"type": "vector",

View File

@@ -1,5 +1,5 @@
{
"version": 6,
"version": 8,
"sources": {
"mapbox": {
"type": "vector",

View File

@@ -8,22 +8,30 @@
using namespace mbgl;
TEST(Mapbox, SourceURL) {
EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "key"), "https://api.tiles.mapbox.com/v4/user.map.json?access_token=key&secure");
EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "token"), "https://api.tiles.mapbox.com/v4/user.map.json?access_token=token&secure");
EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "key"), "https://api.mapbox.com/v4/user.map.json?access_token=key&secure");
EXPECT_EQ(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", "token"), "https://api.mapbox.com/v4/user.map.json?access_token=token&secure");
EXPECT_THROW(mbgl::util::mapbox::normalizeSourceURL("mapbox://user.map", ""), std::runtime_error);
}
TEST(Mapbox, GlyphsURL) {
EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fontstack/{fontstack}/{range}.pbf", "key"), "https://api.tiles.mapbox.com/v4/fontstack/{fontstack}/{range}.pbf?access_token=key");
EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fonts/v1/user/{fontstack}/{range}.pbf", "key"), "https://api.tiles.mapbox.com/fonts/v1/user/{fontstack}/{range}.pbf?access_token=key");
EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fonts/boxmap/Comic%20Sans/0-255.pbf", "key"), "https://api.mapbox.com/fonts/v1/boxmap/Comic%20Sans/0-255.pbf?access_token=key");
EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("mapbox://fonts/boxmap/{fontstack}/{range}.pbf", "key"), "https://api.mapbox.com/fonts/v1/boxmap/{fontstack}/{range}.pbf?access_token=key");
EXPECT_EQ(mbgl::util::mapbox::normalizeGlyphsURL("http://path", "key"), "http://path");
}
TEST(Mapbox, StyleURL) {
EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("mapbox://user.style", "key"), "https://api.tiles.mapbox.com/styles/v1/user/user.style?access_token=key");
EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("mapbox://styles/user/style", "key"), "https://api.mapbox.com/styles/v1/user/style?access_token=key");
EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("mapbox://styles/user/style/draft", "key"), "https://api.mapbox.com/styles/v1/user/style/draft?access_token=key");
EXPECT_EQ(mbgl::util::mapbox::normalizeStyleURL("http://path", "key"), "http://path");
}
TEST(Mapbox, SpriteURL) {
EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("map/box/sprites@2x.json", "key"), "map/box/sprites@2x.json");
EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("mapbox://sprites/mapbox/streets-v8.json", "key"), "https://api.mapbox.com/styles/v1/mapbox/streets-v8/sprite.json?access_token=key");
EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("mapbox://sprites/mapbox/streets-v8@2x.png", "key"), "https://api.mapbox.com/styles/v1/mapbox/streets-v8/sprite@2x.png?access_token=key");
EXPECT_EQ(mbgl::util::mapbox::normalizeSpriteURL("mapbox://sprites/mapbox/streets-v8/draft@2x.png", "key"), "https://api.mapbox.com/styles/v1/mapbox/streets-v8/draft/sprite@2x.png?access_token=key");
}
TEST(Mapbox, TileURL) {
try {
EXPECT_EQ(mbgl::util::mapbox::normalizeTileURL("http://path.png/tile.png", "mapbox://user.map", SourceType::Raster), "http://path.png/tile{ratio}.png");