From 688540a44d7977208b606b02376edc7d87854db8 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Tue, 31 Oct 2017 13:10:01 +0200 Subject: Bump Mapbox GL Native mapbox-gl-native @ 0ef7b7154f6d4498077a83db5c486c61bc34938c --- deps/earcut/0.12.3/LICENSE | 15 - deps/earcut/0.12.3/include/mapbox/earcut.hpp | 773 --------------------- deps/earcut/0.12.4/LICENSE | 15 + deps/earcut/0.12.4/include/mapbox/earcut.hpp | 772 ++++++++++++++++++++ include/mbgl/map/mode.hpp | 9 - include/mbgl/renderer/mode.hpp | 18 + include/mbgl/renderer/renderer.hpp | 2 +- include/mbgl/style/conversion.hpp | 254 ++++++- include/mbgl/style/conversion/constant.hpp | 98 +-- include/mbgl/style/conversion/coordinate.hpp | 21 +- .../conversion/data_driven_property_value.hpp | 3 +- include/mbgl/style/conversion/filter.hpp | 243 +------ include/mbgl/style/conversion/function.hpp | 47 +- include/mbgl/style/conversion/geojson.hpp | 10 +- include/mbgl/style/conversion/geojson_options.hpp | 79 +-- include/mbgl/style/conversion/layer.hpp | 210 +----- include/mbgl/style/conversion/light.hpp | 106 +-- .../style/conversion/make_property_setters.hpp | 211 ------ include/mbgl/style/conversion/position.hpp | 14 +- include/mbgl/style/conversion/property_setter.hpp | 72 -- include/mbgl/style/conversion/property_value.hpp | 3 +- include/mbgl/style/conversion/source.hpp | 176 +---- include/mbgl/style/conversion/tileset.hpp | 65 +- .../mbgl/style/conversion/transition_options.hpp | 32 +- include/mbgl/util/geo.hpp | 7 + include/mbgl/util/projection.hpp | 4 +- include/mbgl/util/tileset.hpp | 1 + mapbox-gl-native.pro | 32 +- platform/default/mbgl/gl/headless_frontend.cpp | 18 +- platform/default/mbgl/gl/headless_frontend.hpp | 8 +- platform/default/mbgl/map/map_snapshotter.cpp | 118 +++- platform/default/mbgl/map/map_snapshotter.hpp | 23 +- platform/qt/mbgl/gl/gl_impl.hpp | 173 +++++ platform/qt/src/qmapboxgl.cpp | 10 + platform/qt/src/qt_conversion.hpp | 191 ++--- platform/qt/src/qt_geojson.cpp | 166 +++++ platform/qt/src/qt_geojson.hpp | 194 +----- src/mbgl/annotation/render_annotation_source.cpp | 4 +- src/mbgl/gl/context.cpp | 18 +- src/mbgl/gl/context.hpp | 3 +- src/mbgl/gl/gl.hpp | 30 +- src/mbgl/map/update.hpp | 26 - src/mbgl/renderer/paint_parameters.hpp | 1 + src/mbgl/renderer/render_item.hpp | 36 - src/mbgl/renderer/render_style.cpp | 449 ------------ src/mbgl/renderer/render_style.hpp | 92 --- src/mbgl/renderer/render_style_observer.hpp | 14 - src/mbgl/renderer/renderer_impl.cpp | 20 +- src/mbgl/renderer/renderer_impl.hpp | 2 +- src/mbgl/storage/file_source_request.cpp | 37 - src/mbgl/storage/file_source_request.hpp | 31 - src/mbgl/style/conversion/constant.cpp | 94 +++ src/mbgl/style/conversion/coordinate.cpp | 29 + src/mbgl/style/conversion/filter.cpp | 248 +++++++ src/mbgl/style/conversion/geojson.cpp | 18 +- src/mbgl/style/conversion/geojson_options.cpp | 85 +++ src/mbgl/style/conversion/json.hpp | 2 +- src/mbgl/style/conversion/layer.cpp | 206 ++++++ src/mbgl/style/conversion/light.cpp | 115 +++ .../style/conversion/make_property_setters.hpp | 209 ++++++ src/mbgl/style/conversion/position.cpp | 22 + src/mbgl/style/conversion/property_setter.hpp | 70 ++ src/mbgl/style/conversion/source.cpp | 175 +++++ src/mbgl/style/conversion/tileset.cpp | 73 ++ src/mbgl/style/conversion/transition_options.cpp | 40 ++ src/mbgl/style/parser.cpp | 6 +- src/mbgl/style/rapidjson_conversion.hpp | 152 ++-- src/mbgl/style/style_impl.cpp | 3 +- src/mbgl/tile/geojson_tile.cpp | 24 +- src/mbgl/tile/tile_id.hpp | 276 -------- src/mbgl/util/thread.hpp | 163 ----- src/mbgl/util/tile_cover.cpp | 4 +- 72 files changed, 3262 insertions(+), 3708 deletions(-) delete mode 100644 deps/earcut/0.12.3/LICENSE delete mode 100644 deps/earcut/0.12.3/include/mapbox/earcut.hpp create mode 100644 deps/earcut/0.12.4/LICENSE create mode 100644 deps/earcut/0.12.4/include/mapbox/earcut.hpp create mode 100644 include/mbgl/renderer/mode.hpp delete mode 100644 include/mbgl/style/conversion/make_property_setters.hpp delete mode 100644 include/mbgl/style/conversion/property_setter.hpp create mode 100644 platform/qt/mbgl/gl/gl_impl.hpp create mode 100644 platform/qt/src/qt_geojson.cpp delete mode 100644 src/mbgl/map/update.hpp delete mode 100644 src/mbgl/renderer/render_item.hpp delete mode 100644 src/mbgl/renderer/render_style.cpp delete mode 100644 src/mbgl/renderer/render_style.hpp delete mode 100644 src/mbgl/renderer/render_style_observer.hpp delete mode 100644 src/mbgl/storage/file_source_request.cpp delete mode 100644 src/mbgl/storage/file_source_request.hpp create mode 100644 src/mbgl/style/conversion/constant.cpp create mode 100644 src/mbgl/style/conversion/coordinate.cpp create mode 100644 src/mbgl/style/conversion/filter.cpp create mode 100644 src/mbgl/style/conversion/geojson_options.cpp create mode 100644 src/mbgl/style/conversion/layer.cpp create mode 100644 src/mbgl/style/conversion/light.cpp create mode 100644 src/mbgl/style/conversion/make_property_setters.hpp create mode 100644 src/mbgl/style/conversion/position.cpp create mode 100644 src/mbgl/style/conversion/property_setter.hpp create mode 100644 src/mbgl/style/conversion/source.cpp create mode 100644 src/mbgl/style/conversion/tileset.cpp create mode 100644 src/mbgl/style/conversion/transition_options.cpp delete mode 100644 src/mbgl/tile/tile_id.hpp delete mode 100644 src/mbgl/util/thread.hpp diff --git a/deps/earcut/0.12.3/LICENSE b/deps/earcut/0.12.3/LICENSE deleted file mode 100644 index 8bafb57730..0000000000 --- a/deps/earcut/0.12.3/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -ISC License - -Copyright (c) 2015, Mapbox - -Permission to use, copy, modify, and/or distribute this software for any purpose -with or without fee is hereby granted, provided that the above copyright notice -and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF -THIS SOFTWARE. diff --git a/deps/earcut/0.12.3/include/mapbox/earcut.hpp b/deps/earcut/0.12.3/include/mapbox/earcut.hpp deleted file mode 100644 index 1f14b9f723..0000000000 --- a/deps/earcut/0.12.3/include/mapbox/earcut.hpp +++ /dev/null @@ -1,773 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace mapbox { - -namespace util { - -template struct nth { - inline static typename std::tuple_element::type - get(const T& t) { return std::get(t); }; -}; - -} - -namespace detail { - -template -class Earcut { -public: - std::vector indices; - N vertices = 0; - - template - void operator()(const Polygon& points); - -private: - struct Node { - Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {} - Node(const Node&) = delete; - Node& operator=(const Node&) = delete; - Node(Node&&) = delete; - Node& operator=(Node&&) = delete; - - const N i; - const double x; - const double y; - - // previous and next vertice nodes in a polygon ring - Node* prev = nullptr; - Node* next = nullptr; - - // z-order curve value - int32_t z = 0; - - // previous and next nodes in z-order - Node* prevZ = nullptr; - Node* nextZ = nullptr; - - // indicates whether this is a steiner point - bool steiner = false; - }; - - template Node* linkedList(const Ring& points, const bool clockwise); - Node* filterPoints(Node* start, Node* end = nullptr); - void earcutLinked(Node* ear, int pass = 0); - bool isEar(Node* ear); - bool isEarHashed(Node* ear); - Node* cureLocalIntersections(Node* start); - void splitEarcut(Node* start); - template Node* eliminateHoles(const Polygon& points, Node* outerNode); - void eliminateHole(Node* hole, Node* outerNode); - Node* findHoleBridge(Node* hole, Node* outerNode); - void indexCurve(Node* start); - Node* sortLinked(Node* list); - int32_t zOrder(const double x_, const double y_); - Node* getLeftmost(Node* start); - bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const; - bool isValidDiagonal(Node* a, Node* b); - double area(const Node* p, const Node* q, const Node* r) const; - bool equals(const Node* p1, const Node* p2); - bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2); - bool intersectsPolygon(const Node* a, const Node* b); - bool locallyInside(const Node* a, const Node* b); - bool middleInside(const Node* a, const Node* b); - Node* splitPolygon(Node* a, Node* b); - template Node* insertNode(N i, const Point& p, Node* last); - void removeNode(Node* p); - - bool hashing; - double minX, maxX; - double minY, maxY; - double size; - - template > - class ObjectPool { - public: - ObjectPool() { } - ObjectPool(std::size_t blockSize_) { - reset(blockSize_); - } - ~ObjectPool() { - clear(); - } - template - T* construct(Args&&... args) { - if (currentIndex >= blockSize) { - currentBlock = alloc.allocate(blockSize); - allocations.emplace_back(currentBlock); - currentIndex = 0; - } - T* object = ¤tBlock[currentIndex++]; - alloc.construct(object, std::forward(args)...); - return object; - } - void reset(std::size_t newBlockSize) { - for (auto allocation : allocations) alloc.deallocate(allocation, blockSize); - allocations.clear(); - blockSize = std::max(1, newBlockSize); - currentBlock = nullptr; - currentIndex = blockSize; - } - void clear() { reset(blockSize); } - private: - T* currentBlock = nullptr; - std::size_t currentIndex = 1; - std::size_t blockSize = 1; - std::vector allocations; - Alloc alloc; - }; - ObjectPool nodes; -}; - -template template -void Earcut::operator()(const Polygon& points) { - // reset - indices.clear(); - vertices = 0; - - if (points.empty()) return; - - double x; - double y; - size = 0; - int threshold = 80; - std::size_t len = 0; - - for (size_t i = 0; threshold >= 0 && i < points.size(); i++) { - threshold -= static_cast(points[i].size()); - len += points[i].size(); - } - - //estimate size of nodes and indices - nodes.reset(len * 3 / 2); - indices.reserve(len + points[0].size()); - - Node* outerNode = linkedList(points[0], true); - if (!outerNode) return; - - if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); - - // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - hashing = threshold < 0; - if (hashing) { - Node* p = outerNode->next; - minX = maxX = p->x; - minY = maxY = p->y; - do { - x = p->x; - y = p->y; - minX = (std::min)(minX, x); - minY = (std::min)(minY, y); - maxX = (std::max)(maxX, x); - maxY = (std::max)(maxY, y); - p = p->next; - } while (p != outerNode); - - // minX, minY and size are later used to transform coords into integers for z-order calculation - size = (std::max)(maxX - minX, maxY - minY); - } - - earcutLinked(outerNode); - - nodes.clear(); -} - -// create a circular doubly linked list from polygon points in the specified winding order -template template -typename Earcut::Node* -Earcut::linkedList(const Ring& points, const bool clockwise) { - using Point = typename Ring::value_type; - double sum = 0; - const int len = static_cast(points.size()); - int i, j; - Point p1, p2; - Node* last = nullptr; - - // calculate original winding order of a polygon ring - for (i = 0, j = len - 1; i < len; j = i++) { - p1 = points[i]; - p2 = points[j]; - const double p20 = util::nth<0, Point>::get(p2); - const double p10 = util::nth<0, Point>::get(p1); - const double p11 = util::nth<1, Point>::get(p1); - const double p21 = util::nth<1, Point>::get(p2); - sum += (p20 - p10) * (p11 + p21); - } - - // link points into circular doubly-linked list in the specified winding order - if (clockwise == (sum > 0)) { - for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last); - } else { - for (i = len - 1; i >= 0; i--) last = insertNode(vertices + i, points[i], last); - } - - if (last && equals(last, last->next)) { - removeNode(last); - last = last->next; - } - - vertices += len; - - return last; -} - -// eliminate colinear or duplicate points -template -typename Earcut::Node* -Earcut::filterPoints(Node* start, Node* end) { - if (!end) end = start; - - Node* p = start; - bool again; - do { - again = false; - - if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) { - removeNode(p); - p = end = p->prev; - - if (p == p->next) return nullptr; - again = true; - - } else { - p = p->next; - } - } while (again || p != end); - - return end; -} - -// main ear slicing loop which triangulates a polygon (given as a linked list) -template -void Earcut::earcutLinked(Node* ear, int pass) { - if (!ear) return; - - // interlink polygon nodes in z-order - if (!pass && hashing) indexCurve(ear); - - Node* stop = ear; - Node* prev; - Node* next; - - int iterations = 0; - - // iterate through ears, slicing them one by one - while (ear->prev != ear->next) { - iterations++; - prev = ear->prev; - next = ear->next; - - if (hashing ? isEarHashed(ear) : isEar(ear)) { - // cut off the triangle - indices.emplace_back(prev->i); - indices.emplace_back(ear->i); - indices.emplace_back(next->i); - - removeNode(ear); - - // skipping the next vertice leads to less sliver triangles - ear = next->next; - stop = next->next; - - continue; - } - - ear = next; - - // if we looped through the whole remaining polygon and can't find any more ears - if (ear == stop) { - // try filtering points and slicing again - if (!pass) earcutLinked(filterPoints(ear), 1); - - // if this didn't work, try curing all small self-intersections locally - else if (pass == 1) { - ear = cureLocalIntersections(ear); - earcutLinked(ear, 2); - - // as a last resort, try splitting the remaining polygon into two - } else if (pass == 2) splitEarcut(ear); - - break; - } - } -} - -// check whether a polygon node forms a valid ear with adjacent nodes -template -bool Earcut::isEar(Node* ear) { - const Node* a = ear->prev; - const Node* b = ear; - const Node* c = ear->next; - - if (area(a, b, c) >= 0) return false; // reflex, can't be an ear - - // now make sure we don't have other points inside the potential ear - Node* p = ear->next->next; - - while (p != ear->prev) { - if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && - area(p->prev, p, p->next) >= 0) return false; - p = p->next; - } - - return true; -} - -template -bool Earcut::isEarHashed(Node* ear) { - const Node* a = ear->prev; - const Node* b = ear; - const Node* c = ear->next; - - if (area(a, b, c) >= 0) return false; // reflex, can't be an ear - - // triangle bbox; min & max are calculated like this for speed - const double minTX = (std::min)(a->x, (std::min)(b->x, c->x)); - const double minTY = (std::min)(a->y, (std::min)(b->y, c->y)); - const double maxTX = (std::max)(a->x, (std::max)(b->x, c->x)); - const double maxTY = (std::max)(a->y, (std::max)(b->y, c->y)); - - // z-order range for the current triangle bbox; - const int32_t minZ = zOrder(minTX, minTY); - const int32_t maxZ = zOrder(maxTX, maxTY); - - // first look for points inside the triangle in increasing z-order - Node* p = ear->nextZ; - - while (p && p->z <= maxZ) { - if (p != ear->prev && p != ear->next && - pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && - area(p->prev, p, p->next) >= 0) return false; - p = p->nextZ; - } - - // then look for points in decreasing z-order - p = ear->prevZ; - - while (p && p->z >= minZ) { - if (p != ear->prev && p != ear->next && - pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && - area(p->prev, p, p->next) >= 0) return false; - p = p->prevZ; - } - - return true; -} - -// go through all polygon nodes and cure small local self-intersections -template -typename Earcut::Node* -Earcut::cureLocalIntersections(Node* start) { - Node* p = start; - do { - Node* a = p->prev; - Node* b = p->next->next; - - // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) - if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) { - indices.emplace_back(a->i); - indices.emplace_back(p->i); - indices.emplace_back(b->i); - - // remove two nodes involved - removeNode(p); - removeNode(p->next); - - p = start = b; - } - p = p->next; - } while (p != start); - - return p; -} - -// try splitting polygon into two and triangulate them independently -template -void Earcut::splitEarcut(Node* start) { - // look for a valid diagonal that divides the polygon into two - Node* a = start; - do { - Node* b = a->next->next; - while (b != a->prev) { - if (a->i != b->i && isValidDiagonal(a, b)) { - // split the polygon in two by the diagonal - Node* c = splitPolygon(a, b); - - // filter colinear points around the cuts - a = filterPoints(a, a->next); - c = filterPoints(c, c->next); - - // run earcut on each half - earcutLinked(a); - earcutLinked(c); - return; - } - b = b->next; - } - a = a->next; - } while (a != start); -} - -// link every hole into the outer loop, producing a single-ring polygon without holes -template template -typename Earcut::Node* -Earcut::eliminateHoles(const Polygon& points, Node* outerNode) { - const size_t len = points.size(); - - std::vector queue; - for (size_t i = 1; i < len; i++) { - Node* list = linkedList(points[i], false); - if (list) { - if (list == list->next) list->steiner = true; - queue.push_back(getLeftmost(list)); - } - } - std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) { - return a->x < b->x; - }); - - // process holes from left to right - for (size_t i = 0; i < queue.size(); i++) { - eliminateHole(queue[i], outerNode); - outerNode = filterPoints(outerNode, outerNode->next); - } - - return outerNode; -} - -// find a bridge between vertices that connects hole with an outer ring and and link it -template -void Earcut::eliminateHole(Node* hole, Node* outerNode) { - outerNode = findHoleBridge(hole, outerNode); - if (outerNode) { - Node* b = splitPolygon(outerNode, hole); - filterPoints(b, b->next); - } -} - -// David Eberly's algorithm for finding a bridge between hole and outer polygon -template -typename Earcut::Node* -Earcut::findHoleBridge(Node* hole, Node* outerNode) { - Node* p = outerNode; - double hx = hole->x; - double hy = hole->y; - double qx = -std::numeric_limits::infinity(); - Node* m = nullptr; - - // find a segment intersected by a ray from the hole's leftmost Vertex to the left; - // segment's endpoint with lesser x will be potential connection Vertex - do { - if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) { - double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); - if (x <= hx && x > qx) { - qx = x; - if (x == hx) { - if (hy == p->y) return p; - if (hy == p->next->y) return p->next; - } - m = p->x < p->next->x ? p : p->next; - } - } - p = p->next; - } while (p != outerNode); - - if (!m) return 0; - - if (hx == qx) return m->prev; - - // look for points inside the triangle of hole Vertex, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex - - const Node* stop = m; - double tanMin = std::numeric_limits::infinity(); - double tanCur = 0; - - p = m->next; - double mx = m->x; - double my = m->y; - - while (p != stop) { - if (hx >= p->x && p->x >= mx && hx != p->x && - pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) { - - tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential - - if ((tanCur < tanMin || (tanCur == tanMin && p->x > m->x)) && locallyInside(p, hole)) { - m = p; - tanMin = tanCur; - } - } - - p = p->next; - } - - return m; -} - -// interlink polygon nodes in z-order -template -void Earcut::indexCurve(Node* start) { - assert(start); - Node* p = start; - - do { - p->z = p->z ? p->z : zOrder(p->x, p->y); - p->prevZ = p->prev; - p->nextZ = p->next; - p = p->next; - } while (p != start); - - p->prevZ->nextZ = nullptr; - p->prevZ = nullptr; - - sortLinked(p); -} - -// Simon Tatham's linked list merge sort algorithm -// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html -template -typename Earcut::Node* -Earcut::sortLinked(Node* list) { - assert(list); - Node* p; - Node* q; - Node* e; - Node* tail; - int i, numMerges, pSize, qSize; - int inSize = 1; - - while (true) { - p = list; - list = nullptr; - tail = nullptr; - numMerges = 0; - - while (p) { - numMerges++; - q = p; - pSize = 0; - for (i = 0; i < inSize; i++) { - pSize++; - q = q->nextZ; - if (!q) break; - } - - qSize = inSize; - - while (pSize > 0 || (qSize > 0 && q)) { - - if (pSize == 0) { - e = q; - q = q->nextZ; - qSize--; - } else if (qSize == 0 || !q) { - e = p; - p = p->nextZ; - pSize--; - } else if (p->z <= q->z) { - e = p; - p = p->nextZ; - pSize--; - } else { - e = q; - q = q->nextZ; - qSize--; - } - - if (tail) tail->nextZ = e; - else list = e; - - e->prevZ = tail; - tail = e; - } - - p = q; - } - - tail->nextZ = nullptr; - - if (numMerges <= 1) return list; - - inSize *= 2; - } -} - -// z-order of a Vertex given coords and size of the data bounding box -template -int32_t Earcut::zOrder(const double x_, const double y_) { - // coords are transformed into non-negative 15-bit integer range - int32_t x = static_cast(32767.0 * (x_ - minX) / size); - int32_t y = static_cast(32767.0 * (y_ - minY) / size); - - x = (x | (x << 8)) & 0x00FF00FF; - x = (x | (x << 4)) & 0x0F0F0F0F; - x = (x | (x << 2)) & 0x33333333; - x = (x | (x << 1)) & 0x55555555; - - y = (y | (y << 8)) & 0x00FF00FF; - y = (y | (y << 4)) & 0x0F0F0F0F; - y = (y | (y << 2)) & 0x33333333; - y = (y | (y << 1)) & 0x55555555; - - return x | (y << 1); -} - -// find the leftmost node of a polygon ring -template -typename Earcut::Node* -Earcut::getLeftmost(Node* start) { - Node* p = start; - Node* leftmost = start; - do { - if (p->x < leftmost->x) leftmost = p; - p = p->next; - } while (p != start); - - return leftmost; -} - -// check if a point lies within a convex triangle -template -bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { - return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && - (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && - (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; -} - -// check if a diagonal between two polygon nodes is valid (lies in polygon interior) -template -bool Earcut::isValidDiagonal(Node* a, Node* b) { - return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && - locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b); -} - -// signed area of a triangle -template -double Earcut::area(const Node* p, const Node* q, const Node* r) const { - return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y); -} - -// check if two points are equal -template -bool Earcut::equals(const Node* p1, const Node* p2) { - return p1->x == p2->x && p1->y == p2->y; -} - -// check if two segments intersect -template -bool Earcut::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) { - if ((equals(p1, q1) && equals(p2, q2)) || - (equals(p1, q2) && equals(p2, q1))) return true; - return (area(p1, q1, p2) > 0) != (area(p1, q1, q2) > 0) && - (area(p2, q2, p1) > 0) != (area(p2, q2, q1) > 0); -} - -// check if a polygon diagonal intersects any polygon segments -template -bool Earcut::intersectsPolygon(const Node* a, const Node* b) { - const Node* p = a; - do { - if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && - intersects(p, p->next, a, b)) return true; - p = p->next; - } while (p != a); - - return false; -} - -// check if a polygon diagonal is locally inside the polygon -template -bool Earcut::locallyInside(const Node* a, const Node* b) { - return area(a->prev, a, a->next) < 0 ? - area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 : - area(a, b, a->prev) < 0 || area(a, a->next, b) < 0; -} - -// check if the middle Vertex of a polygon diagonal is inside the polygon -template -bool Earcut::middleInside(const Node* a, const Node* b) { - const Node* p = a; - bool inside = false; - double px = (a->x + b->x) / 2; - double py = (a->y + b->y) / 2; - do { - if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y && - (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x)) - inside = !inside; - p = p->next; - } while (p != a); - - return inside; -} - -// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits -// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a -// single ring -template -typename Earcut::Node* -Earcut::splitPolygon(Node* a, Node* b) { - Node* a2 = nodes.construct(a->i, a->x, a->y); - Node* b2 = nodes.construct(b->i, b->x, b->y); - Node* an = a->next; - Node* bp = b->prev; - - a->next = b; - b->prev = a; - - a2->next = an; - an->prev = a2; - - b2->next = a2; - a2->prev = b2; - - bp->next = b2; - b2->prev = bp; - - return b2; -} - -// create a node and util::optionally link it with previous one (in a circular doubly linked list) -template template -typename Earcut::Node* -Earcut::insertNode(N i, const Point& pt, Node* last) { - Node* p = nodes.construct(i, util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt)); - - if (!last) { - p->prev = p; - p->next = p; - - } else { - assert(last); - p->next = last->next; - p->prev = last; - last->next->prev = p; - last->next = p; - } - return p; -} - -template -void Earcut::removeNode(Node* p) { - p->next->prev = p->prev; - p->prev->next = p->next; - - if (p->prevZ) p->prevZ->nextZ = p->nextZ; - if (p->nextZ) p->nextZ->prevZ = p->prevZ; -} -} - -template -std::vector earcut(const Polygon& poly) { - mapbox::detail::Earcut earcut; - earcut(poly); - return earcut.indices; -} -} diff --git a/deps/earcut/0.12.4/LICENSE b/deps/earcut/0.12.4/LICENSE new file mode 100644 index 0000000000..8bafb57730 --- /dev/null +++ b/deps/earcut/0.12.4/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2015, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/deps/earcut/0.12.4/include/mapbox/earcut.hpp b/deps/earcut/0.12.4/include/mapbox/earcut.hpp new file mode 100644 index 0000000000..a916c1d254 --- /dev/null +++ b/deps/earcut/0.12.4/include/mapbox/earcut.hpp @@ -0,0 +1,772 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mapbox { + +namespace util { + +template struct nth { + inline static typename std::tuple_element::type + get(const T& t) { return std::get(t); }; +}; + +} + +namespace detail { + +template +class Earcut { +public: + std::vector indices; + std::size_t vertices = 0; + + template + void operator()(const Polygon& points); + +private: + struct Node { + Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {} + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + Node(Node&&) = delete; + Node& operator=(Node&&) = delete; + + const N i; + const double x; + const double y; + + // previous and next vertice nodes in a polygon ring + Node* prev = nullptr; + Node* next = nullptr; + + // z-order curve value + int32_t z = 0; + + // previous and next nodes in z-order + Node* prevZ = nullptr; + Node* nextZ = nullptr; + + // indicates whether this is a steiner point + bool steiner = false; + }; + + template Node* linkedList(const Ring& points, const bool clockwise); + Node* filterPoints(Node* start, Node* end = nullptr); + void earcutLinked(Node* ear, int pass = 0); + bool isEar(Node* ear); + bool isEarHashed(Node* ear); + Node* cureLocalIntersections(Node* start); + void splitEarcut(Node* start); + template Node* eliminateHoles(const Polygon& points, Node* outerNode); + void eliminateHole(Node* hole, Node* outerNode); + Node* findHoleBridge(Node* hole, Node* outerNode); + void indexCurve(Node* start); + Node* sortLinked(Node* list); + int32_t zOrder(const double x_, const double y_); + Node* getLeftmost(Node* start); + bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const; + bool isValidDiagonal(Node* a, Node* b); + double area(const Node* p, const Node* q, const Node* r) const; + bool equals(const Node* p1, const Node* p2); + bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2); + bool intersectsPolygon(const Node* a, const Node* b); + bool locallyInside(const Node* a, const Node* b); + bool middleInside(const Node* a, const Node* b); + Node* splitPolygon(Node* a, Node* b); + template Node* insertNode(std::size_t i, const Point& p, Node* last); + void removeNode(Node* p); + + bool hashing; + double minX, maxX; + double minY, maxY; + double inv_size = 0; + + template > + class ObjectPool { + public: + ObjectPool() { } + ObjectPool(std::size_t blockSize_) { + reset(blockSize_); + } + ~ObjectPool() { + clear(); + } + template + T* construct(Args&&... args) { + if (currentIndex >= blockSize) { + currentBlock = alloc.allocate(blockSize); + allocations.emplace_back(currentBlock); + currentIndex = 0; + } + T* object = ¤tBlock[currentIndex++]; + alloc.construct(object, std::forward(args)...); + return object; + } + void reset(std::size_t newBlockSize) { + for (auto allocation : allocations) alloc.deallocate(allocation, blockSize); + allocations.clear(); + blockSize = std::max(1, newBlockSize); + currentBlock = nullptr; + currentIndex = blockSize; + } + void clear() { reset(blockSize); } + private: + T* currentBlock = nullptr; + std::size_t currentIndex = 1; + std::size_t blockSize = 1; + std::vector allocations; + Alloc alloc; + }; + ObjectPool nodes; +}; + +template template +void Earcut::operator()(const Polygon& points) { + // reset + indices.clear(); + vertices = 0; + + if (points.empty()) return; + + double x; + double y; + int threshold = 80; + std::size_t len = 0; + + for (size_t i = 0; threshold >= 0 && i < points.size(); i++) { + threshold -= static_cast(points[i].size()); + len += points[i].size(); + } + + //estimate size of nodes and indices + nodes.reset(len * 3 / 2); + indices.reserve(len + points[0].size()); + + Node* outerNode = linkedList(points[0], true); + if (!outerNode) return; + + if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); + + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + hashing = threshold < 0; + if (hashing) { + Node* p = outerNode->next; + minX = maxX = p->x; + minY = maxY = p->y; + do { + x = p->x; + y = p->y; + minX = std::min(minX, x); + minY = std::min(minY, y); + maxX = std::max(maxX, x); + maxY = std::max(maxY, y); + p = p->next; + } while (p != outerNode); + + // minX, minY and size are later used to transform coords into integers for z-order calculation + inv_size = std::max(maxX - minX, maxY - minY); + inv_size = inv_size != .0 ? (1. / inv_size) : .0; + } + + earcutLinked(outerNode); + + nodes.clear(); +} + +// create a circular doubly linked list from polygon points in the specified winding order +template template +typename Earcut::Node* +Earcut::linkedList(const Ring& points, const bool clockwise) { + using Point = typename Ring::value_type; + double sum = 0; + const std::size_t len = points.size(); + std::size_t i, j; + Node* last = nullptr; + + // calculate original winding order of a polygon ring + for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) { + const auto& p1 = points[i]; + const auto& p2 = points[j]; + const double p20 = util::nth<0, Point>::get(p2); + const double p10 = util::nth<0, Point>::get(p1); + const double p11 = util::nth<1, Point>::get(p1); + const double p21 = util::nth<1, Point>::get(p2); + sum += (p20 - p10) * (p11 + p21); + } + + // link points into circular doubly-linked list in the specified winding order + if (clockwise == (sum > 0)) { + for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last); + } else { + for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last); + } + + if (last && equals(last, last->next)) { + removeNode(last); + last = last->next; + } + + vertices += len; + + return last; +} + +// eliminate colinear or duplicate points +template +typename Earcut::Node* +Earcut::filterPoints(Node* start, Node* end) { + if (!end) end = start; + + Node* p = start; + bool again; + do { + again = false; + + if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) { + removeNode(p); + p = end = p->prev; + + if (p == p->next) break; + again = true; + + } else { + p = p->next; + } + } while (again || p != end); + + return end; +} + +// main ear slicing loop which triangulates a polygon (given as a linked list) +template +void Earcut::earcutLinked(Node* ear, int pass) { + if (!ear) return; + + // interlink polygon nodes in z-order + if (!pass && hashing) indexCurve(ear); + + Node* stop = ear; + Node* prev; + Node* next; + + int iterations = 0; + + // iterate through ears, slicing them one by one + while (ear->prev != ear->next) { + iterations++; + prev = ear->prev; + next = ear->next; + + if (hashing ? isEarHashed(ear) : isEar(ear)) { + // cut off the triangle + indices.emplace_back(prev->i); + indices.emplace_back(ear->i); + indices.emplace_back(next->i); + + removeNode(ear); + + // skipping the next vertice leads to less sliver triangles + ear = next->next; + stop = next->next; + + continue; + } + + ear = next; + + // if we looped through the whole remaining polygon and can't find any more ears + if (ear == stop) { + // try filtering points and slicing again + if (!pass) earcutLinked(filterPoints(ear), 1); + + // if this didn't work, try curing all small self-intersections locally + else if (pass == 1) { + ear = cureLocalIntersections(ear); + earcutLinked(ear, 2); + + // as a last resort, try splitting the remaining polygon into two + } else if (pass == 2) splitEarcut(ear); + + break; + } + } +} + +// check whether a polygon node forms a valid ear with adjacent nodes +template +bool Earcut::isEar(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // now make sure we don't have other points inside the potential ear + Node* p = ear->next->next; + + while (p != ear->prev) { + if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->next; + } + + return true; +} + +template +bool Earcut::isEarHashed(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // triangle bbox; min & max are calculated like this for speed + const double minTX = std::min(a->x, std::min(b->x, c->x)); + const double minTY = std::min(a->y, std::min(b->y, c->y)); + const double maxTX = std::max(a->x, std::max(b->x, c->x)); + const double maxTY = std::max(a->y, std::max(b->y, c->y)); + + // z-order range for the current triangle bbox; + const int32_t minZ = zOrder(minTX, minTY); + const int32_t maxZ = zOrder(maxTX, maxTY); + + // first look for points inside the triangle in increasing z-order + Node* p = ear->nextZ; + + while (p && p->z <= maxZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->nextZ; + } + + // then look for points in decreasing z-order + p = ear->prevZ; + + while (p && p->z >= minZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->prevZ; + } + + return true; +} + +// go through all polygon nodes and cure small local self-intersections +template +typename Earcut::Node* +Earcut::cureLocalIntersections(Node* start) { + Node* p = start; + do { + Node* a = p->prev; + Node* b = p->next->next; + + // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) + if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) { + indices.emplace_back(a->i); + indices.emplace_back(p->i); + indices.emplace_back(b->i); + + // remove two nodes involved + removeNode(p); + removeNode(p->next); + + p = start = b; + } + p = p->next; + } while (p != start); + + return p; +} + +// try splitting polygon into two and triangulate them independently +template +void Earcut::splitEarcut(Node* start) { + // look for a valid diagonal that divides the polygon into two + Node* a = start; + do { + Node* b = a->next->next; + while (b != a->prev) { + if (a->i != b->i && isValidDiagonal(a, b)) { + // split the polygon in two by the diagonal + Node* c = splitPolygon(a, b); + + // filter colinear points around the cuts + a = filterPoints(a, a->next); + c = filterPoints(c, c->next); + + // run earcut on each half + earcutLinked(a); + earcutLinked(c); + return; + } + b = b->next; + } + a = a->next; + } while (a != start); +} + +// link every hole into the outer loop, producing a single-ring polygon without holes +template template +typename Earcut::Node* +Earcut::eliminateHoles(const Polygon& points, Node* outerNode) { + const size_t len = points.size(); + + std::vector queue; + for (size_t i = 1; i < len; i++) { + Node* list = linkedList(points[i], false); + if (list) { + if (list == list->next) list->steiner = true; + queue.push_back(getLeftmost(list)); + } + } + std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) { + return a->x < b->x; + }); + + // process holes from left to right + for (size_t i = 0; i < queue.size(); i++) { + eliminateHole(queue[i], outerNode); + outerNode = filterPoints(outerNode, outerNode->next); + } + + return outerNode; +} + +// find a bridge between vertices that connects hole with an outer ring and and link it +template +void Earcut::eliminateHole(Node* hole, Node* outerNode) { + outerNode = findHoleBridge(hole, outerNode); + if (outerNode) { + Node* b = splitPolygon(outerNode, hole); + filterPoints(b, b->next); + } +} + +// David Eberly's algorithm for finding a bridge between hole and outer polygon +template +typename Earcut::Node* +Earcut::findHoleBridge(Node* hole, Node* outerNode) { + Node* p = outerNode; + double hx = hole->x; + double hy = hole->y; + double qx = -std::numeric_limits::infinity(); + Node* m = nullptr; + + // find a segment intersected by a ray from the hole's leftmost Vertex to the left; + // segment's endpoint with lesser x will be potential connection Vertex + do { + if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) { + double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); + if (x <= hx && x > qx) { + qx = x; + if (x == hx) { + if (hy == p->y) return p; + if (hy == p->next->y) return p->next; + } + m = p->x < p->next->x ? p : p->next; + } + } + p = p->next; + } while (p != outerNode); + + if (!m) return 0; + + if (hx == qx) return m->prev; + + // look for points inside the triangle of hole Vertex, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex + + const Node* stop = m; + double tanMin = std::numeric_limits::infinity(); + double tanCur = 0; + + p = m->next; + double mx = m->x; + double my = m->y; + + while (p != stop) { + if (hx >= p->x && p->x >= mx && hx != p->x && + pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) { + + tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential + + if ((tanCur < tanMin || (tanCur == tanMin && p->x > m->x)) && locallyInside(p, hole)) { + m = p; + tanMin = tanCur; + } + } + + p = p->next; + } + + return m; +} + +// interlink polygon nodes in z-order +template +void Earcut::indexCurve(Node* start) { + assert(start); + Node* p = start; + + do { + p->z = p->z ? p->z : zOrder(p->x, p->y); + p->prevZ = p->prev; + p->nextZ = p->next; + p = p->next; + } while (p != start); + + p->prevZ->nextZ = nullptr; + p->prevZ = nullptr; + + sortLinked(p); +} + +// Simon Tatham's linked list merge sort algorithm +// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html +template +typename Earcut::Node* +Earcut::sortLinked(Node* list) { + assert(list); + Node* p; + Node* q; + Node* e; + Node* tail; + int i, numMerges, pSize, qSize; + int inSize = 1; + + for (;;) { + p = list; + list = nullptr; + tail = nullptr; + numMerges = 0; + + while (p) { + numMerges++; + q = p; + pSize = 0; + for (i = 0; i < inSize; i++) { + pSize++; + q = q->nextZ; + if (!q) break; + } + + qSize = inSize; + + while (pSize > 0 || (qSize > 0 && q)) { + + if (pSize == 0) { + e = q; + q = q->nextZ; + qSize--; + } else if (qSize == 0 || !q) { + e = p; + p = p->nextZ; + pSize--; + } else if (p->z <= q->z) { + e = p; + p = p->nextZ; + pSize--; + } else { + e = q; + q = q->nextZ; + qSize--; + } + + if (tail) tail->nextZ = e; + else list = e; + + e->prevZ = tail; + tail = e; + } + + p = q; + } + + tail->nextZ = nullptr; + + if (numMerges <= 1) return list; + + inSize *= 2; + } +} + +// z-order of a Vertex given coords and size of the data bounding box +template +int32_t Earcut::zOrder(const double x_, const double y_) { + // coords are transformed into non-negative 15-bit integer range + int32_t x = static_cast(32767.0 * (x_ - minX) * inv_size); + int32_t y = static_cast(32767.0 * (y_ - minY) * inv_size); + + x = (x | (x << 8)) & 0x00FF00FF; + x = (x | (x << 4)) & 0x0F0F0F0F; + x = (x | (x << 2)) & 0x33333333; + x = (x | (x << 1)) & 0x55555555; + + y = (y | (y << 8)) & 0x00FF00FF; + y = (y | (y << 4)) & 0x0F0F0F0F; + y = (y | (y << 2)) & 0x33333333; + y = (y | (y << 1)) & 0x55555555; + + return x | (y << 1); +} + +// find the leftmost node of a polygon ring +template +typename Earcut::Node* +Earcut::getLeftmost(Node* start) { + Node* p = start; + Node* leftmost = start; + do { + if (p->x < leftmost->x) leftmost = p; + p = p->next; + } while (p != start); + + return leftmost; +} + +// check if a point lies within a convex triangle +template +bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { + return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && + (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && + (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; +} + +// check if a diagonal between two polygon nodes is valid (lies in polygon interior) +template +bool Earcut::isValidDiagonal(Node* a, Node* b) { + return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && + locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b); +} + +// signed area of a triangle +template +double Earcut::area(const Node* p, const Node* q, const Node* r) const { + return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y); +} + +// check if two points are equal +template +bool Earcut::equals(const Node* p1, const Node* p2) { + return p1->x == p2->x && p1->y == p2->y; +} + +// check if two segments intersect +template +bool Earcut::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) { + if ((equals(p1, q1) && equals(p2, q2)) || + (equals(p1, q2) && equals(p2, q1))) return true; + return (area(p1, q1, p2) > 0) != (area(p1, q1, q2) > 0) && + (area(p2, q2, p1) > 0) != (area(p2, q2, q1) > 0); +} + +// check if a polygon diagonal intersects any polygon segments +template +bool Earcut::intersectsPolygon(const Node* a, const Node* b) { + const Node* p = a; + do { + if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && + intersects(p, p->next, a, b)) return true; + p = p->next; + } while (p != a); + + return false; +} + +// check if a polygon diagonal is locally inside the polygon +template +bool Earcut::locallyInside(const Node* a, const Node* b) { + return area(a->prev, a, a->next) < 0 ? + area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 : + area(a, b, a->prev) < 0 || area(a, a->next, b) < 0; +} + +// check if the middle Vertex of a polygon diagonal is inside the polygon +template +bool Earcut::middleInside(const Node* a, const Node* b) { + const Node* p = a; + bool inside = false; + double px = (a->x + b->x) / 2; + double py = (a->y + b->y) / 2; + do { + if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y && + (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x)) + inside = !inside; + p = p->next; + } while (p != a); + + return inside; +} + +// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits +// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a +// single ring +template +typename Earcut::Node* +Earcut::splitPolygon(Node* a, Node* b) { + Node* a2 = nodes.construct(a->i, a->x, a->y); + Node* b2 = nodes.construct(b->i, b->x, b->y); + Node* an = a->next; + Node* bp = b->prev; + + a->next = b; + b->prev = a; + + a2->next = an; + an->prev = a2; + + b2->next = a2; + a2->prev = b2; + + bp->next = b2; + b2->prev = bp; + + return b2; +} + +// create a node and util::optionally link it with previous one (in a circular doubly linked list) +template template +typename Earcut::Node* +Earcut::insertNode(std::size_t i, const Point& pt, Node* last) { + Node* p = nodes.construct(static_cast(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt)); + + if (!last) { + p->prev = p; + p->next = p; + + } else { + assert(last); + p->next = last->next; + p->prev = last; + last->next->prev = p; + last->next = p; + } + return p; +} + +template +void Earcut::removeNode(Node* p) { + p->next->prev = p->prev; + p->prev->next = p->next; + + if (p->prevZ) p->prevZ->nextZ = p->nextZ; + if (p->nextZ) p->nextZ->prevZ = p->prevZ; +} +} + +template +std::vector earcut(const Polygon& poly) { + mapbox::detail::Earcut earcut; + earcut(poly); + return std::move(earcut.indices); +} +} diff --git a/include/mbgl/map/mode.hpp b/include/mbgl/map/mode.hpp index 05de2df22c..256d152e43 100644 --- a/include/mbgl/map/mode.hpp +++ b/include/mbgl/map/mode.hpp @@ -14,15 +14,6 @@ enum class MapMode : EnumType { Still, // a once-off still image }; -// We can avoid redundant GL calls when it is known that the GL context is not -// being shared. In a shared GL context case, we need to make sure that the -// correct GL configurations are in use - they might have changed between render -// calls. -enum class GLContextMode : EnumType { - Unique, - Shared, -}; - // We can choose to constrain the map both horizontally or vertically, or only // vertically e.g. while panning. enum class ConstrainMode : EnumType { diff --git a/include/mbgl/renderer/mode.hpp b/include/mbgl/renderer/mode.hpp new file mode 100644 index 0000000000..6ff42d8058 --- /dev/null +++ b/include/mbgl/renderer/mode.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace mbgl { + +using EnumType = uint32_t; + +// We can avoid redundant GL calls when it is known that the GL context is not +// being shared. In a shared GL context case, we need to make sure that the +// correct GL configurations are in use - they might have changed between render +// calls. +enum class GLContextMode : EnumType { + Unique, + Shared, +}; + +} // namespace mbgl diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp index be8abb2c29..21d411afd0 100644 --- a/include/mbgl/renderer/renderer.hpp +++ b/include/mbgl/renderer/renderer.hpp @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include #include diff --git a/include/mbgl/style/conversion.hpp b/include/mbgl/style/conversion.hpp index 27504a89b1..0b7e0b2b2f 100644 --- a/include/mbgl/style/conversion.hpp +++ b/include/mbgl/style/conversion.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include @@ -9,9 +11,8 @@ namespace style { namespace conversion { /* - The `conversion` namespace defines conversions from a templated type `V` representing a JSON - object conforming to the schema defined by the Mapbox Style Specification, to the various C++ - types that form the C++ model of that domain: + The `conversion` namespace defines conversions from JSON structures conforming to the schema defined by + the Mapbox Style Specification, to the various C++ types that form the C++ model of that domain: * `std::unique_ptr` * `std::unique_ptr` @@ -20,15 +21,31 @@ namespace conversion { A single template function serves as the public interface: - template - optional convert(const V& value, Error& error); + template + optional convert(const Convertible& input, Error& error); Where `T` is one of the above types. If the conversion fails, the result is empty, and the error parameter includes diagnostic text suitable for presentation to a library user. Otherwise, a filled optional is returned. - The implementation of `convert` requires that the following are legal expressions for a value `v` - of type `const V&`: + `Convertible` is a type that encapsulates a special form of polymorphism over various underlying types that + can serve as input to the conversion algorithm. For instance, on macOS, we need to support + conversion from both RapidJSON types, and a JSON structure represented with `NSArray`/`NSDictionary`/etc. + On Qt, we need to support conversion from RapidJSON types and QVariant. + + We don't want to use traditional forms of polymorphism to accomplish this: + + * Compile time polymorphism using a template parameter for the actual value type leads to + excessive code bloat and long compile times. + * Runtime polymorphism using virtual methods requires extra heap allocation and ubiquitous + use of std::unique_ptr, unsuitable for this performance-sensitive code. + + Therefore, we're using a custom implementation of runtime polymorphism where we manually create and + dispatch through a table of function pointers (vtable), while keeping the storage for any of the possible + underlying types inline on the stack, using `std::aligned_storage`. + + For a given underlying type T, an explicit specialization of `ConversionTraits` must be provided. This + specialization must provide the following static methods: * `isUndefined(v)` -- returns a boolean indication whether `v` is undefined or a JSON null @@ -48,21 +65,234 @@ namespace conversion { * `toNumber(v)` -- returns `optional`, absence indicating `v` is not a JSON number * `toDouble(v)` -- returns `optional`, absence indicating `v` is not a JSON number * `toString(v)` -- returns `optional`, absence indicating `v` is not a JSON string - * `toValue(v)` -- returns `optional`, a variant type, for generic conversion, + * `toValue(v)` -- returns `optional`, a variant type, for generic conversion, absence indicating `v` is not a boolean, number, or string. Numbers should be converted to unsigned integer, signed integer, or floating point, in descending preference. - The mbgl core implements these requirements for RapidJSON types, and the node bindings implement - them for v8 types. + In addition, the type T must be move-constructable. And finally, `Convertible::Storage`, a typedef for + `std::aligned_storage_t`, must be large enough to satisfy the memory requirements for any of the + possible underlying types. (A static assert will fail if this is not the case.) + + `Convertible` itself is movable, but not copyable. A moved-from `Convertible` is in an invalid state; + you must not do anything with it except let it go out of scope. */ struct Error { std::string message; }; +template +class ConversionTraits; + +class Convertible { +public: + template + Convertible(T&& value) : vtable(vtableForType>()) { + static_assert(sizeof(Storage) >= sizeof(std::decay_t), "Storage must be large enough to hold value type"); + new (static_cast(&storage)) std::decay_t(std::forward(value)); + } + + Convertible(Convertible&& v) + : vtable(v.vtable) + { + if (vtable) { + vtable->move(std::move(v.storage), this->storage); + } + } + + ~Convertible() { + if (vtable) { + vtable->destroy(storage); + } + } + + Convertible& operator=(Convertible&& v) { + if (vtable) { + vtable->destroy(storage); + } + vtable = v.vtable; + if (vtable) { + vtable->move(std::move(v.storage), this->storage); + } + v.vtable = nullptr; + return *this; + } + + Convertible() = delete; + Convertible(const Convertible&) = delete; + Convertible& operator=(const Convertible&) = delete; + + friend inline bool isUndefined(const Convertible& v) { + assert(v.vtable); + return v.vtable->isUndefined(v.storage); + } + + friend inline bool isArray(const Convertible& v) { + assert(v.vtable); + return v.vtable->isArray(v.storage); + } + + friend inline std::size_t arrayLength(const Convertible& v) { + assert(v.vtable); + return v.vtable->arrayLength(v.storage); + } + + friend inline Convertible arrayMember(const Convertible& v, std::size_t i) { + assert(v.vtable); + return v.vtable->arrayMember(v.storage, i); + } + + friend inline bool isObject(const Convertible& v) { + assert(v.vtable); + return v.vtable->isObject(v.storage); + } + + friend inline optional objectMember(const Convertible& v, const char * name) { + assert(v.vtable); + return v.vtable->objectMember(v.storage, name); + } + + friend inline optional eachMember(const Convertible& v, const std::function (const std::string&, const Convertible&)>& fn) { + assert(v.vtable); + return v.vtable->eachMember(v.storage, fn); + } + + friend inline optional toBool(const Convertible& v) { + assert(v.vtable); + return v.vtable->toBool(v.storage); + } + + friend inline optional toNumber(const Convertible& v) { + assert(v.vtable); + return v.vtable->toNumber(v.storage); + } + + friend inline optional toDouble(const Convertible& v) { + assert(v.vtable); + return v.vtable->toDouble(v.storage); + } + + friend inline optional toString(const Convertible& v) { + assert(v.vtable); + return v.vtable->toString(v.storage); + } + + friend inline optional toValue(const Convertible& v) { + assert(v.vtable); + return v.vtable->toValue(v.storage); + } + + friend inline optional toGeoJSON(const Convertible& v, Error& error) { + assert(v.vtable); + return v.vtable->toGeoJSON(v.storage, error); + } + +private: +#if __ANDROID__ + // Android: JSValue* or mbgl::android::Value + using Storage = std::aligned_storage_t<32, 8>; +#elif __QT__ + // Qt: JSValue* or QVariant + using Storage = std::aligned_storage_t<32, 8>; +#else + // Node: JSValue* or v8::Local + // iOS/macOS: JSValue* or id + using Storage = std::aligned_storage_t<8, 8>; +#endif + + struct VTable { + void (*move) (Storage&& src, Storage& dest); + void (*destroy) (Storage&); + + bool (*isUndefined) (const Storage&); + + bool (*isArray) (const Storage&); + std::size_t (*arrayLength) (const Storage&); + Convertible (*arrayMember) (const Storage&, std::size_t); + + bool (*isObject) (const Storage&); + optional (*objectMember) (const Storage&, const char *); + optional (*eachMember) (const Storage&, const std::function (const std::string&, const Convertible&)>&); + + optional (*toBool) (const Storage&); + optional (*toNumber) (const Storage&); + optional (*toDouble) (const Storage&); + optional (*toString) (const Storage&); + optional (*toValue) (const Storage&); + + // https://github.com/mapbox/mapbox-gl-native/issues/5623 + optional (*toGeoJSON) (const Storage&, Error&); + }; + + template + static VTable* vtableForType() { + using Traits = ConversionTraits; + static VTable vtable = { + [] (Storage&& src, Storage& dest) { + auto srcValue = reinterpret_cast(src); + new (static_cast(&dest)) T(std::move(srcValue)); + srcValue.~T(); + }, + [] (Storage& s) { + reinterpret_cast(s).~T(); + }, + [] (const Storage& s) { + return Traits::isUndefined(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::isArray(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::arrayLength(reinterpret_cast(s)); + }, + [] (const Storage& s, std::size_t i) { + return Convertible(Traits::arrayMember(reinterpret_cast(s), i)); + }, + [] (const Storage& s) { + return Traits::isObject(reinterpret_cast(s)); + }, + [] (const Storage& s, const char * key) { + optional member = Traits::objectMember(reinterpret_cast(s), key); + if (member) { + return optional(Convertible(std::move(*member))); + } else { + return optional(); + } + }, + [] (const Storage& s, const std::function (const std::string&, const Convertible&)>& fn) { + return Traits::eachMember(reinterpret_cast(s), [&](const std::string& k, T&& v) { + return fn(k, Convertible(std::move(v))); + }); + }, + [] (const Storage& s) { + return Traits::toBool(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::toNumber(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::toDouble(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::toString(reinterpret_cast(s)); + }, + [] (const Storage& s) { + return Traits::toValue(reinterpret_cast(s)); + }, + [] (const Storage& s, Error& err) { + return Traits::toGeoJSON(reinterpret_cast(s), err); + } + }; + return &vtable; + } + + VTable* vtable; + Storage storage; +}; + template struct Converter; -template -optional convert(const V& value, Error& error, Args&&...args) { +template +optional convert(const Convertible& value, Error& error, Args&&...args) { return Converter()(value, error, std::forward(args)...); } diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp index 07c0a35fae..7b3249da52 100644 --- a/include/mbgl/style/conversion/constant.hpp +++ b/include/mbgl/style/conversion/constant.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -16,47 +15,22 @@ namespace conversion { template <> struct Converter { - template - optional operator()(const V& value, Error& error) const { - optional converted = toBool(value); - if (!converted) { - error = { "value must be a boolean" }; - return {}; - } - return *converted; - } + optional operator()(const Convertible& value, Error& error) const; }; template <> struct Converter { - template - optional operator()(const V& value, Error& error) const { - optional converted = toNumber(value); - if (!converted) { - error = { "value must be a number" }; - return {}; - } - return *converted; - } + optional operator()(const Convertible& value, Error& error) const; }; template <> struct Converter { - template - optional operator()(const V& value, Error& error) const { - optional converted = toString(value); - if (!converted) { - error = { "value must be a string" }; - return {}; - } - return *converted; - } + optional operator()(const Convertible& value, Error& error) const; }; template struct Converter::value>> { - template - optional operator()(const V& value, Error& error) const { + optional operator()(const Convertible& value, Error& error) const { optional string = toString(value); if (!string) { error = { "value must be a string" }; @@ -75,28 +49,12 @@ struct Converter::value>> { template <> struct Converter { - template - optional operator()(const V& value, Error& error) const { - optional string = toString(value); - if (!string) { - error = { "value must be a string" }; - return {}; - } - - optional color = Color::parse(*string); - if (!color) { - error = { "value must be a valid color" }; - return {}; - } - - return *color; - } + optional operator()(const Convertible& value, Error& error) const; }; template struct Converter> { - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { if (!isArray(value) || arrayLength(value) != N) { error = { "value must be an array of " + util::toString(N) + " numbers" }; return {}; @@ -117,52 +75,12 @@ struct Converter> { template <> struct Converter> { - template - optional> operator()(const V& value, Error& error) const { - if (!isArray(value)) { - error = { "value must be an array" }; - return {}; - } - - std::vector result; - result.reserve(arrayLength(value)); - - for (std::size_t i = 0; i < arrayLength(value); ++i) { - optional number = toNumber(arrayMember(value, i)); - if (!number) { - error = { "value must be an array of numbers" }; - return {}; - } - result.push_back(*number); - } - - return result; - } + optional> operator()(const Convertible& value, Error& error) const; }; template <> struct Converter> { - template - optional> operator()(const V& value, Error& error) const { - if (!isArray(value)) { - error = { "value must be an array" }; - return {}; - } - - std::vector result; - result.reserve(arrayLength(value)); - - for (std::size_t i = 0; i < arrayLength(value); ++i) { - optional string = toString(arrayMember(value, i)); - if (!string) { - error = { "value must be an array of strings" }; - return {}; - } - result.push_back(*string); - } - - return result; - } + optional> operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/coordinate.hpp b/include/mbgl/style/conversion/coordinate.hpp index 732624e77f..e11db5e32f 100644 --- a/include/mbgl/style/conversion/coordinate.hpp +++ b/include/mbgl/style/conversion/coordinate.hpp @@ -10,26 +10,7 @@ namespace conversion { template<> struct Converter { public: - template - optional operator() (const V& value, Error& error) const { - if (!isArray(value) || arrayLength(value) < 2 ) { - error = { "coordinate array must contain numeric longitude and latitude values" }; - return {}; - } - //Style spec uses GeoJSON convention for specifying coordinates - optional latitude = toDouble(arrayMember(value, 1)); - optional longitude = toDouble(arrayMember(value, 0)); - - if (!latitude || !longitude) { - error = { "coordinate array must contain numeric longitude and latitude values" }; - return {}; - } - if (*latitude < -90 || *latitude > 90 ){ - error = { "coordinate latitude must be between -90 and 90" }; - return {}; - } - return LatLng(*latitude, *longitude); - } + optional operator() (const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/data_driven_property_value.hpp b/include/mbgl/style/conversion/data_driven_property_value.hpp index 79b15dcfb0..1e54c15a49 100644 --- a/include/mbgl/style/conversion/data_driven_property_value.hpp +++ b/include/mbgl/style/conversion/data_driven_property_value.hpp @@ -11,8 +11,7 @@ namespace conversion { template struct Converter> { - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { if (isUndefined(value)) { return DataDrivenPropertyValue(); } else if (!isObject(value)) { diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp index 986d1bf80d..9daf6ea7a4 100644 --- a/include/mbgl/style/conversion/filter.hpp +++ b/include/mbgl/style/conversion/filter.hpp @@ -2,7 +2,6 @@ #include #include -#include namespace mbgl { namespace style { @@ -11,247 +10,7 @@ namespace conversion { template <> struct Converter { public: - template - optional operator()(const V& value, Error& error) const { - if (!isArray(value)) { - error = { "filter expression must be an array" }; - return {}; - } - - if (arrayLength(value) < 1) { - error = { "filter expression must have at least 1 element" }; - return {}; - } - - optional op = toString(arrayMember(value, 0)); - if (!op) { - error = { "filter operator must be a string" }; - return {}; - } - - if (*op == "==") { - return convertEqualityFilter(value, error); - } else if (*op == "!=") { - return convertEqualityFilter(value, error); - } else if (*op == ">") { - return convertBinaryFilter(value, error); - } else if (*op == ">=") { - return convertBinaryFilter(value, error); - } else if (*op == "<") { - return convertBinaryFilter(value, error); - } else if (*op == "<=") { - return convertBinaryFilter(value, error); - } else if (*op == "in") { - return convertSetFilter(value, error); - } else if (*op == "!in") { - return convertSetFilter(value, error); - } else if (*op == "all") { - return convertCompoundFilter(value, error); - } else if (*op == "any") { - return convertCompoundFilter(value, error); - } else if (*op == "none") { - return convertCompoundFilter(value, error); - } else if (*op == "has") { - return convertUnaryFilter(value, error); - } else if (*op == "!has") { - return convertUnaryFilter(value, error); - } - - error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" }; - return {}; - } - -private: - optional normalizeValue(const optional& value, Error& error) const { - if (!value) { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - } else { - return *value; - } - } - - template - optional toFeatureType(const V& value, Error& error) const { - optional type = toString(value); - if (!type) { - error = { "value for $type filter must be a string" }; - return {}; - } else if (*type == "Point") { - return FeatureType::Point; - } else if (*type == "LineString") { - return FeatureType::LineString; - } else if (*type == "Polygon") { - return FeatureType::Polygon; - } else { - error = { "value for $type filter must be Point, LineString, or Polygon" }; - return {}; - } - } - - template - optional toFeatureIdentifier(const V& value, Error& error) const { - optional identifier = toValue(value); - if (!identifier) { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - } else { - return (*identifier).match( - [] (uint64_t t) -> optional { return { t }; }, - [] ( int64_t t) -> optional { return { t }; }, - [] ( double t) -> optional { return { t }; }, - [] (const std::string& t) -> optional { return { t }; }, - [&] (const auto&) -> optional { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - }); - } - } - - template - optional convertUnaryFilter(const V& value, Error& error) const { - if (arrayLength(value) < 2) { - error = { "filter expression must have 2 elements" }; - return {}; - } - - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$id") { - return { IdentifierFilterType {} }; - } else { - return { FilterType { *key } }; - } - } - - template - optional convertEqualityFilter(const V& value, Error& error) const { - if (arrayLength(value) < 3) { - error = { "filter expression must have 3 elements" }; - return {}; - } - - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$type") { - optional filterValue = toFeatureType(arrayMember(value, 2), error); - if (!filterValue) { - return {}; - } - - return { TypeFilterType { *filterValue } }; - - } else if (*key == "$id") { - optional filterValue = toFeatureIdentifier(arrayMember(value, 2), error); - if (!filterValue) { - return {}; - } - - return { IdentifierFilterType { *filterValue } }; - - } else { - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); - if (!filterValue) { - return {}; - } - - return { FilterType { *key, *filterValue } }; - } - } - - template - optional convertBinaryFilter(const V& value, Error& error) const { - if (arrayLength(value) < 3) { - error = { "filter expression must have 3 elements" }; - return {}; - } - - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); - if (!filterValue) { - return {}; - } - - return { FilterType { *key, *filterValue } }; - } - - template - optional convertSetFilter(const V& value, Error& error) const { - if (arrayLength(value) < 2) { - error = { "filter expression must at least 2 elements" }; - return {}; - } - - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$type") { - std::vector values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = toFeatureType(arrayMember(value, i), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { TypeFilterType { std::move(values) } }; - - } else if (*key == "$id") { - std::vector values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = toFeatureIdentifier(arrayMember(value, i), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { IdentifierFilterType { std::move(values) } }; - - } else { - std::vector values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = normalizeValue(toValue(arrayMember(value, i)), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { FilterType { *key, std::move(values) } }; - } - } - - template - optional convertCompoundFilter(const V& value, Error& error) const { - std::vector filters; - for (std::size_t i = 1; i < arrayLength(value); ++i) { - optional element = operator()(arrayMember(value, i), error); - if (!element) { - return {}; - } - filters.push_back(*element); - } - - return { FilterType { std::move(filters) } }; - } + optional operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp index 752b6dd045..e230884944 100644 --- a/include/mbgl/style/conversion/function.hpp +++ b/include/mbgl/style/conversion/function.hpp @@ -11,8 +11,8 @@ namespace mbgl { namespace style { namespace conversion { -template -optional> convertStops(const V& value, Error& error) { +template +optional> convertStops(const Convertible& value, Error& error) { auto stopsValue = objectMember(value, "stops"); if (!stopsValue) { error = { "function value must specify stops" }; @@ -63,8 +63,7 @@ template struct Converter> { static constexpr const char * type = "exponential"; - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { auto stops = convertStops(value, error); if (!stops) { return {}; @@ -89,8 +88,7 @@ template struct Converter> { static constexpr const char * type = "interval"; - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { auto stops = convertStops(value, error); if (!stops) { return {}; @@ -101,8 +99,7 @@ struct Converter> { template <> struct Converter { - template - optional operator()(const V& value, Error& error) const { + optional operator()(const Convertible& value, Error& error) const { auto b = toBool(value); if (b) { return { *b }; @@ -127,8 +124,7 @@ template struct Converter> { static constexpr const char * type = "categorical"; - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { auto stops = convertStops(value, error); if (!stops) { return {}; @@ -142,8 +138,7 @@ template struct Converter> { static constexpr const char * type = "identity"; - template - optional> operator()(const V&, Error&) const { + optional> operator()(const Convertible&, Error&) const { return IdentityStops(); } }; @@ -154,8 +149,7 @@ struct StopsConverter; template struct StopsConverter> { public: - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { std::string type = util::Interpolatable::value ? "exponential" : "interval"; auto typeValue = objectMember(value, "type"); @@ -193,8 +187,7 @@ public: template struct Converter> { - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { if (!isObject(value)) { error = { "function must be an object" }; return {}; @@ -209,8 +202,8 @@ struct Converter> { } }; -template -optional> convertDefaultValue(const V& value, Error& error) { +template +optional> convertDefaultValue(const Convertible& value, Error& error) { auto defaultValueValue = objectMember(value, "default"); if (!defaultValueValue) { return optional(); @@ -227,8 +220,7 @@ optional> convertDefaultValue(const V& value, Error& error) { template struct Converter> { - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { if (!isObject(value)) { error = { "function must be an object" }; return {}; @@ -267,8 +259,7 @@ struct CompositeValue : std::pair { template struct Converter> { - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { if (!isObject(value)) { error = { "stop must be an object" }; return {}; @@ -304,8 +295,7 @@ template struct Converter> { static constexpr const char * type = "exponential"; - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { auto stops = convertStops, T>(value, error); if (!stops) { return {}; @@ -330,8 +320,7 @@ template struct Converter> { static constexpr const char * type = "interval"; - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { auto stops = convertStops, T>(value, error); if (!stops) { return {}; @@ -350,8 +339,7 @@ template struct Converter> { static constexpr const char * type = "categorical"; - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { auto stops = convertStops, T>(value, error); if (!stops) { return {}; @@ -368,8 +356,7 @@ struct Converter> { template struct Converter> { - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { if (!isObject(value)) { error = { "function must be an object" }; return {}; diff --git a/include/mbgl/style/conversion/geojson.hpp b/include/mbgl/style/conversion/geojson.hpp index 0b594f066c..403c5f953b 100644 --- a/include/mbgl/style/conversion/geojson.hpp +++ b/include/mbgl/style/conversion/geojson.hpp @@ -7,15 +7,13 @@ namespace mbgl { namespace style { namespace conversion { +// Workaround until https://github.com/mapbox/mapbox-gl-native/issues/5623 is done. +optional parseGeoJSON(const std::string&, Error&); + template <> struct Converter { public: - optional operator()(const std::string&, Error&) const; - - // This is explicitly specialized in the .cpp file for JSValue. It may also be explicitly - // specialized for SDK-specific types (e.g. mbgl::android::Value). - template - optional operator()(const V&, Error&) const; + optional operator()(const Convertible&, Error&) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp index 1c9c18250c..3f625babb6 100644 --- a/include/mbgl/style/conversion/geojson_options.hpp +++ b/include/mbgl/style/conversion/geojson_options.hpp @@ -9,84 +9,7 @@ namespace conversion { template <> struct Converter { - - template - optional operator()(const V& value, Error& error) const { - GeoJSONOptions options; - - const auto minzoomValue = objectMember(value, "minzoom"); - if (minzoomValue) { - if (toNumber(*minzoomValue)) { - options.minzoom = static_cast(*toNumber(*minzoomValue)); - } else { - error = { "GeoJSON source minzoom value must be a number" }; - return {}; - } - } - - const auto maxzoomValue = objectMember(value, "maxzoom"); - if (maxzoomValue) { - if (toNumber(*maxzoomValue)) { - options.maxzoom = static_cast(*toNumber(*maxzoomValue)); - } else { - error = { "GeoJSON source maxzoom value must be a number" }; - return {}; - } - } - - const auto bufferValue = objectMember(value, "buffer"); - if (bufferValue) { - if (toNumber(*bufferValue)) { - options.buffer = static_cast(*toNumber(*bufferValue)); - } else { - error = { "GeoJSON source buffer value must be a number" }; - return {}; - } - } - - const auto toleranceValue = objectMember(value, "tolerance"); - if (toleranceValue) { - if (toNumber(*toleranceValue)) { - options.tolerance = static_cast(*toNumber(*toleranceValue)); - } else { - error = { "GeoJSON source tolerance value must be a number" }; - return {}; - } - } - - const auto clusterValue = objectMember(value, "cluster"); - if (clusterValue) { - if (toBool(*clusterValue)) { - options.cluster = *toBool(*clusterValue); - } else { - error = { "GeoJSON source cluster value must be a boolean" }; - return {}; - } - } - - const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom"); - if (clusterMaxZoomValue) { - if (toNumber(*clusterMaxZoomValue)) { - options.clusterMaxZoom = static_cast(*toNumber(*clusterMaxZoomValue)); - } else { - error = { "GeoJSON source clusterMaxZoom value must be a number" }; - return {}; - } - } - - const auto clusterRadiusValue = objectMember(value, "clusterRadius"); - if (clusterRadiusValue) { - if (toNumber(*clusterRadiusValue)) { - options.clusterRadius = static_cast(*toNumber(*clusterRadiusValue)); - } else { - error = { "GeoJSON source clusterRadius value must be a number" }; - return {}; - } - } - - return { options }; - } - + optional operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp index 1fe467165d..1c0e2e2f07 100644 --- a/include/mbgl/style/conversion/layer.hpp +++ b/include/mbgl/style/conversion/layer.hpp @@ -1,220 +1,24 @@ #pragma once #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include + +#include namespace mbgl { namespace style { namespace conversion { -template -optional setLayoutProperty(Layer& layer, const std::string& name, const V& value) { - static const auto setters = makeLayoutPropertySetters(); - auto it = setters.find(name); - if (it == setters.end()) { - return Error { "property not found" }; - } - return it->second(layer, value); -} - -template -optional setPaintProperty(Layer& layer, const std::string& name, const V& value) { - static const auto setters = makePaintPropertySetters(); - auto it = setters.find(name); - if (it == setters.end()) { - return Error { "property not found" }; - } - return it->second(layer, value); -} - -template -optional setPaintProperties(Layer& layer, const V& value) { - auto paintValue = objectMember(value, "paint"); - if (!paintValue) { - return {}; - } - return eachMember(*paintValue, [&] (const std::string& k, const V& v) { - return setPaintProperty(layer, k, v); - }); -} - template <> struct Converter> { public: - template - optional> operator()(const V& value, Error& error) const { - if (!isObject(value)) { - error = { "layer must be an object" }; - return {}; - } - - auto idValue = objectMember(value, "id"); - if (!idValue) { - error = { "layer must have an id" }; - return {}; - } - - optional id = toString(*idValue); - if (!id) { - error = { "layer id must be a string" }; - return {}; - } - - auto typeValue = objectMember(value, "type"); - if (!typeValue) { - error = { "layer must have a type" }; - return {}; - } - - optional type = toString(*typeValue); - if (!type) { - error = { "layer type must be a string" }; - return {}; - } - - optional> converted; - - if (*type == "fill") { - converted = convertVectorLayer(*id, value, error); - } else if (*type == "fill-extrusion") { - converted = convertVectorLayer(*id, value, error); - } else if (*type == "line") { - converted = convertVectorLayer(*id, value, error); - } else if (*type == "circle") { - converted = convertVectorLayer(*id, value, error); - } else if (*type == "symbol") { - converted = convertVectorLayer(*id, value, error); - } else if (*type == "raster") { - converted = convertRasterLayer(*id, value, error); - } else if (*type == "background") { - converted = convertBackgroundLayer(*id, value, error); - } else { - error = { "invalid layer type" }; - return {}; - } - - if (!converted) { - return converted; - } - - std::unique_ptr layer = std::move(*converted); - - auto minzoomValue = objectMember(value, "minzoom"); - if (minzoomValue) { - optional minzoom = toNumber(*minzoomValue); - if (!minzoom) { - error = { "minzoom must be numeric" }; - return {}; - } - layer->setMinZoom(*minzoom); - } - - auto maxzoomValue = objectMember(value, "maxzoom"); - if (maxzoomValue) { - optional maxzoom = toNumber(*maxzoomValue); - if (!maxzoom) { - error = { "maxzoom must be numeric" }; - return {}; - } - layer->setMaxZoom(*maxzoom); - } - - auto layoutValue = objectMember(value, "layout"); - if (layoutValue) { - if (!isObject(*layoutValue)) { - error = { "layout must be an object" }; - return {}; - } - optional error_ = eachMember(*layoutValue, [&] (const std::string& k, const V& v) { - return setLayoutProperty(*layer, k, v); - }); - if (error_) { - error = *error_; - return {}; - } - } - - optional error_ = setPaintProperties(*layer, value); - if (error_) { - error = *error_; - return {}; - } - - return std::move(layer); - } - -private: - template - optional> convertVectorLayer(const std::string& id, const V& value, Error& error) const { - auto sourceValue = objectMember(value, "source"); - if (!sourceValue) { - error = { "layer must have a source" }; - return {}; - } - - optional source = toString(*sourceValue); - if (!source) { - error = { "layer source must be a string" }; - return {}; - } - - std::unique_ptr layer = std::make_unique(id, *source); - - auto sourceLayerValue = objectMember(value, "source-layer"); - if (sourceLayerValue) { - optional sourceLayer = toString(*sourceLayerValue); - if (!sourceLayer) { - error = { "layer source-layer must be a string" }; - return {}; - } - layer->setSourceLayer(*sourceLayer); - } - - auto filterValue = objectMember(value, "filter"); - if (filterValue) { - optional filter = convert(*filterValue, error); - if (!filter) { - return {}; - } - layer->setFilter(*filter); - } - - return { std::move(layer) }; - } - - template - optional> convertRasterLayer(const std::string& id, const V& value, Error& error) const { - auto sourceValue = objectMember(value, "source"); - if (!sourceValue) { - error = { "layer must have a source" }; - return {}; - } - - optional source = toString(*sourceValue); - if (!source) { - error = { "layer source must be a string" }; - return {}; - } - - return { std::make_unique(id, *source) }; - } - - template - optional> convertBackgroundLayer(const std::string& id, const V&, Error&) const { - return { std::make_unique(id) }; - } + optional> operator()(const Convertible& value, Error& error) const; }; +optional setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value); +optional setPaintProperty(Layer& layer, const std::string& name, const Convertible& value); +optional setPaintProperties(Layer& layer, const Convertible& value); + } // namespace conversion } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/conversion/light.hpp b/include/mbgl/style/conversion/light.hpp index ba162516c0..289fca2e31 100644 --- a/include/mbgl/style/conversion/light.hpp +++ b/include/mbgl/style/conversion/light.hpp @@ -2,9 +2,6 @@ #include #include -#include -#include -#include namespace mbgl { namespace style { @@ -13,108 +10,7 @@ namespace conversion { template <> struct Converter { public: - template - optional operator()(const V& value, Error& error) const { - if (!isObject(value)) { - error = { "light must be an object" }; - return {}; - } - - Light light; - - const auto anchor = objectMember(value, "anchor"); - if (anchor) { - optional> convertedAnchor = - convert>(*anchor, error); - - if (convertedAnchor) { - light.setAnchor(*convertedAnchor); - } else { - return {}; - } - } - - const auto anchorTransition = objectMember(value, "anchor-transition"); - if (anchorTransition) { - optional transition = - convert(*anchorTransition, error); - if (transition) { - light.setAnchorTransition(*transition); - } else { - return {}; - } - } - - const auto color = objectMember(value, "color"); - if (color) { - optional> convertedColor = - convert>(*color, error); - - if (convertedColor) { - light.setColor(*convertedColor); - } else { - return {}; - } - } - - const auto colorTransition = objectMember(value, "color-transition"); - if (colorTransition) { - optional transition = - convert(*colorTransition, error); - if (transition) { - light.setColorTransition(*transition); - } else { - return {}; - } - } - - const auto position = objectMember(value, "position"); - if (position) { - optional> convertedPosition = - convert>(*position, error); - - if (convertedPosition) { - light.setPosition(*convertedPosition); - } else { - return {}; - } - } - - const auto positionTransition = objectMember(value, "position-transition"); - if (positionTransition) { - optional transition = - convert(*positionTransition, error); - if (transition) { - light.setPositionTransition(*transition); - } else { - return {}; - } - } - - const auto intensity = objectMember(value, "intensity"); - if (intensity) { - optional> convertedIntensity = - convert>(*intensity, error); - - if (convertedIntensity) { - light.setIntensity(*convertedIntensity); - } else { - return {}; - } - } - - const auto intensityTransition = objectMember(value, "intensity-transition"); - if (intensityTransition) { - optional transition = - convert(*intensityTransition, error); - if (transition) { - light.setIntensityTransition(*transition); - } else { - return {}; - } - } - return { std::move(light) }; - }; + optional operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp deleted file mode 100644 index 59b0e7be32..0000000000 --- a/include/mbgl/style/conversion/make_property_setters.hpp +++ /dev/null @@ -1,211 +0,0 @@ -#pragma once - -// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`. - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace mbgl { -namespace style { -namespace conversion { - -template -auto makeLayoutPropertySetters() { - std::unordered_map> result; - - result["visibility"] = &setVisibility; - - - result["line-cap"] = &setProperty, &LineLayer::setLineCap>; - result["line-join"] = &setProperty, &LineLayer::setLineJoin>; - result["line-miter-limit"] = &setProperty, &LineLayer::setLineMiterLimit>; - result["line-round-limit"] = &setProperty, &LineLayer::setLineRoundLimit>; - - result["symbol-placement"] = &setProperty, &SymbolLayer::setSymbolPlacement>; - result["symbol-spacing"] = &setProperty, &SymbolLayer::setSymbolSpacing>; - result["symbol-avoid-edges"] = &setProperty, &SymbolLayer::setSymbolAvoidEdges>; - result["icon-allow-overlap"] = &setProperty, &SymbolLayer::setIconAllowOverlap>; - result["icon-ignore-placement"] = &setProperty, &SymbolLayer::setIconIgnorePlacement>; - result["icon-optional"] = &setProperty, &SymbolLayer::setIconOptional>; - result["icon-rotation-alignment"] = &setProperty, &SymbolLayer::setIconRotationAlignment>; - result["icon-size"] = &setProperty, &SymbolLayer::setIconSize>; - result["icon-text-fit"] = &setProperty, &SymbolLayer::setIconTextFit>; - result["icon-text-fit-padding"] = &setProperty>, &SymbolLayer::setIconTextFitPadding>; - result["icon-image"] = &setProperty, &SymbolLayer::setIconImage>; - result["icon-rotate"] = &setProperty, &SymbolLayer::setIconRotate>; - result["icon-padding"] = &setProperty, &SymbolLayer::setIconPadding>; - result["icon-keep-upright"] = &setProperty, &SymbolLayer::setIconKeepUpright>; - result["icon-offset"] = &setProperty>, &SymbolLayer::setIconOffset>; - result["icon-anchor"] = &setProperty, &SymbolLayer::setIconAnchor>; - result["icon-pitch-alignment"] = &setProperty, &SymbolLayer::setIconPitchAlignment>; - result["text-pitch-alignment"] = &setProperty, &SymbolLayer::setTextPitchAlignment>; - result["text-rotation-alignment"] = &setProperty, &SymbolLayer::setTextRotationAlignment>; - result["text-field"] = &setProperty, &SymbolLayer::setTextField>; - result["text-font"] = &setProperty>, &SymbolLayer::setTextFont>; - result["text-size"] = &setProperty, &SymbolLayer::setTextSize>; - result["text-max-width"] = &setProperty, &SymbolLayer::setTextMaxWidth>; - result["text-line-height"] = &setProperty, &SymbolLayer::setTextLineHeight>; - result["text-letter-spacing"] = &setProperty, &SymbolLayer::setTextLetterSpacing>; - result["text-justify"] = &setProperty, &SymbolLayer::setTextJustify>; - result["text-anchor"] = &setProperty, &SymbolLayer::setTextAnchor>; - result["text-max-angle"] = &setProperty, &SymbolLayer::setTextMaxAngle>; - result["text-rotate"] = &setProperty, &SymbolLayer::setTextRotate>; - result["text-padding"] = &setProperty, &SymbolLayer::setTextPadding>; - result["text-keep-upright"] = &setProperty, &SymbolLayer::setTextKeepUpright>; - result["text-transform"] = &setProperty, &SymbolLayer::setTextTransform>; - result["text-offset"] = &setProperty>, &SymbolLayer::setTextOffset>; - result["text-allow-overlap"] = &setProperty, &SymbolLayer::setTextAllowOverlap>; - result["text-ignore-placement"] = &setProperty, &SymbolLayer::setTextIgnorePlacement>; - result["text-optional"] = &setProperty, &SymbolLayer::setTextOptional>; - - - - - - return result; -} - -template -auto makePaintPropertySetters() { - std::unordered_map> result; - - result["fill-antialias"] = &setProperty, &FillLayer::setFillAntialias>; - result["fill-antialias-transition"] = &setTransition; - result["fill-opacity"] = &setProperty, &FillLayer::setFillOpacity>; - result["fill-opacity-transition"] = &setTransition; - result["fill-color"] = &setProperty, &FillLayer::setFillColor>; - result["fill-color-transition"] = &setTransition; - result["fill-outline-color"] = &setProperty, &FillLayer::setFillOutlineColor>; - result["fill-outline-color-transition"] = &setTransition; - result["fill-translate"] = &setProperty>, &FillLayer::setFillTranslate>; - result["fill-translate-transition"] = &setTransition; - result["fill-translate-anchor"] = &setProperty, &FillLayer::setFillTranslateAnchor>; - result["fill-translate-anchor-transition"] = &setTransition; - result["fill-pattern"] = &setProperty, &FillLayer::setFillPattern>; - result["fill-pattern-transition"] = &setTransition; - - result["line-opacity"] = &setProperty, &LineLayer::setLineOpacity>; - result["line-opacity-transition"] = &setTransition; - result["line-color"] = &setProperty, &LineLayer::setLineColor>; - result["line-color-transition"] = &setTransition; - result["line-translate"] = &setProperty>, &LineLayer::setLineTranslate>; - result["line-translate-transition"] = &setTransition; - result["line-translate-anchor"] = &setProperty, &LineLayer::setLineTranslateAnchor>; - result["line-translate-anchor-transition"] = &setTransition; - result["line-width"] = &setProperty, &LineLayer::setLineWidth>; - result["line-width-transition"] = &setTransition; - result["line-gap-width"] = &setProperty, &LineLayer::setLineGapWidth>; - result["line-gap-width-transition"] = &setTransition; - result["line-offset"] = &setProperty, &LineLayer::setLineOffset>; - result["line-offset-transition"] = &setTransition; - result["line-blur"] = &setProperty, &LineLayer::setLineBlur>; - result["line-blur-transition"] = &setTransition; - result["line-dasharray"] = &setProperty>, &LineLayer::setLineDasharray>; - result["line-dasharray-transition"] = &setTransition; - result["line-pattern"] = &setProperty, &LineLayer::setLinePattern>; - result["line-pattern-transition"] = &setTransition; - - result["icon-opacity"] = &setProperty, &SymbolLayer::setIconOpacity>; - result["icon-opacity-transition"] = &setTransition; - result["icon-color"] = &setProperty, &SymbolLayer::setIconColor>; - result["icon-color-transition"] = &setTransition; - result["icon-halo-color"] = &setProperty, &SymbolLayer::setIconHaloColor>; - result["icon-halo-color-transition"] = &setTransition; - result["icon-halo-width"] = &setProperty, &SymbolLayer::setIconHaloWidth>; - result["icon-halo-width-transition"] = &setTransition; - result["icon-halo-blur"] = &setProperty, &SymbolLayer::setIconHaloBlur>; - result["icon-halo-blur-transition"] = &setTransition; - result["icon-translate"] = &setProperty>, &SymbolLayer::setIconTranslate>; - result["icon-translate-transition"] = &setTransition; - result["icon-translate-anchor"] = &setProperty, &SymbolLayer::setIconTranslateAnchor>; - result["icon-translate-anchor-transition"] = &setTransition; - result["text-opacity"] = &setProperty, &SymbolLayer::setTextOpacity>; - result["text-opacity-transition"] = &setTransition; - result["text-color"] = &setProperty, &SymbolLayer::setTextColor>; - result["text-color-transition"] = &setTransition; - result["text-halo-color"] = &setProperty, &SymbolLayer::setTextHaloColor>; - result["text-halo-color-transition"] = &setTransition; - result["text-halo-width"] = &setProperty, &SymbolLayer::setTextHaloWidth>; - result["text-halo-width-transition"] = &setTransition; - result["text-halo-blur"] = &setProperty, &SymbolLayer::setTextHaloBlur>; - result["text-halo-blur-transition"] = &setTransition; - result["text-translate"] = &setProperty>, &SymbolLayer::setTextTranslate>; - result["text-translate-transition"] = &setTransition; - result["text-translate-anchor"] = &setProperty, &SymbolLayer::setTextTranslateAnchor>; - result["text-translate-anchor-transition"] = &setTransition; - - result["circle-radius"] = &setProperty, &CircleLayer::setCircleRadius>; - result["circle-radius-transition"] = &setTransition; - result["circle-color"] = &setProperty, &CircleLayer::setCircleColor>; - result["circle-color-transition"] = &setTransition; - result["circle-blur"] = &setProperty, &CircleLayer::setCircleBlur>; - result["circle-blur-transition"] = &setTransition; - result["circle-opacity"] = &setProperty, &CircleLayer::setCircleOpacity>; - result["circle-opacity-transition"] = &setTransition; - result["circle-translate"] = &setProperty>, &CircleLayer::setCircleTranslate>; - result["circle-translate-transition"] = &setTransition; - result["circle-translate-anchor"] = &setProperty, &CircleLayer::setCircleTranslateAnchor>; - result["circle-translate-anchor-transition"] = &setTransition; - result["circle-pitch-scale"] = &setProperty, &CircleLayer::setCirclePitchScale>; - result["circle-pitch-scale-transition"] = &setTransition; - result["circle-pitch-alignment"] = &setProperty, &CircleLayer::setCirclePitchAlignment>; - result["circle-pitch-alignment-transition"] = &setTransition; - result["circle-stroke-width"] = &setProperty, &CircleLayer::setCircleStrokeWidth>; - result["circle-stroke-width-transition"] = &setTransition; - result["circle-stroke-color"] = &setProperty, &CircleLayer::setCircleStrokeColor>; - result["circle-stroke-color-transition"] = &setTransition; - result["circle-stroke-opacity"] = &setProperty, &CircleLayer::setCircleStrokeOpacity>; - result["circle-stroke-opacity-transition"] = &setTransition; - - result["fill-extrusion-opacity"] = &setProperty, &FillExtrusionLayer::setFillExtrusionOpacity>; - result["fill-extrusion-opacity-transition"] = &setTransition; - result["fill-extrusion-color"] = &setProperty, &FillExtrusionLayer::setFillExtrusionColor>; - result["fill-extrusion-color-transition"] = &setTransition; - result["fill-extrusion-translate"] = &setProperty>, &FillExtrusionLayer::setFillExtrusionTranslate>; - result["fill-extrusion-translate-transition"] = &setTransition; - result["fill-extrusion-translate-anchor"] = &setProperty, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>; - result["fill-extrusion-translate-anchor-transition"] = &setTransition; - result["fill-extrusion-pattern"] = &setProperty, &FillExtrusionLayer::setFillExtrusionPattern>; - result["fill-extrusion-pattern-transition"] = &setTransition; - result["fill-extrusion-height"] = &setProperty, &FillExtrusionLayer::setFillExtrusionHeight>; - result["fill-extrusion-height-transition"] = &setTransition; - result["fill-extrusion-base"] = &setProperty, &FillExtrusionLayer::setFillExtrusionBase>; - result["fill-extrusion-base-transition"] = &setTransition; - - result["raster-opacity"] = &setProperty, &RasterLayer::setRasterOpacity>; - result["raster-opacity-transition"] = &setTransition; - result["raster-hue-rotate"] = &setProperty, &RasterLayer::setRasterHueRotate>; - result["raster-hue-rotate-transition"] = &setTransition; - result["raster-brightness-min"] = &setProperty, &RasterLayer::setRasterBrightnessMin>; - result["raster-brightness-min-transition"] = &setTransition; - result["raster-brightness-max"] = &setProperty, &RasterLayer::setRasterBrightnessMax>; - result["raster-brightness-max-transition"] = &setTransition; - result["raster-saturation"] = &setProperty, &RasterLayer::setRasterSaturation>; - result["raster-saturation-transition"] = &setTransition; - result["raster-contrast"] = &setProperty, &RasterLayer::setRasterContrast>; - result["raster-contrast-transition"] = &setTransition; - result["raster-fade-duration"] = &setProperty, &RasterLayer::setRasterFadeDuration>; - result["raster-fade-duration-transition"] = &setTransition; - - result["background-color"] = &setProperty, &BackgroundLayer::setBackgroundColor>; - result["background-color-transition"] = &setTransition; - result["background-pattern"] = &setProperty, &BackgroundLayer::setBackgroundPattern>; - result["background-pattern-transition"] = &setTransition; - result["background-opacity"] = &setProperty, &BackgroundLayer::setBackgroundOpacity>; - result["background-opacity-transition"] = &setTransition; - - return result; -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/conversion/position.hpp b/include/mbgl/style/conversion/position.hpp index 7036b03822..044c45862d 100644 --- a/include/mbgl/style/conversion/position.hpp +++ b/include/mbgl/style/conversion/position.hpp @@ -2,9 +2,6 @@ #include #include -#include - -#include namespace mbgl { namespace style { @@ -12,16 +9,7 @@ namespace conversion { template <> struct Converter { - template - optional operator()(const V& value, Error& error) const { - optional> spherical = convert>(value, error); - - if (!spherical) { - return {}; - } - - return Position(*spherical); - } + optional operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/property_setter.hpp b/include/mbgl/style/conversion/property_setter.hpp deleted file mode 100644 index 759c4512cc..0000000000 --- a/include/mbgl/style/conversion/property_setter.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -namespace mbgl { -namespace style { -namespace conversion { - -template -using PropertySetter = optional (*) (Layer&, const V&); - -template -optional setProperty(Layer& layer, const V& value) { - auto* typedLayer = layer.as(); - if (!typedLayer) { - return Error { "layer doesn't support this property" }; - } - - Error error; - optional typedValue = convert(value, error); - if (!typedValue) { - return error; - } - - (typedLayer->*setter)(*typedValue); - return {}; -} - -template -optional setTransition(Layer& layer, const V& value) { - auto* typedLayer = layer.as(); - if (!typedLayer) { - return Error { "layer doesn't support this property" }; - } - - Error error; - optional transition = convert(value, error); - if (!transition) { - return error; - } - - (typedLayer->*setter)(*transition); - return {}; -} - -template -optional setVisibility(Layer& layer, const V& value) { - if (isUndefined(value)) { - layer.setVisibility(VisibilityType::Visible); - return {}; - } - - Error error; - optional visibility = convert(value, error); - if (!visibility) { - return error; - } - - layer.setVisibility(*visibility); - return {}; -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp index f8937da07d..c7f971ec91 100644 --- a/include/mbgl/style/conversion/property_value.hpp +++ b/include/mbgl/style/conversion/property_value.hpp @@ -11,8 +11,7 @@ namespace conversion { template struct Converter> { - template - optional> operator()(const V& value, Error& error) const { + optional> operator()(const Convertible& value, Error& error) const { if (isUndefined(value)) { return PropertyValue(); } else if (isObject(value)) { diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp index e0563ac10b..2cf2e36da4 100644 --- a/include/mbgl/style/conversion/source.hpp +++ b/include/mbgl/style/conversion/source.hpp @@ -1,16 +1,9 @@ #pragma once #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include + +#include namespace mbgl { namespace style { @@ -19,170 +12,7 @@ namespace conversion { template <> struct Converter> { public: - - template - optional> operator()(const V& value, Error& error, const std::string& id) const { - if (!isObject(value)) { - error = { "source must be an object" }; - return {}; - } - - auto typeValue = objectMember(value, "type"); - if (!typeValue) { - error = { "source must have a type" }; - return {}; - } - - optional type = toString(*typeValue); - if (!type) { - error = { "source type must be a string" }; - return {}; - } - - if (*type == "raster") { - return convertRasterSource(id, value, error); - } else if (*type == "vector") { - return convertVectorSource(id, value, error); - } else if (*type == "geojson") { - return convertGeoJSONSource(id, value, error); - } else if (*type == "image") { - return convertImageSource(id, value, error); - } else { - error = { "invalid source type" }; - return {}; - } - } - -private: - // A tile source can either specify a URL to TileJSON, or inline TileJSON. - template - optional> convertURLOrTileset(const V& value, Error& error) const { - auto urlVal = objectMember(value, "url"); - if (!urlVal) { - optional tileset = convert(value, error); - if (!tileset) { - return {}; - } - return { *tileset }; - } - - optional url = toString(*urlVal); - if (!url) { - error = { "source url must be a string" }; - return {}; - } - - return { *url }; - } - - template - optional> convertRasterSource(const std::string& id, - const V& value, - Error& error) const { - optional> urlOrTileset = convertURLOrTileset(value, error); - if (!urlOrTileset) { - return {}; - } - - uint16_t tileSize = util::tileSize; - auto tileSizeValue = objectMember(value, "tileSize"); - if (tileSizeValue) { - optional size = toNumber(*tileSizeValue); - if (!size || *size < 0 || *size > std::numeric_limits::max()) { - error = { "invalid tileSize" }; - return {}; - } - tileSize = *size; - } - - return { std::make_unique(id, std::move(*urlOrTileset), tileSize) }; - } - - template - optional> convertVectorSource(const std::string& id, - const V& value, - Error& error) const { - optional> urlOrTileset = convertURLOrTileset(value, error); - if (!urlOrTileset) { - return {}; - } - - return { std::make_unique(id, std::move(*urlOrTileset)) }; - } - - template - optional> convertGeoJSONSource(const std::string& id, - const V& value, - Error& error) const { - auto dataValue = objectMember(value, "data"); - if (!dataValue) { - error = { "GeoJSON source must have a data value" }; - return {}; - } - - optional options = convert(value, error); - if (!options) { - return {}; - } - - auto result = std::make_unique(id, *options); - - if (isObject(*dataValue)) { - optional geoJSON = convert(*dataValue, error); - if (!geoJSON) { - return {}; - } - result->setGeoJSON(std::move(*geoJSON)); - } else if (toString(*dataValue)) { - result->setURL(*toString(*dataValue)); - } else { - error = { "GeoJSON data must be a URL or an object" }; - return {}; - } - - return { std::move(result) }; - } - - template - optional> convertImageSource(const std::string& id, - const V& value, - Error& error) const { - auto urlValue = objectMember(value, "url"); - if (!urlValue) { - error = { "Image source must have a url value" }; - return {}; - } - - auto urlString = toString(*urlValue); - if (!urlString) { - error = { "Image url must be a URL string" }; - return {}; - } - - auto coordinatesValue = objectMember(value, "coordinates"); - if (!coordinatesValue) { - error = { "Image source must have a coordinates values" }; - return {}; - } - - if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) { - error = { "Image coordinates must be an array of four longitude latitude pairs" }; - return {}; - } - - std::array coordinates; - for (std::size_t i=0; i < 4; i++) { - auto latLng = conversion::convert(arrayMember(*coordinatesValue,i), error); - if (!latLng) { - return {}; - } - coordinates[i] = *latLng; - } - auto result = std::make_unique(id, coordinates); - result->setURL(*urlString); - - return { std::move(result) }; - } + optional> operator()(const Convertible& value, Error& error, const std::string& id) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp index 377170aa6a..1fb4acf70d 100644 --- a/include/mbgl/style/conversion/tileset.hpp +++ b/include/mbgl/style/conversion/tileset.hpp @@ -10,70 +10,7 @@ namespace conversion { template <> struct Converter { public: - template - optional operator()(const V& value, Error& error) const { - Tileset result; - - auto tiles = objectMember(value, "tiles"); - if (!tiles) { - error = { "source must have tiles" }; - return {}; - } - - if (!isArray(*tiles)) { - error = { "source tiles must be an array" }; - return {}; - } - - for (std::size_t i = 0; i < arrayLength(*tiles); i++) { - optional urlTemplate = toString(arrayMember(*tiles, i)); - if (!urlTemplate) { - error = { "source tiles member must be a string" }; - return {}; - } - result.tiles.push_back(std::move(*urlTemplate)); - } - - auto schemeValue = objectMember(value, "scheme"); - if (schemeValue) { - optional scheme = toString(*schemeValue); - if (scheme && *scheme == "tms") { - result.scheme = Tileset::Scheme::TMS; - } - } - - auto minzoomValue = objectMember(value, "minzoom"); - if (minzoomValue) { - optional minzoom = toNumber(*minzoomValue); - if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits::max()) { - error = { "invalid minzoom" }; - return {}; - } - result.zoomRange.min = *minzoom; - } - - auto maxzoomValue = objectMember(value, "maxzoom"); - if (maxzoomValue) { - optional maxzoom = toNumber(*maxzoomValue); - if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits::max()) { - error = { "invalid maxzoom" }; - return {}; - } - result.zoomRange.max = *maxzoom; - } - - auto attributionValue = objectMember(value, "attribution"); - if (attributionValue) { - optional attribution = toString(*attributionValue); - if (!attribution) { - error = { "source attribution must be a string" }; - return {}; - } - result.attribution = std::move(*attribution); - } - - return result; - } + optional operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/style/conversion/transition_options.hpp b/include/mbgl/style/conversion/transition_options.hpp index de8834d578..0563f39ac3 100644 --- a/include/mbgl/style/conversion/transition_options.hpp +++ b/include/mbgl/style/conversion/transition_options.hpp @@ -10,37 +10,7 @@ namespace conversion { template <> struct Converter { public: - template - optional operator()(const V& value, Error& error) const { - if (!isObject(value)) { - error = { "transition must be an object" }; - return {}; - } - - TransitionOptions result; - - auto duration = objectMember(value, "duration"); - if (duration) { - auto number = toNumber(*duration); - if (!number) { - error = { "duration must be a number" }; - return {}; - } - result.duration = { std::chrono::milliseconds(int64_t(*number)) }; - } - - auto delay = objectMember(value, "delay"); - if (delay) { - auto number = toNumber(*delay); - if (!number) { - error = { "delay must be a number" }; - return {}; - } - result.delay = { std::chrono::milliseconds(int64_t(*number)) }; - } - - return result; - } + optional operator()(const Convertible& value, Error& error) const; }; } // namespace conversion diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 6d725b102b..54a8c99fab 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -161,6 +161,13 @@ public: point.longitude() <= ne.longitude()); } + bool contains(const LatLngBounds& area) const { + return (area.ne.latitude() <= ne.latitude() && + area.sw.latitude() >= sw.latitude() && + area.ne.longitude() <= ne.longitude() && + area.sw.longitude() >= sw.longitude()); + } + bool intersects(const LatLngBounds area) const { return (area.ne.latitude() > sw.latitude() && area.sw.latitude() < ne.latitude() && diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index f64502c5bc..1613af3b36 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -92,8 +92,8 @@ public: const double t2z = tileSize * std::pow(2, zoom); Point pt = project_(point, t2z); // Flip y coordinate - auto x = std::round(std::min(pt.x, t2z)); - auto y = std::round(std::min(t2z - pt.y, t2z)); + auto x = ::round(std::min(pt.x, t2z)); + auto y = ::round(std::min(t2z - pt.y, t2z)); return { x, y }; } private: diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp index 61aa47d4ea..5a03e1a9da 100644 --- a/include/mbgl/util/tileset.hpp +++ b/include/mbgl/util/tileset.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/mapbox-gl-native.pro b/mapbox-gl-native.pro index 9f9f411477..38742c0499 100644 --- a/mapbox-gl-native.pro +++ b/mapbox-gl-native.pro @@ -2,7 +2,7 @@ TARGET = qmapboxgl load(qt_helper_lib) -CONFIG += qt c++14 exceptions warn_off staticlib +CONFIG += qt c++14 exceptions warn_off staticlib object_parallel_to_source QT += network-private \ gui-private \ @@ -12,6 +12,7 @@ QMAKE_CXXFLAGS += \ -DNDEBUG \ -DQT_IMAGE_DECODERS \ -DRAPIDJSON_HAS_STDSTRING=1 \ + -DMBGL_USE_GLES2 \ -D__QT__ \ -O3 \ -ftemplate-depth=1024 \ @@ -39,17 +40,6 @@ win32 { -D_USE_MATH_DEFINES } -win32:qtConfig(dynamicgl) { - QMAKE_CXXFLAGS += \ - -DMBGL_USE_GLES2 \ - -DQT_OPENGL_ES_2 -} - -qtConfig(opengles2) { - QMAKE_CXXFLAGS += \ - -DMBGL_USE_GLES2 -} - qtConfig(system-zlib) { QMAKE_USE_PRIVATE += zlib } else { @@ -76,6 +66,7 @@ SOURCES += \ platform/qt/src/qmapbox.cpp \ platform/qt/src/qmapboxgl.cpp \ platform/qt/src/qmapboxgl_renderer_frontend_p.cpp \ + platform/qt/src/qt_geojson.cpp \ platform/qt/src/qt_image.cpp \ platform/qt/src/run_loop.cpp \ platform/qt/src/sqlite3.cpp \ @@ -190,7 +181,17 @@ SOURCES += \ src/mbgl/storage/resource.cpp \ src/mbgl/storage/resource_transform.cpp \ src/mbgl/storage/response.cpp \ + src/mbgl/style/conversion/constant.cpp \ + src/mbgl/style/conversion/coordinate.cpp \ + src/mbgl/style/conversion/filter.cpp \ src/mbgl/style/conversion/geojson.cpp \ + src/mbgl/style/conversion/geojson_options.cpp \ + src/mbgl/style/conversion/layer.cpp \ + src/mbgl/style/conversion/light.cpp \ + src/mbgl/style/conversion/position.cpp \ + src/mbgl/style/conversion/source.cpp \ + src/mbgl/style/conversion/tileset.cpp \ + src/mbgl/style/conversion/transition_options.cpp \ src/mbgl/style/function/categorical_stops.cpp \ src/mbgl/style/function/identity_stops.cpp \ src/mbgl/style/image.cpp \ @@ -323,8 +324,8 @@ INCLUDEPATH += \ deps/boost/1.62.0/include \ deps/cheap-ruler/2.5.3 \ deps/cheap-ruler/2.5.3/include \ - deps/earcut/0.12.3 \ - deps/earcut/0.12.3/include \ + deps/earcut/0.12.4 \ + deps/earcut/0.12.4/include \ deps/geojson/0.4.2 \ deps/geojson/0.4.2/include \ deps/geojsonvt/6.3.0 \ @@ -355,8 +356,9 @@ INCLUDEPATH += \ deps/wagyu/0.4.3/include \ include \ platform/default \ + platform/qt \ platform/qt/include \ src QMAKE_CXXFLAGS += \ - -DMBGL_VERSION_REV=\\\"qt-v1.1.0\\\" + -DMBGL_VERSION_REV=\\\"qt-v1.1.1\\\" diff --git a/platform/default/mbgl/gl/headless_frontend.cpp b/platform/default/mbgl/gl/headless_frontend.cpp index 5d2932258a..9df35657b0 100644 --- a/platform/default/mbgl/gl/headless_frontend.cpp +++ b/platform/default/mbgl/gl/headless_frontend.cpp @@ -1,15 +1,17 @@ #include #include +#include #include +#include #include namespace mbgl { -HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional programCacheDir) - : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler, programCacheDir) { +HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional programCacheDir, GLContextMode mode) + : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler, programCacheDir, mode) { } -HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional programCacheDir) +HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional programCacheDir, GLContextMode mode) : size(size_), pixelRatio(pixelRatio_), backend({ static_cast(size.width * pixelRatio), @@ -20,7 +22,7 @@ HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fi renderer->render(*updateParameters); } }), - renderer(std::make_unique(backend, pixelRatio, fileSource, scheduler, GLContextMode::Unique, programCacheDir)) { + renderer(std::make_unique(backend, pixelRatio, fileSource, scheduler, mode, programCacheDir)) { } HeadlessFrontend::~HeadlessFrontend() = default; @@ -83,4 +85,12 @@ PremultipliedImage HeadlessFrontend::render(Map& map) { return result; } +optional HeadlessFrontend::getTransformState() const { + if (updateParameters) { + return updateParameters->transformState; + } else { + return {}; + } +} + } // namespace mbgl diff --git a/platform/default/mbgl/gl/headless_frontend.hpp b/platform/default/mbgl/gl/headless_frontend.hpp index 33503bc13b..0530d84a25 100644 --- a/platform/default/mbgl/gl/headless_frontend.hpp +++ b/platform/default/mbgl/gl/headless_frontend.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -14,11 +15,12 @@ class Scheduler; class Renderer; class RendererBackend; class Map; +class TransformState; class HeadlessFrontend : public RendererFrontend { public: - HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional programCacheDir = {}); - HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional programCacheDir = {}); + HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional programCacheDir = {}, GLContextMode mode = GLContextMode::Unique); + HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional programCacheDir = {}, GLContextMode mode = GLContextMode::Unique); ~HeadlessFrontend() override; void reset() override; @@ -34,6 +36,8 @@ public: PremultipliedImage readStillImage(); PremultipliedImage render(Map&); + optional getTransformState() const; + private: Size size; float pixelRatio; diff --git a/platform/default/mbgl/map/map_snapshotter.cpp b/platform/default/mbgl/map/map_snapshotter.cpp index 95c46344fe..7b4ec5913b 100644 --- a/platform/default/mbgl/map/map_snapshotter.cpp +++ b/platform/default/mbgl/map/map_snapshotter.cpp @@ -3,9 +3,11 @@ #include #include #include +#include #include #include #include +#include namespace mbgl { @@ -20,6 +22,18 @@ public: const optional region, const optional programCacheDir); + void setStyleURL(std::string styleURL); + std::string getStyleURL() const; + + void setSize(Size); + Size getSize() const; + + void setCameraOptions(CameraOptions); + CameraOptions getCameraOptions() const; + + void setRegion(LatLngBounds); + LatLngBounds getRegion() const; + void snapshot(ActorRef); private: @@ -44,18 +58,80 @@ MapSnapshotter::Impl::Impl(FileSource& fileSource, // Set region, if specified if (region) { - mbgl::EdgeInsets insets = { 0, 0, 0, 0 }; - std::vector latLngs = { region->southwest(), region->northeast() }; - map.jumpTo(map.cameraForLatLngs(latLngs, insets)); + this->setRegion(*region); } } void MapSnapshotter::Impl::snapshot(ActorRef callback) { map.renderStill([this, callback = std::move(callback)] (std::exception_ptr error) mutable { - callback.invoke(&MapSnapshotter::Callback::operator(), error, error ? PremultipliedImage() : frontend.readStillImage()); + + // Create lambda that captures the current transform state + // and can be used to translate for geographic to screen + // coordinates + assert (frontend.getTransformState()); + PointForFn pointForFn { [=, center=map.getLatLng(), transformState = *frontend.getTransformState()] (const LatLng& latLng) { + LatLng unwrappedLatLng = latLng.wrapped(); + unwrappedLatLng.unwrapForShortestPath(center); + Transform transform { transformState }; + return transform.latLngToScreenCoordinate(unwrappedLatLng); + }}; + + // Collect all source attributions + std::vector attributions; + for (auto source : map.getStyle().getSources()) { + auto attribution = source->getAttribution(); + if (attribution) { + attributions.push_back(*attribution); + } + } + + // Invoke callback + callback.invoke( + &MapSnapshotter::Callback::operator(), + error, + error ? PremultipliedImage() : frontend.readStillImage(), + std::move(attributions), + std::move(pointForFn) + ); }); } +void MapSnapshotter::Impl::setStyleURL(std::string styleURL) { + map.getStyle().loadURL(styleURL); +} + +std::string MapSnapshotter::Impl::getStyleURL() const { + return map.getStyle().getURL(); +} + +void MapSnapshotter::Impl::setSize(Size size) { + map.setSize(size); + frontend.setSize(size); +} + +Size MapSnapshotter::Impl::getSize() const { + return map.getSize(); +} + +void MapSnapshotter::Impl::setCameraOptions(CameraOptions cameraOptions) { + map.jumpTo(cameraOptions); +} + +CameraOptions MapSnapshotter::Impl::getCameraOptions() const { + EdgeInsets insets; + return map.getCameraOptions(insets); +} + +void MapSnapshotter::Impl::setRegion(LatLngBounds region) { + mbgl::EdgeInsets insets = { 0, 0, 0, 0 }; + std::vector latLngs = { region.southwest(), region.northeast() }; + map.jumpTo(map.cameraForLatLngs(latLngs, insets)); +} + +LatLngBounds MapSnapshotter::Impl::getRegion() const { + return map.latLngBoundsForCamera(getCameraOptions()); +} + MapSnapshotter::MapSnapshotter(FileSource& fileSource, Scheduler& scheduler, const std::string& styleURL, @@ -70,7 +146,39 @@ MapSnapshotter::MapSnapshotter(FileSource& fileSource, MapSnapshotter::~MapSnapshotter() = default; void MapSnapshotter::snapshot(ActorRef callback) { - impl->actor().invoke(&Impl::snapshot, std::move(callback)); + impl->actor().invoke(&Impl::snapshot, std::move(callback)); +} + +void MapSnapshotter::setStyleURL(const std::string& styleURL) { + impl->actor().invoke(&Impl::setStyleURL, styleURL); +} + +std::string MapSnapshotter::getStyleURL() const { + return impl->actor().ask(&Impl::getStyleURL).get(); +} + +void MapSnapshotter::setSize(const Size& size) { + impl->actor().invoke(&Impl::setSize, size); +} + +Size MapSnapshotter::getSize() const { + return impl->actor().ask(&Impl::getSize).get(); +} + +void MapSnapshotter::setCameraOptions(const CameraOptions& options) { + impl->actor().invoke(&Impl::setCameraOptions, options); +} + +CameraOptions MapSnapshotter::getCameraOptions() const { + return impl->actor().ask(&Impl::getCameraOptions).get(); +} + +void MapSnapshotter::setRegion(const LatLngBounds& bounds) { + impl->actor().invoke(&Impl::setRegion, std::move(bounds)); +} + +LatLngBounds MapSnapshotter::getRegion() const { + return impl->actor().ask(&Impl::getRegion).get(); } } // namespace mbgl diff --git a/platform/default/mbgl/map/map_snapshotter.hpp b/platform/default/mbgl/map/map_snapshotter.hpp index 0afa848fd8..985396e5a3 100644 --- a/platform/default/mbgl/map/map_snapshotter.hpp +++ b/platform/default/mbgl/map/map_snapshotter.hpp @@ -3,9 +3,12 @@ #include #include #include +#include #include +#include #include +#include #include namespace mbgl { @@ -16,6 +19,10 @@ class FileSource; class Size; class LatLngBounds; +namespace style { +class Style; +} // namespace style + class MapSnapshotter { public: MapSnapshotter(FileSource& fileSource, @@ -29,7 +36,21 @@ public: ~MapSnapshotter(); - using Callback = std::function; + void setStyleURL(const std::string& styleURL); + std::string getStyleURL() const; + + void setSize(const Size&); + Size getSize() const; + + void setCameraOptions(const CameraOptions&); + CameraOptions getCameraOptions() const; + + void setRegion(const LatLngBounds&); + LatLngBounds getRegion() const; + + using PointForFn = std::function; + using Attributions = std::vector; + using Callback = std::function; void snapshot(ActorRef); private: diff --git a/platform/qt/mbgl/gl/gl_impl.hpp b/platform/qt/mbgl/gl/gl_impl.hpp new file mode 100644 index 0000000000..a9f720db7c --- /dev/null +++ b/platform/qt/mbgl/gl/gl_impl.hpp @@ -0,0 +1,173 @@ +#pragma once + +#include + +// Qt4 +#if QT_VERSION < 0x050000 + #if MBGL_USE_GLES2 + #define GL_GLEXT_PROTOTYPES + #include + #include + #else + #define GL_GLEXT_PROTOTYPES + #include + #include + #endif + +// Qt5 +#else + #include + #include + + #ifndef GL_RGBA8_OES + #define GL_RGBA8_OES GL_RGBA8 + #endif + + #ifndef GL_DEPTH24_STENCIL8_OES + #define GL_DEPTH24_STENCIL8_OES GL_DEPTH24_STENCIL8 + #endif + + #define glActiveTexture(...) QOpenGLContext::currentContext()->functions()->glActiveTexture(__VA_ARGS__) + #define glAttachShader(...) QOpenGLContext::currentContext()->functions()->glAttachShader(__VA_ARGS__) + #define glBindAttribLocation(...) QOpenGLContext::currentContext()->functions()->glBindAttribLocation(__VA_ARGS__) + #define glBindBuffer(...) QOpenGLContext::currentContext()->functions()->glBindBuffer(__VA_ARGS__) + #define glBindFramebuffer(...) QOpenGLContext::currentContext()->functions()->glBindFramebuffer(__VA_ARGS__) + #define glBindRenderbuffer(...) QOpenGLContext::currentContext()->functions()->glBindRenderbuffer(__VA_ARGS__) + #define glBindTexture(...) QOpenGLContext::currentContext()->functions()->glBindTexture(__VA_ARGS__) + #define glBlendColor(...) QOpenGLContext::currentContext()->functions()->glBlendColor(__VA_ARGS__) + #define glBlendEquation(...) QOpenGLContext::currentContext()->functions()->glBlendEquation(__VA_ARGS__) + #define glBlendEquationSeparate(...) QOpenGLContext::currentContext()->functions()->glBlendEquationSeparate(__VA_ARGS__) + #define glBlendFunc(...) QOpenGLContext::currentContext()->functions()->glBlendFunc(__VA_ARGS__) + #define glBlendFuncSeparate(...) QOpenGLContext::currentContext()->functions()->glBlendFuncSeparate(__VA_ARGS__) + #define glBufferData(...) QOpenGLContext::currentContext()->functions()->glBufferData(__VA_ARGS__) + #define glBufferSubData(...) QOpenGLContext::currentContext()->functions()->glBufferSubData(__VA_ARGS__) + #define glCheckFramebufferStatus(...) QOpenGLContext::currentContext()->functions()->glCheckFramebufferStatus(__VA_ARGS__) + #define glClear(...) QOpenGLContext::currentContext()->functions()->glClear(__VA_ARGS__) + #define glClearColor(...) QOpenGLContext::currentContext()->functions()->glClearColor(__VA_ARGS__) + #define glClearDepthf(...) QOpenGLContext::currentContext()->functions()->glClearDepthf(__VA_ARGS__) + #define glClearStencil(...) QOpenGLContext::currentContext()->functions()->glClearStencil(__VA_ARGS__) + #define glColorMask(...) QOpenGLContext::currentContext()->functions()->glColorMask(__VA_ARGS__) + #define glCompileShader(...) QOpenGLContext::currentContext()->functions()->glCompileShader(__VA_ARGS__) + #define glCompressedTexImage2D(...) QOpenGLContext::currentContext()->functions()->glCompressedTexImage2D(__VA_ARGS__) + #define glCompressedTexSubImage2D(...) QOpenGLContext::currentContext()->functions()->glCompressedTexSubImage2D(__VA_ARGS__) + #define glCopyTexImage2D(...) QOpenGLContext::currentContext()->functions()->glCopyTexImage2D(__VA_ARGS__) + #define glCopyTexSubImage2D(...) QOpenGLContext::currentContext()->functions()->glCopyTexSubImage2D(__VA_ARGS__) + #define glCreateProgram(...) QOpenGLContext::currentContext()->functions()->glCreateProgram(__VA_ARGS__) + #define glCreateShader(...) QOpenGLContext::currentContext()->functions()->glCreateShader(__VA_ARGS__) + #define glCullFace(...) QOpenGLContext::currentContext()->functions()->glCullFace(__VA_ARGS__) + #define glDeleteBuffers(...) QOpenGLContext::currentContext()->functions()->glDeleteBuffers(__VA_ARGS__) + #define glDeleteFramebuffers(...) QOpenGLContext::currentContext()->functions()->glDeleteFramebuffers(__VA_ARGS__) + #define glDeleteProgram(...) QOpenGLContext::currentContext()->functions()->glDeleteProgram(__VA_ARGS__) + #define glDeleteRenderbuffers(...) QOpenGLContext::currentContext()->functions()->glDeleteRenderbuffers(__VA_ARGS__) + #define glDeleteShader(...) QOpenGLContext::currentContext()->functions()->glDeleteShader(__VA_ARGS__) + #define glDeleteTextures(...) QOpenGLContext::currentContext()->functions()->glDeleteTextures(__VA_ARGS__) + #define glDepthFunc(...) QOpenGLContext::currentContext()->functions()->glDepthFunc(__VA_ARGS__) + #define glDepthMask(...) QOpenGLContext::currentContext()->functions()->glDepthMask(__VA_ARGS__) + #define glDepthRangef(...) QOpenGLContext::currentContext()->functions()->glDepthRangef(__VA_ARGS__) + #define glDetachShader(...) QOpenGLContext::currentContext()->functions()->glDetachShader(__VA_ARGS__) + #define glDisable(...) QOpenGLContext::currentContext()->functions()->glDisable(__VA_ARGS__) + #define glDisableVertexAttribArray(...) QOpenGLContext::currentContext()->functions()->glDisableVertexAttribArray(__VA_ARGS__) + #define glDrawArrays(...) QOpenGLContext::currentContext()->functions()->glDrawArrays(__VA_ARGS__) + #define glDrawElements(...) QOpenGLContext::currentContext()->functions()->glDrawElements(__VA_ARGS__) + #define glEnable(...) QOpenGLContext::currentContext()->functions()->glEnable(__VA_ARGS__) + #define glEnableVertexAttribArray(...) QOpenGLContext::currentContext()->functions()->glEnableVertexAttribArray(__VA_ARGS__) + #define glFinish(...) QOpenGLContext::currentContext()->functions()->glFinish(__VA_ARGS__) + #define glFlush(...) QOpenGLContext::currentContext()->functions()->glFlush(__VA_ARGS__) + #define glFramebufferRenderbuffer(...) QOpenGLContext::currentContext()->functions()->glFramebufferRenderbuffer(__VA_ARGS__) + #define glFramebufferTexture2D(...) QOpenGLContext::currentContext()->functions()->glFramebufferTexture2D(__VA_ARGS__) + #define glFrontFace(...) QOpenGLContext::currentContext()->functions()->glFrontFace(__VA_ARGS__) + #define glGenBuffers(...) QOpenGLContext::currentContext()->functions()->glGenBuffers(__VA_ARGS__) + #define glGenerateMipmap(...) QOpenGLContext::currentContext()->functions()->glGenerateMipmap(__VA_ARGS__) + #define glGenFramebuffers(...) QOpenGLContext::currentContext()->functions()->glGenFramebuffers(__VA_ARGS__) + #define glGenRenderbuffers(...) QOpenGLContext::currentContext()->functions()->glGenRenderbuffers(__VA_ARGS__) + #define glGenTextures(...) QOpenGLContext::currentContext()->functions()->glGenTextures(__VA_ARGS__) + #define glGetActiveAttrib(...) QOpenGLContext::currentContext()->functions()->glGetActiveAttrib(__VA_ARGS__) + #define glGetActiveUniform(...) QOpenGLContext::currentContext()->functions()->glGetActiveUniform(__VA_ARGS__) + #define glGetAttachedShaders(...) QOpenGLContext::currentContext()->functions()->glGetAttachedShaders(__VA_ARGS__) + #define glGetAttribLocation(...) QOpenGLContext::currentContext()->functions()->glGetAttribLocation(__VA_ARGS__) + #define glGetBooleanv(...) QOpenGLContext::currentContext()->functions()->glGetBooleanv(__VA_ARGS__) + #define glGetBufferParameteriv(...) QOpenGLContext::currentContext()->functions()->glGetBufferParameteriv(__VA_ARGS__) + #define glGetError(...) QOpenGLContext::currentContext()->functions()->glGetError(__VA_ARGS__) + #define glGetFloatv(...) QOpenGLContext::currentContext()->functions()->glGetFloatv(__VA_ARGS__) + #define glGetFramebufferAttachmentParameteriv(...) QOpenGLContext::currentContext()->functions()->glGetFramebufferAttachmentParameteriv(__VA_ARGS__) + #define glGetIntegerv(...) QOpenGLContext::currentContext()->functions()->glGetIntegerv(__VA_ARGS__) + #define glGetProgramInfoLog(...) QOpenGLContext::currentContext()->functions()->glGetProgramInfoLog(__VA_ARGS__) + #define glGetProgramiv(...) QOpenGLContext::currentContext()->functions()->glGetProgramiv(__VA_ARGS__) + #define glGetRenderbufferParameteriv(...) QOpenGLContext::currentContext()->functions()->glGetRenderbufferParameteriv(__VA_ARGS__) + #define glGetShaderInfoLog(...) QOpenGLContext::currentContext()->functions()->glGetShaderInfoLog(__VA_ARGS__) + #define glGetShaderiv(...) QOpenGLContext::currentContext()->functions()->glGetShaderiv(__VA_ARGS__) + #define glGetShaderPrecisionFormat(...) QOpenGLContext::currentContext()->functions()->glGetShaderPrecisionFormat(__VA_ARGS__) + #define glGetShaderSource(...) QOpenGLContext::currentContext()->functions()->glGetShaderSource(__VA_ARGS__) + #define glGetString(...) QOpenGLContext::currentContext()->functions()->glGetString(__VA_ARGS__) + #define glGetTexParameterfv(...) QOpenGLContext::currentContext()->functions()->glGetTexParameterfv(__VA_ARGS__) + #define glGetTexParameteriv(...) QOpenGLContext::currentContext()->functions()->glGetTexParameteriv(__VA_ARGS__) + #define glGetUniformfv(...) QOpenGLContext::currentContext()->functions()->glGetUniformfv(__VA_ARGS__) + #define glGetUniformiv(...) QOpenGLContext::currentContext()->functions()->glGetUniformiv(__VA_ARGS__) + #define glGetUniformLocation(...) QOpenGLContext::currentContext()->functions()->glGetUniformLocation(__VA_ARGS__) + #define glGetVertexAttribfv(...) QOpenGLContext::currentContext()->functions()->glGetVertexAttribfv(__VA_ARGS__) + #define glGetVertexAttribiv(...) QOpenGLContext::currentContext()->functions()->glGetVertexAttribiv(__VA_ARGS__) + #define glGetVertexAttribPointerv(...) QOpenGLContext::currentContext()->functions()->glGetVertexAttribPointerv(__VA_ARGS__) + #define glHint(...) QOpenGLContext::currentContext()->functions()->glHint(__VA_ARGS__) + #define glIsBuffer(...) QOpenGLContext::currentContext()->functions()->glIsBuffer(__VA_ARGS__) + #define glIsEnabled(...) QOpenGLContext::currentContext()->functions()->glIsEnabled(__VA_ARGS__) + #define glIsFramebuffer(...) QOpenGLContext::currentContext()->functions()->glIsFramebuffer(__VA_ARGS__) + #define glIsProgram(...) QOpenGLContext::currentContext()->functions()->glIsProgram(__VA_ARGS__) + #define glIsRenderbuffer(...) QOpenGLContext::currentContext()->functions()->glIsRenderbuffer(__VA_ARGS__) + #define glIsShader(...) QOpenGLContext::currentContext()->functions()->glIsShader(__VA_ARGS__) + #define glIsTexture(...) QOpenGLContext::currentContext()->functions()->glIsTexture(__VA_ARGS__) + #define glLineWidth(...) QOpenGLContext::currentContext()->functions()->glLineWidth(__VA_ARGS__) + #define glLinkProgram(...) QOpenGLContext::currentContext()->functions()->glLinkProgram(__VA_ARGS__) + #define glPixelStorei(...) QOpenGLContext::currentContext()->functions()->glPixelStorei(__VA_ARGS__) + #define glPolygonOffset(...) QOpenGLContext::currentContext()->functions()->glPolygonOffset(__VA_ARGS__) + #define glReadPixels(...) QOpenGLContext::currentContext()->functions()->glReadPixels(__VA_ARGS__) + #define glReleaseShaderCompiler(...) QOpenGLContext::currentContext()->functions()->glReleaseShaderCompiler(__VA_ARGS__) + #define glRenderbufferStorage(...) QOpenGLContext::currentContext()->functions()->glRenderbufferStorage(__VA_ARGS__) + #define glSampleCoverage(...) QOpenGLContext::currentContext()->functions()->glSampleCoverage(__VA_ARGS__) + #define glScissor(...) QOpenGLContext::currentContext()->functions()->glScissor(__VA_ARGS__) + #define glShaderBinary(...) QOpenGLContext::currentContext()->functions()->glShaderBinary(__VA_ARGS__) + #define glShaderSource(...) QOpenGLContext::currentContext()->functions()->glShaderSource(__VA_ARGS__) + #define glStencilFunc(...) QOpenGLContext::currentContext()->functions()->glStencilFunc(__VA_ARGS__) + #define glStencilFuncSeparate(...) QOpenGLContext::currentContext()->functions()->glStencilFuncSeparate(__VA_ARGS__) + #define glStencilMask(...) QOpenGLContext::currentContext()->functions()->glStencilMask(__VA_ARGS__) + #define glStencilMaskSeparate(...) QOpenGLContext::currentContext()->functions()->glStencilMaskSeparate(__VA_ARGS__) + #define glStencilOp(...) QOpenGLContext::currentContext()->functions()->glStencilOp(__VA_ARGS__) + #define glStencilOpSeparate(...) QOpenGLContext::currentContext()->functions()->glStencilOpSeparate(__VA_ARGS__) + #define glTexImage2D(...) QOpenGLContext::currentContext()->functions()->glTexImage2D(__VA_ARGS__) + #define glTexLevelParameteriv(...) QOpenGLContext::currentContext()->functions()->glTexLevelParameteriv(__VA_ARGS__) + #define glTexParameterf(...) QOpenGLContext::currentContext()->functions()->glTexParameterf(__VA_ARGS__) + #define glTexParameterfv(...) QOpenGLContext::currentContext()->functions()->glTexParameterfv(__VA_ARGS__) + #define glTexParameteri(...) QOpenGLContext::currentContext()->functions()->glTexParameteri(__VA_ARGS__) + #define glTexParameteriv(...) QOpenGLContext::currentContext()->functions()->glTexParameteriv(__VA_ARGS__) + #define glTexSubImage2D(...) QOpenGLContext::currentContext()->functions()->glTexSubImage2D(__VA_ARGS__) + #define glUniform1f(...) QOpenGLContext::currentContext()->functions()->glUniform1f(__VA_ARGS__) + #define glUniform1fv(...) QOpenGLContext::currentContext()->functions()->glUniform1fv(__VA_ARGS__) + #define glUniform1i(...) QOpenGLContext::currentContext()->functions()->glUniform1i(__VA_ARGS__) + #define glUniform1iv(...) QOpenGLContext::currentContext()->functions()->glUniform1iv(__VA_ARGS__) + #define glUniform2f(...) QOpenGLContext::currentContext()->functions()->glUniform2f(__VA_ARGS__) + #define glUniform2fv(...) QOpenGLContext::currentContext()->functions()->glUniform2fv(__VA_ARGS__) + #define glUniform2i(...) QOpenGLContext::currentContext()->functions()->glUniform2i(__VA_ARGS__) + #define glUniform2iv(...) QOpenGLContext::currentContext()->functions()->glUniform2iv(__VA_ARGS__) + #define glUniform3f(...) QOpenGLContext::currentContext()->functions()->glUniform3f(__VA_ARGS__) + #define glUniform3fv(...) QOpenGLContext::currentContext()->functions()->glUniform3fv(__VA_ARGS__) + #define glUniform3i(...) QOpenGLContext::currentContext()->functions()->glUniform3i(__VA_ARGS__) + #define glUniform3iv(...) QOpenGLContext::currentContext()->functions()->glUniform3iv(__VA_ARGS__) + #define glUniform4f(...) QOpenGLContext::currentContext()->functions()->glUniform4f(__VA_ARGS__) + #define glUniform4fv(...) QOpenGLContext::currentContext()->functions()->glUniform4fv(__VA_ARGS__) + #define glUniform4i(...) QOpenGLContext::currentContext()->functions()->glUniform4i(__VA_ARGS__) + #define glUniform4iv(...) QOpenGLContext::currentContext()->functions()->glUniform4iv(__VA_ARGS__) + #define glUniformMatrix2fv(...) QOpenGLContext::currentContext()->functions()->glUniformMatrix2fv(__VA_ARGS__) + #define glUniformMatrix3fv(...) QOpenGLContext::currentContext()->functions()->glUniformMatrix3fv(__VA_ARGS__) + #define glUniformMatrix4fv(...) QOpenGLContext::currentContext()->functions()->glUniformMatrix4fv(__VA_ARGS__) + #define glUseProgram(...) QOpenGLContext::currentContext()->functions()->glUseProgram(__VA_ARGS__) + #define glValidateProgram(...) QOpenGLContext::currentContext()->functions()->glValidateProgram(__VA_ARGS__) + #define glVertexAttrib1f(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib1f(__VA_ARGS__) + #define glVertexAttrib1fv(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib1fv(__VA_ARGS__) + #define glVertexAttrib2f(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib2f(__VA_ARGS__) + #define glVertexAttrib2fv(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib2fv(__VA_ARGS__) + #define glVertexAttrib3f(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib3f(__VA_ARGS__) + #define glVertexAttrib3fv(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib3fv(__VA_ARGS__) + #define glVertexAttrib4f(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib4f(__VA_ARGS__) + #define glVertexAttrib4fv(...) QOpenGLContext::currentContext()->functions()->glVertexAttrib4fv(__VA_ARGS__) + #define glVertexAttribPointer(...) QOpenGLContext::currentContext()->functions()->glVertexAttribPointer(__VA_ARGS__) + #define glViewport(...) QOpenGLContext::currentContext()->functions()->glViewport(__VA_ARGS__) +#endif diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 074ef280aa..740bb90202 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -14,7 +14,17 @@ #include #include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include diff --git a/platform/qt/src/qt_conversion.hpp b/platform/qt/src/qt_conversion.hpp index 40d7e5b928..19b0cb54fc 100644 --- a/platform/qt/src/qt_conversion.hpp +++ b/platform/qt/src/qt_conversion.hpp @@ -1,120 +1,145 @@ #pragma once #include -#include +#include #include -#include - -#include #include +#include +#include +#include "qt_geojson.hpp" namespace mbgl { namespace style { namespace conversion { -inline bool isUndefined(const QVariant& value) { - return value.isNull() || !value.isValid(); -} +template <> +class ConversionTraits { +public: + static bool isUndefined(const QVariant& value) { + return value.isNull() || !value.isValid(); + } -inline bool isArray(const QVariant& value) { - return value.canConvert(QVariant::List); -} + static bool isArray(const QVariant& value) { + return value.canConvert(QVariant::List); + } -inline std::size_t arrayLength(const QVariant& value) { - return value.toList().size(); -} + static std::size_t arrayLength(const QVariant& value) { + return value.toList().size(); + } -inline QVariant arrayMember(const QVariant& value, std::size_t i) { - return value.toList()[i]; -} + static QVariant arrayMember(const QVariant& value, std::size_t i) { + return value.toList()[i]; + } -inline bool isObject(const QVariant& value) { - return value.canConvert(QVariant::Map) - || value.type() == QVariant::ByteArray -#if QT_VERSION >= 0x050000 - || QString(value.typeName()) == QStringLiteral("QMapbox::Feature"); -#else - || QString(value.typeName()) == QString("QMapbox::Feature"); -#endif -} + static bool isObject(const QVariant& value) { + return value.canConvert(QVariant::Map) + || value.type() == QVariant::ByteArray + #if QT_VERSION >= 0x050000 + || QString(value.typeName()) == QStringLiteral("QMapbox::Feature"); + #else + || QString(value.typeName()) == QString("QMapbox::Feature"); + #endif + } -inline optional objectMember(const QVariant& value, const char* key) { - auto map = value.toMap(); - auto iter = map.constFind(key); + static optional objectMember(const QVariant& value, const char* key) { + auto map = value.toMap(); + auto iter = map.constFind(key); - if (iter != map.constEnd()) { - return iter.value(); - } else { - return {}; + if (iter != map.constEnd()) { + return iter.value(); + } else { + return {}; + } } -} -using EachMemberFn = std::function(const std::string&, const QVariant&)>; + template + static optional eachMember(const QVariant& value, Fn&& fn) { + auto map = value.toMap(); + auto iter = map.constBegin(); -optional eachMember(const QVariant& value, EachMemberFn&& fn) { - auto map = value.toMap(); - auto iter = map.constBegin(); + while (iter != map.constEnd()) { + optional result = fn(iter.key().toStdString(), QVariant(iter.value())); + if (result) { + return result; + } - while (iter != map.constEnd()) { - optional result = fn(iter.key().toStdString(), iter.value()); - if (result) { - return result; + ++iter; } - ++iter; + return {}; } - return {}; -} + static optional toBool(const QVariant& value) { + if (value.type() == QVariant::Bool) { + return value.toBool(); + } else { + return {}; + } + } -inline optional toBool(const QVariant& value) { - if (value.type() == QVariant::Bool) { - return value.toBool(); - } else { - return {}; + static optional toNumber(const QVariant& value) { + if (value.type() == QVariant::Int || value.type() == QVariant::Double) { + return value.toFloat(); + } else { + return {}; + } } -} -inline optional toNumber(const QVariant& value) { - if (value.type() == QVariant::Int || value.type() == QVariant::Double) { - return value.toFloat(); - } else { - return {}; + static optional toDouble(const QVariant& value) { + if (value.type() == QVariant::Int || value.type() == QVariant::Double) { + return value.toDouble(); + } else { + return {}; + } } -} -inline optional toDouble(const QVariant& value) { - if (value.type() == QVariant::Int || value.type() == QVariant::Double) { - return value.toDouble(); - } else { - return {}; + + static optional toString(const QVariant& value) { + if (value.type() == QVariant::String) { + return value.toString().toStdString(); + } else if (value.type() == QVariant::Color) { + return value.value().name().toStdString(); + } else { + return {}; + } } -} -inline optional toString(const QVariant& value) { - if (value.type() == QVariant::String) { - return value.toString().toStdString(); - } else if (value.type() == QVariant::Color) { - return value.value().name().toStdString(); - } else { - return {}; + static optional toValue(const QVariant& value) { + if (value.type() == QVariant::Bool) { + return { value.toBool() }; + } else if (value.type() == QVariant::String) { + return { value.toString().toStdString() }; + } else if (value.type() == QVariant::Color) { + return { value.value().name().toStdString() }; + } else if (value.type() == QVariant::Int) { + return { int64_t(value.toInt()) }; + } else if (value.canConvert(QVariant::Double)) { + return { value.toDouble() }; + } else { + return {}; + } } -} -inline optional toValue(const QVariant& value) { - if (value.type() == QVariant::Bool) { - return { value.toBool() }; - } else if (value.type() == QVariant::String) { - return { value.toString().toStdString() }; - } else if (value.type() == QVariant::Color) { - return { value.value().name().toStdString() }; - } else if (value.type() == QVariant::Int) { - return { int64_t(value.toInt()) }; - } else if (value.canConvert(QVariant::Double)) { - return { value.toDouble() }; - } else { - return {}; + static optional toGeoJSON(const QVariant& value, Error& error) { + #if QT_VERSION >= 0x050000 + if (value.typeName() == QStringLiteral("QMapbox::Feature")) { + #else + if (value.typeName() == QString("QMapbox::Feature")) { + #endif + return GeoJSON { asMapboxGLFeature(value.value()) }; + } else if (value.type() != QVariant::ByteArray) { + error = { "JSON data must be in QByteArray" }; + return {}; + } + + QByteArray data = value.toByteArray(); + return parseGeoJSON(std::string(data.constData(), data.size()), error); } +}; + +template +optional convert(const QVariant& value, Error& error, Args&&...args) { + return convert(Convertible(value), error, std::forward(args)...); } } // namespace conversion diff --git a/platform/qt/src/qt_geojson.cpp b/platform/qt/src/qt_geojson.cpp new file mode 100644 index 0000000000..80377de64d --- /dev/null +++ b/platform/qt/src/qt_geojson.cpp @@ -0,0 +1,166 @@ +#include "qt_geojson.hpp" +#include +#include +#include + +namespace QMapbox { + +mbgl::Point asMapboxGLPoint(const QMapbox::Coordinate &coordinate) { + return mbgl::Point { coordinate.second, coordinate.first }; +} + +mbgl::MultiPoint asMapboxGLMultiPoint(const QMapbox::Coordinates &multiPoint) { + mbgl::MultiPoint mbglMultiPoint; + mbglMultiPoint.reserve(multiPoint.size()); + for (const auto &point: multiPoint) { + mbglMultiPoint.emplace_back(asMapboxGLPoint(point)); + } + return mbglMultiPoint; +}; + +mbgl::LineString asMapboxGLLineString(const QMapbox::Coordinates &lineString) { + mbgl::LineString mbglLineString; + mbglLineString.reserve(lineString.size()); + for (const auto &coordinate : lineString) { + mbglLineString.emplace_back(asMapboxGLPoint(coordinate)); + } + return mbglLineString; +}; + +mbgl::MultiLineString asMapboxGLMultiLineString(const QMapbox::CoordinatesCollection &multiLineString) { + mbgl::MultiLineString mbglMultiLineString; + mbglMultiLineString.reserve(multiLineString.size()); + for (const auto &lineString : multiLineString) { + mbglMultiLineString.emplace_back(std::forward>(asMapboxGLLineString(lineString))); + } + return mbglMultiLineString; +}; + +mbgl::Polygon asMapboxGLPolygon(const QMapbox::CoordinatesCollection &polygon) { + mbgl::Polygon mbglPolygon; + mbglPolygon.reserve(polygon.size()); + for (const auto &linearRing : polygon) { + mbgl::LinearRing mbglLinearRing; + mbglLinearRing.reserve(linearRing.size()); + for (const auto &coordinate: linearRing) { + mbglLinearRing.emplace_back(asMapboxGLPoint(coordinate)); + } + mbglPolygon.emplace_back(std::move(mbglLinearRing)); + } + return mbglPolygon; +}; + +mbgl::MultiPolygon asMapboxGLMultiPolygon(const QMapbox::CoordinatesCollections &multiPolygon) { + mbgl::MultiPolygon mbglMultiPolygon; + mbglMultiPolygon.reserve(multiPolygon.size()); + for (const auto &polygon : multiPolygon) { + mbglMultiPolygon.emplace_back(std::forward>(asMapboxGLPolygon(polygon))); + } + return mbglMultiPolygon; +}; + +mbgl::Value asMapboxGLPropertyValue(const QVariant &value) { + auto valueList = [](const QVariantList &list) { + std::vector mbglList; + mbglList.reserve(list.size()); + for (const auto& listValue : list) { + mbglList.emplace_back(asMapboxGLPropertyValue(listValue)); + } + return mbglList; + }; + + auto valueMap = [](const QVariantMap &map) { + std::unordered_map mbglMap; + mbglMap.reserve(map.size()); + auto it = map.constBegin(); + while (it != map.constEnd()) { + mbglMap.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value()))); + ++it; + } + return mbglMap; + }; + + switch (value.type()) { +#if QT_VERSION >= 0x050000 + case QMetaType::UnknownType: +#else + case QVariant::Invalid: +#endif + return mbgl::NullValue {}; + case QMetaType::Bool: + return { value.toBool() }; + case QMetaType::ULongLong: + return { uint64_t(value.toULongLong()) }; + case QMetaType::LongLong: + return { int64_t(value.toLongLong()) }; + case QMetaType::Double: + return { value.toDouble() }; + case QMetaType::QString: + return { value.toString().toStdString() }; + case QMetaType::QVariantList: + return valueList(value.toList()); + case QMetaType::QVariantMap: + return valueMap(value.toMap()); + default: + qWarning() << "Unsupported feature property value:" << value; + return {}; + } +} + +mbgl::FeatureIdentifier asMapboxGLFeatureIdentifier(const QVariant &id) { + switch (id.type()) { +#if QT_VERSION >= 0x050000 + case QMetaType::UnknownType: +#else + case QVariant::Invalid: +#endif + return {}; + case QMetaType::ULongLong: + return { uint64_t(id.toULongLong()) }; + case QMetaType::LongLong: + return { int64_t(id.toLongLong()) }; + case QMetaType::Double: + return { id.toDouble() }; + case QMetaType::QString: + return { id.toString().toStdString() }; + default: + qWarning() << "Unsupported feature identifier:" << id; + return {}; + } +} + +mbgl::Feature asMapboxGLFeature(const QMapbox::Feature &feature) { + mbgl::PropertyMap properties; + properties.reserve(feature.properties.size()); + auto it = feature.properties.constBegin(); + while (it != feature.properties.constEnd()) { + properties.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value()))); + } + + mbgl::FeatureIdentifier id = asMapboxGLFeatureIdentifier(feature.id); + + if (feature.type == QMapbox::Feature::PointType) { + const QMapbox::Coordinates &points = feature.geometry.first().first(); + if (points.size() == 1) { + return { asMapboxGLPoint(points.first()), std::move(properties), std::move(id) }; + } else { + return { asMapboxGLMultiPoint(points), std::move(properties), std::move(id) }; + } + } else if (feature.type == QMapbox::Feature::LineStringType) { + const QMapbox::CoordinatesCollection &lineStrings = feature.geometry.first(); + if (lineStrings.size() == 1) { + return { asMapboxGLLineString(lineStrings.first()), std::move(properties), std::move(id) }; + } else { + return { asMapboxGLMultiLineString(lineStrings), std::move(properties), std::move(id) }; + } + } else { // PolygonType + const QMapbox::CoordinatesCollections &polygons = feature.geometry; + if (polygons.size() == 1) { + return { asMapboxGLPolygon(polygons.first()), std::move(properties), std::move(id) }; + } else { + return { asMapboxGLMultiPolygon(polygons), std::move(properties), std::move(id) }; + } + } +}; + +} // namespace QMapbox diff --git a/platform/qt/src/qt_geojson.hpp b/platform/qt/src/qt_geojson.hpp index a6958b7edc..a9c10272ab 100644 --- a/platform/qt/src/qt_geojson.hpp +++ b/platform/qt/src/qt_geojson.hpp @@ -1,7 +1,8 @@ #pragma once #include -#include +#include +#include #include @@ -13,187 +14,14 @@ namespace QMapbox { -mbgl::Point asMapboxGLPoint(const QMapbox::Coordinate &coordinate) { - return mbgl::Point { coordinate.second, coordinate.first }; -} - -mbgl::MultiPoint asMapboxGLMultiPoint(const QMapbox::Coordinates &multiPoint) { - mbgl::MultiPoint mbglMultiPoint; - mbglMultiPoint.reserve(multiPoint.size()); - for (const auto &point: multiPoint) { - mbglMultiPoint.emplace_back(asMapboxGLPoint(point)); - } - return mbglMultiPoint; -}; - -mbgl::LineString asMapboxGLLineString(const QMapbox::Coordinates &lineString) { - mbgl::LineString mbglLineString; - mbglLineString.reserve(lineString.size()); - for (const auto &coordinate : lineString) { - mbglLineString.emplace_back(asMapboxGLPoint(coordinate)); - } - return mbglLineString; -}; - -mbgl::MultiLineString asMapboxGLMultiLineString(const QMapbox::CoordinatesCollection &multiLineString) { - mbgl::MultiLineString mbglMultiLineString; - mbglMultiLineString.reserve(multiLineString.size()); - for (const auto &lineString : multiLineString) { - mbglMultiLineString.emplace_back(std::forward>(asMapboxGLLineString(lineString))); - } - return mbglMultiLineString; -}; - -mbgl::Polygon asMapboxGLPolygon(const QMapbox::CoordinatesCollection &polygon) { - mbgl::Polygon mbglPolygon; - mbglPolygon.reserve(polygon.size()); - for (const auto &linearRing : polygon) { - mbgl::LinearRing mbglLinearRing; - mbglLinearRing.reserve(linearRing.size()); - for (const auto &coordinate: linearRing) { - mbglLinearRing.emplace_back(asMapboxGLPoint(coordinate)); - } - mbglPolygon.emplace_back(std::move(mbglLinearRing)); - } - return mbglPolygon; -}; - -mbgl::MultiPolygon asMapboxGLMultiPolygon(const QMapbox::CoordinatesCollections &multiPolygon) { - mbgl::MultiPolygon mbglMultiPolygon; - mbglMultiPolygon.reserve(multiPolygon.size()); - for (const auto &polygon : multiPolygon) { - mbglMultiPolygon.emplace_back(std::forward>(asMapboxGLPolygon(polygon))); - } - return mbglMultiPolygon; -}; - -mbgl::Value asMapboxGLPropertyValue(const QVariant &value) { - auto valueList = [](const QVariantList &list) { - std::vector mbglList; - mbglList.reserve(list.size()); - for (const auto& listValue : list) { - mbglList.emplace_back(asMapboxGLPropertyValue(listValue)); - } - return mbglList; - }; - - auto valueMap = [](const QVariantMap &map) { - std::unordered_map mbglMap; - mbglMap.reserve(map.size()); - auto it = map.constBegin(); - while (it != map.constEnd()) { - mbglMap.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value()))); - ++it; - } - return mbglMap; - }; - - switch (value.type()) { -#if QT_VERSION >= 0x050000 - case QMetaType::UnknownType: -#else - case QVariant::Invalid: -#endif - return mbgl::NullValue {}; - case QMetaType::Bool: - return { value.toBool() }; - case QMetaType::ULongLong: - return { uint64_t(value.toULongLong()) }; - case QMetaType::LongLong: - return { int64_t(value.toLongLong()) }; - case QMetaType::Double: - return { value.toDouble() }; - case QMetaType::QString: - return { value.toString().toStdString() }; - case QMetaType::QVariantList: - return valueList(value.toList()); - case QMetaType::QVariantMap: - return valueMap(value.toMap()); - default: - qWarning() << "Unsupported feature property value:" << value; - return {}; - } -} - -mbgl::FeatureIdentifier asMapboxGLFeatureIdentifier(const QVariant &id) { - switch (id.type()) { -#if QT_VERSION >= 0x050000 - case QMetaType::UnknownType: -#else - case QVariant::Invalid: -#endif - return {}; - case QMetaType::ULongLong: - return { uint64_t(id.toULongLong()) }; - case QMetaType::LongLong: - return { int64_t(id.toLongLong()) }; - case QMetaType::Double: - return { id.toDouble() }; - case QMetaType::QString: - return { id.toString().toStdString() }; - default: - qWarning() << "Unsupported feature identifier:" << id; - return {}; - } -} - -mbgl::Feature asMapboxGLFeature(const QMapbox::Feature &feature) { - mbgl::PropertyMap properties; - properties.reserve(feature.properties.size()); - auto it = feature.properties.constBegin(); - while (it != feature.properties.constEnd()) { - properties.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value()))); - } - - mbgl::FeatureIdentifier id = asMapboxGLFeatureIdentifier(feature.id); - - if (feature.type == QMapbox::Feature::PointType) { - const QMapbox::Coordinates &points = feature.geometry.first().first(); - if (points.size() == 1) { - return { asMapboxGLPoint(points.first()), std::move(properties), std::move(id) }; - } else { - return { asMapboxGLMultiPoint(points), std::move(properties), std::move(id) }; - } - } else if (feature.type == QMapbox::Feature::LineStringType) { - const QMapbox::CoordinatesCollection &lineStrings = feature.geometry.first(); - if (lineStrings.size() == 1) { - return { asMapboxGLLineString(lineStrings.first()), std::move(properties), std::move(id) }; - } else { - return { asMapboxGLMultiLineString(lineStrings), std::move(properties), std::move(id) }; - } - } else { // PolygonType - const QMapbox::CoordinatesCollections &polygons = feature.geometry; - if (polygons.size() == 1) { - return { asMapboxGLPolygon(polygons.first()), std::move(properties), std::move(id) }; - } else { - return { asMapboxGLMultiPolygon(polygons), std::move(properties), std::move(id) }; - } - } -}; +mbgl::Point asMapboxGLPoint(const QMapbox::Coordinate &coordinate); +mbgl::MultiPoint asMapboxGLMultiPoint(const QMapbox::Coordinates &multiPoint); +mbgl::LineString asMapboxGLLineString(const QMapbox::Coordinates &lineString); +mbgl::MultiLineString asMapboxGLMultiLineString(const QMapbox::CoordinatesCollection &multiLineString); +mbgl::Polygon asMapboxGLPolygon(const QMapbox::CoordinatesCollection &polygon); +mbgl::MultiPolygon asMapboxGLMultiPolygon(const QMapbox::CoordinatesCollections &multiPolygon); +mbgl::Value asMapboxGLPropertyValue(const QVariant &value); +mbgl::FeatureIdentifier asMapboxGLFeatureIdentifier(const QVariant &id); +mbgl::Feature asMapboxGLFeature(const QMapbox::Feature &feature); } // namespace QMapbox - -namespace mbgl { -namespace style { -namespace conversion { - -template <> -optional Converter::operator()(const QVariant& value, Error& error) const { -#if QT_VERSION >= 0x050000 - if (value.typeName() == QStringLiteral("QMapbox::Feature")) { -#else - if (value.typeName() == QString("QMapbox::Feature")) { -#endif - return GeoJSON { asMapboxGLFeature(value.value()) }; - } else if (value.type() != QVariant::ByteArray) { - error = { "JSON data must be in QByteArray" }; - return {}; - } - - QByteArray data = value.toByteArray(); - return convert(std::string(data.constData(), data.size()), error); -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index 34fb576727..ba80be0da0 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -38,7 +38,9 @@ void RenderAnnotationSource::update(Immutable baseImpl_, parameters, SourceType::Annotations, util::tileSize, - { 0, util::DEFAULT_MAX_ZOOM }, + // Zoom level 16 is typically sufficient for annotations. + // See https://github.com/mapbox/mapbox-gl-native/issues/10197 + { 0, 16 }, [&] (const OverscaledTileID& tileID) { return std::make_unique(tileID, parameters); }); diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index d0c538efb0..e04f134f39 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -248,7 +248,14 @@ UniqueTexture Context::createTexture() { } bool Context::supportsVertexArrays() const { - return vertexArray && + static bool blacklisted = []() { + // Blacklist Adreno 3xx as it crashes on glBuffer(Sub)Data + const std::string renderer = reinterpret_cast(glGetString(GL_RENDERER)); + return renderer.find("Adreno (TM) 3") != std::string::npos; + }(); + + return !blacklisted && + vertexArray && vertexArray->genVertexArrays && vertexArray->bindVertexArray && vertexArray->deleteVertexArrays; @@ -583,13 +590,14 @@ void Context::setDirtyState() { void Context::clear(optional color, optional depth, - optional stencil) { + optional stencil, + optional colorMask_) { GLbitfield mask = 0; if (color) { mask |= GL_COLOR_BUFFER_BIT; clearColor = *color; - colorMask = { true, true, true, true }; + colorMask = colorMask_ ? *colorMask_ : value::ColorMask::Default; } if (depth) { @@ -605,6 +613,10 @@ void Context::clear(optional color, } MBGL_CHECK_ERROR(glClear(mask)); + + if (colorMask_) { + colorMask = value::ColorMask::Default; + } } #if not MBGL_USE_GLES2 diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 528113cbba..2d650e7ecb 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -146,7 +146,8 @@ public: void clear(optional color, optional depth, - optional stencil); + optional stencil, + optional colorMask = value::ColorMask::Default); void setDrawMode(const Points&); void setDrawMode(const Lines&); diff --git a/src/mbgl/gl/gl.hpp b/src/mbgl/gl/gl.hpp index 3e21731330..976b7d2f74 100644 --- a/src/mbgl/gl/gl.hpp +++ b/src/mbgl/gl/gl.hpp @@ -1,36 +1,10 @@ #pragma once +#include + #include #include -#if __APPLE__ - #include "TargetConditionals.h" - #if TARGET_OS_IPHONE - #include - #include - #elif TARGET_IPHONE_SIMULATOR - #include - #include - #elif TARGET_OS_MAC - #include - #include - #include - #else - #error Unsupported Apple platform - #endif -#elif __ANDROID__ || MBGL_USE_GLES2 - #define GL_GLEXT_PROTOTYPES - #include - #include -#elif __QT__ && QT_VERSION >= 0x050000 - #define GL_GLEXT_PROTOTYPES - #include -#else - #define GL_GLEXT_PROTOTYPES - #include - #include -#endif - namespace mbgl { namespace gl { diff --git a/src/mbgl/map/update.hpp b/src/mbgl/map/update.hpp deleted file mode 100644 index 057720a5c9..0000000000 --- a/src/mbgl/map/update.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include - -namespace mbgl { - -enum class Update { - Nothing = 0, - Repaint = 1 << 0, - AnnotationData = 1 << 7 -}; - -MBGL_CONSTEXPR Update operator|(Update lhs, Update rhs) { - return Update(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs)); -} - -MBGL_CONSTEXPR Update& operator|=(Update& lhs, const Update& rhs) { - return (lhs = lhs | rhs); -} - -MBGL_CONSTEXPR bool operator& (Update lhs, Update rhs) { - return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index 4a2c2c6f12..60f5af4e9a 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp deleted file mode 100644 index 4bf5629263..0000000000 --- a/src/mbgl/renderer/render_item.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -#include -#include - -namespace mbgl { - -class RenderLayer; -class RenderSource; -class RenderTile; -class Bucket; - -namespace style { -} // namespace style - -class RenderItem { -public: - RenderItem(RenderLayer& layer_, - RenderSource* renderSource_) - : layer(layer_), source(renderSource_) { - } - - RenderLayer& layer; - RenderSource* source; -}; - -class RenderData { -public: - Color backgroundColor; - std::unordered_set sources; - std::vector order; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_style.cpp b/src/mbgl/renderer/render_style.cpp deleted file mode 100644 index 3d95b12bc4..0000000000 --- a/src/mbgl/renderer/render_style.cpp +++ /dev/null @@ -1,449 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mbgl { - -using namespace style; - -RenderStyleObserver nullObserver; - -RenderStyle::RenderStyle(Scheduler& scheduler_, FileSource& fileSource_) - : scheduler(scheduler_), - fileSource(fileSource_), - glyphManager(std::make_unique(fileSource)), - imageManager(std::make_unique()), - lineAtlas(std::make_unique(Size{ 256, 512 })), - imageImpls(makeMutable>>()), - sourceImpls(makeMutable>>()), - layerImpls(makeMutable>>()), - renderLight(makeMutable()), - observer(&nullObserver) { - glyphManager->setObserver(this); -} - -RenderStyle::~RenderStyle() { - assert(BackendScope::exists()); // Required for custom layers. -} - -void RenderStyle::setObserver(RenderStyleObserver* observer_) { - observer = observer_; -} - -std::vector RenderStyle::getRenderLayers() const { - std::vector result; - result.reserve(renderLayers.size()); - for (const auto& layer : *layerImpls) { - result.push_back(getRenderLayer(layer->id)); - } - return result; -} - -RenderLayer* RenderStyle::getRenderLayer(const std::string& id) { - auto it = renderLayers.find(id); - return it != renderLayers.end() ? it->second.get() : nullptr; -} - -const RenderLayer* RenderStyle::getRenderLayer(const std::string& id) const { - auto it = renderLayers.find(id); - return it != renderLayers.end() ? it->second.get() : nullptr; -} - -const RenderLight& RenderStyle::getRenderLight() const { - return renderLight; -} - -void RenderStyle::update(const UpdateParameters& parameters) { - assert(BackendScope::exists()); // Required for custom layers. - - const bool zoomChanged = zoomHistory.update(parameters.transformState.getZoom(), parameters.timePoint); - - const TransitionParameters transitionParameters { - parameters.timePoint, - parameters.mode == MapMode::Continuous ? parameters.transitionOptions : TransitionOptions() - }; - - const PropertyEvaluationParameters evaluationParameters { - zoomHistory, - parameters.timePoint, - parameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero() - }; - - const TileParameters tileParameters { - parameters.pixelRatio, - parameters.debugOptions, - parameters.transformState, - parameters.scheduler, - parameters.fileSource, - parameters.mode, - parameters.annotationManager, - *imageManager, - *glyphManager, - parameters.prefetchZoomDelta - }; - - glyphManager->setURL(parameters.glyphURL); - - // Update light. - const bool lightChanged = renderLight.impl != parameters.light; - - if (lightChanged) { - renderLight.impl = parameters.light; - renderLight.transition(transitionParameters); - } - - if (lightChanged || zoomChanged || renderLight.hasTransition()) { - renderLight.evaluate(evaluationParameters); - } - - - const ImageDifference imageDiff = diffImages(imageImpls, parameters.images); - imageImpls = parameters.images; - - // Remove removed images from sprite atlas. - for (const auto& entry : imageDiff.removed) { - imageManager->removeImage(entry.first); - } - - // Add added images to sprite atlas. - for (const auto& entry : imageDiff.added) { - imageManager->addImage(entry.second); - } - - // Update changed images. - for (const auto& entry : imageDiff.changed) { - imageManager->updateImage(entry.second.after); - } - - imageManager->setLoaded(parameters.spriteLoaded); - - - const LayerDifference layerDiff = diffLayers(layerImpls, parameters.layers); - layerImpls = parameters.layers; - - // Remove render layers for removed layers. - for (const auto& entry : layerDiff.removed) { - renderLayers.erase(entry.first); - } - - // Create render layers for newly added layers. - for (const auto& entry : layerDiff.added) { - renderLayers.emplace(entry.first, RenderLayer::create(entry.second)); - } - - // Update render layers for changed layers. - for (const auto& entry : layerDiff.changed) { - renderLayers.at(entry.first)->setImpl(entry.second.after); - } - - // Update layers for class and zoom changes. - for (const auto& entry : renderLayers) { - RenderLayer& layer = *entry.second; - const bool layerAdded = layerDiff.added.count(entry.first); - const bool layerChanged = layerDiff.changed.count(entry.first); - - if (layerAdded || layerChanged) { - layer.transition(transitionParameters); - } - - if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) { - layer.evaluate(evaluationParameters); - } - } - - - const SourceDifference sourceDiff = diffSources(sourceImpls, parameters.sources); - sourceImpls = parameters.sources; - - // Remove render layers for removed sources. - for (const auto& entry : sourceDiff.removed) { - renderSources.erase(entry.first); - } - - // Create render sources for newly added sources. - for (const auto& entry : sourceDiff.added) { - std::unique_ptr renderSource = RenderSource::create(entry.second); - renderSource->setObserver(this); - renderSources.emplace(entry.first, std::move(renderSource)); - } - - // Update all sources. - for (const auto& source : *sourceImpls) { - std::vector> filteredLayers; - bool needsRendering = false; - bool needsRelayout = false; - - for (const auto& layer : *layerImpls) { - if (layer->type == LayerType::Background || - layer->type == LayerType::Custom || - layer->source != source->id) { - continue; - } - - if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) { - needsRendering = true; - } - - if (!needsRelayout && ( - hasLayoutDifference(layerDiff, layer->id) || - !imageDiff.added.empty() || - !imageDiff.removed.empty() || - !imageDiff.changed.empty())) { - needsRelayout = true; - } - - filteredLayers.push_back(layer); - } - - renderSources.at(source->id)->update(source, - filteredLayers, - needsRendering, - needsRelayout, - tileParameters); - } -} - -RenderSource* RenderStyle::getRenderSource(const std::string& id) const { - auto it = renderSources.find(id); - return it != renderSources.end() ? it->second.get() : nullptr; -} - -bool RenderStyle::hasTransitions() const { - if (renderLight.hasTransition()) { - return true; - } - - for (const auto& entry : renderLayers) { - if (entry.second->hasTransition()) { - return true; - } - } - - return false; -} - -bool RenderStyle::isLoaded() const { - for (const auto& entry: renderSources) { - if (!entry.second->isLoaded()) { - return false; - } - } - - if (!imageManager->isLoaded()) { - return false; - } - - return true; -} - -RenderData RenderStyle::getRenderData(MapDebugOptions debugOptions, float angle) { - RenderData result; - - for (const auto& entry : renderSources) { - if (entry.second->isEnabled()) { - result.sources.insert(entry.second.get()); - } - } - - for (auto& layerImpl : *layerImpls) { - RenderLayer* layer = getRenderLayer(layerImpl->id); - assert(layer); - - if (!layer->needsRendering(zoomHistory.lastZoom)) { - continue; - } - - if (const RenderBackgroundLayer* background = layer->as()) { - if (debugOptions & MapDebugOptions::Overdraw) { - // We want to skip glClear optimization in overdraw mode. - result.order.emplace_back(*layer, nullptr); - continue; - } - const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated; - if (layerImpl.get() == layerImpls->at(0).get() && paint.get().from.empty()) { - // This is a solid background. We can use glClear(). - result.backgroundColor = paint.get() * paint.get(); - } else { - // This is a textured background, or not the bottommost layer. We need to render it with a quad. - result.order.emplace_back(*layer, nullptr); - } - continue; - } - - if (layer->is()) { - result.order.emplace_back(*layer, nullptr); - continue; - } - - RenderSource* source = getRenderSource(layer->baseImpl->source); - if (!source) { - Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str()); - continue; - } - - const bool symbolLayer = layer->is(); - - auto sortedTiles = source->getRenderTiles(); - if (symbolLayer) { - // Sort symbol tiles in opposite y position, so tiles with overlapping symbols are drawn - // on top of each other, with lower symbols being drawn on top of higher symbols. - std::sort(sortedTiles.begin(), sortedTiles.end(), - [angle](const RenderTile& a, const RenderTile& b) { - Point pa(a.id.canonical.x, a.id.canonical.y); - Point pb(b.id.canonical.x, b.id.canonical.y); - - auto par = util::rotate(pa, angle); - auto pbr = util::rotate(pb, angle); - - return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x); - }); - } else { - std::sort(sortedTiles.begin(), sortedTiles.end(), - [](const auto& a, const auto& b) { return a.get().id < b.get().id; }); - } - - std::vector> sortedTilesForInsertion; - for (auto& sortedTile : sortedTiles) { - auto& tile = sortedTile.get(); - if (!tile.tile.isRenderable()) { - continue; - } - - // We're not clipping symbol layers, so when we have both parents and children of symbol - // layers, we drop all children in favor of their parent to avoid duplicate labels. - // See https://github.com/mapbox/mapbox-gl-native/issues/2482 - if (symbolLayer) { - bool skip = false; - // Look back through the buckets we decided to render to find out whether there is - // already a bucket from this layer that is a parent of this tile. Tiles are ordered - // by zoom level when we obtain them from getTiles(). - for (auto it = sortedTilesForInsertion.rbegin(); - it != sortedTilesForInsertion.rend(); ++it) { - if (tile.tile.id.isChildOf(it->get().tile.id)) { - skip = true; - break; - } - } - if (skip) { - continue; - } - } - - auto bucket = tile.tile.getBucket(*layer->baseImpl); - if (bucket) { - sortedTilesForInsertion.emplace_back(tile); - tile.used = true; - } - } - layer->setRenderTiles(std::move(sortedTilesForInsertion)); - result.order.emplace_back(*layer, source); - } - - return result; -} - -std::vector RenderStyle::queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const { - std::unordered_map> resultsByLayer; - - if (options.layerIDs) { - std::unordered_set sourceIDs; - for (const auto& layerID : *options.layerIDs) { - if (const RenderLayer* layer = getRenderLayer(layerID)) { - sourceIDs.emplace(layer->baseImpl->source); - } - } - for (const auto& sourceID : sourceIDs) { - if (RenderSource* renderSource = getRenderSource(sourceID)) { - auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, *this, options); - std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); - } - } - } else { - for (const auto& entry : renderSources) { - auto sourceResults = entry.second->queryRenderedFeatures(geometry, transformState, *this, options); - std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); - } - } - - std::vector result; - - if (resultsByLayer.empty()) { - return result; - } - - // Combine all results based on the style layer order. - for (const auto& layerImpl : *layerImpls) { - const RenderLayer* layer = getRenderLayer(layerImpl->id); - if (!layer->needsRendering(zoomHistory.lastZoom)) { - continue; - } - auto it = resultsByLayer.find(layer->baseImpl->id); - if (it != resultsByLayer.end()) { - std::move(it->second.begin(), it->second.end(), std::back_inserter(result)); - } - } - - return result; -} - -void RenderStyle::onLowMemory() { - for (const auto& entry : renderSources) { - entry.second->onLowMemory(); - } -} - -void RenderStyle::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) { - Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s", - glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str()); - observer->onResourceError(error); -} - -void RenderStyle::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) { - Log::Error(Event::Style, "Failed to load tile %s for source %s: %s", - util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str()); - observer->onResourceError(error); -} - -void RenderStyle::onTileChanged(RenderSource&, const OverscaledTileID&) { - observer->onInvalidate(); -} - -void RenderStyle::dumpDebugLogs() const { - for (const auto& entry : renderSources) { - entry.second->dumpDebugLogs(); - } - - imageManager->dumpDebugLogs(); -} - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_style.hpp b/src/mbgl/renderer/render_style.hpp deleted file mode 100644 index 23a640c482..0000000000 --- a/src/mbgl/renderer/render_style.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace mbgl { - -class FileSource; -class GlyphManager; -class ImageManager; -class LineAtlas; -class RenderData; -class TransformState; -class RenderedQueryOptions; -class Scheduler; -class UpdateParameters; -class RenderStyleObserver; - -namespace style { -class Image; -class Source; -class Layer; -} // namespace style - -class RenderStyle : public GlyphManagerObserver, - public RenderSourceObserver { -public: - RenderStyle(Scheduler&, FileSource&); - ~RenderStyle() final; - - void setObserver(RenderStyleObserver*); - void update(const UpdateParameters&); - - bool isLoaded() const; - bool hasTransitions() const; - - RenderSource* getRenderSource(const std::string& id) const; - - std::vector getRenderLayers() const; - - RenderLayer* getRenderLayer(const std::string& id); - const RenderLayer* getRenderLayer(const std::string& id) const; - - const RenderLight& getRenderLight() const; - - RenderData getRenderData(MapDebugOptions, float angle); - - std::vector queryRenderedFeatures(const ScreenLineString& geometry, - const TransformState& transformState, - const RenderedQueryOptions& options) const; - - void onLowMemory(); - - void dumpDebugLogs() const; - - Scheduler& scheduler; - FileSource& fileSource; - std::unique_ptr glyphManager; - std::unique_ptr imageManager; - std::unique_ptr lineAtlas; - -private: - Immutable>> imageImpls; - Immutable>> sourceImpls; - Immutable>> layerImpls; - - std::unordered_map> renderSources; - std::unordered_map> renderLayers; - RenderLight renderLight; - - // GlyphManagerObserver implementation. - void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override; - - // RenderSourceObserver implementation. - void onTileChanged(RenderSource&, const OverscaledTileID&) override; - void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; - - RenderStyleObserver* observer; - ZoomHistory zoomHistory; -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/render_style_observer.hpp b/src/mbgl/renderer/render_style_observer.hpp deleted file mode 100644 index 5852dd68b8..0000000000 --- a/src/mbgl/renderer/render_style_observer.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -namespace mbgl { - -class RenderStyleObserver { -public: - virtual ~RenderStyleObserver() = default; - virtual void onInvalidate() {} - virtual void onResourceError(std::exception_ptr) {} -}; - -} // namespace mbgl diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 6a8c18792e..d7886ebe88 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -298,7 +298,9 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { if (const RenderBackgroundLayer* background = layer->as()) { const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated; - if (layerImpl.get() == layerImpls->at(0).get() && paint.get().from.empty()) { + if (parameters.contextMode == GLContextMode::Unique + && layerImpl.get() == layerImpls->at(0).get() + && paint.get().from.empty()) { // This is a solid background. We can use glClear(). backgroundColor = paint.get() * paint.get(); } else { @@ -434,13 +436,19 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any // tiles whatsoever. { + using namespace gl::value; + MBGL_DEBUG_GROUP(parameters.context, "clear"); parameters.backend.bind(); - parameters.context.clear((parameters.debugOptions & MapDebugOptions::Overdraw) - ? Color::black() - : backgroundColor, - 1.0f, - 0); + if (parameters.debugOptions & MapDebugOptions::Overdraw) { + parameters.context.clear(Color::black(), ClearDepth::Default, ClearStencil::Default); + } else if (parameters.contextMode == GLContextMode::Shared) { + // Preserve the shared context background colors, clearing only alpha. + optional mask = { { false, false, false, true } }; + parameters.context.clear(backgroundColor, ClearDepth::Default, ClearStencil::Default, mask); + } else { + parameters.context.clear(backgroundColor, ClearDepth::Default, ClearStencil::Default); + } } // - CLIPPING MASKS ---------------------------------------------------------------------------- diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 30e7f70722..043253a834 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -9,7 +10,6 @@ #include #include #include -#include #include #include diff --git a/src/mbgl/storage/file_source_request.cpp b/src/mbgl/storage/file_source_request.cpp deleted file mode 100644 index 09ea8cc32a..0000000000 --- a/src/mbgl/storage/file_source_request.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#include -#include - -namespace mbgl { - -FileSourceRequest::FileSourceRequest(FileSource::Callback&& callback) - : responseCallback(callback) - , mailbox(std::make_shared(*Scheduler::GetCurrent())) { -} - -FileSourceRequest::~FileSourceRequest() { - if (cancelCallback) { - cancelCallback(); - } - - mailbox->close(); -} - -void FileSourceRequest::onCancel(std::function&& callback) { - cancelCallback = std::move(callback); -} - -void FileSourceRequest::setResponse(const Response& response) { - // Copy, because calling the callback will sometimes self - // destroy this object. We cannot move because this method - // can be called more than one. - auto callback = responseCallback; - callback(response); -} - -ActorRef FileSourceRequest::actor() { - return ActorRef(*this, mailbox); -} - -} // namespace mbgl diff --git a/src/mbgl/storage/file_source_request.hpp b/src/mbgl/storage/file_source_request.hpp deleted file mode 100644 index 6bd0d44df6..0000000000 --- a/src/mbgl/storage/file_source_request.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -namespace mbgl { - -class Mailbox; - -class FileSourceRequest : public AsyncRequest { -public: - FileSourceRequest(FileSource::Callback&& callback); - ~FileSourceRequest() final; - - void onCancel(std::function&& callback); - void setResponse(const Response& res); - - ActorRef actor(); - -private: - FileSource::Callback responseCallback = nullptr; - std::function cancelCallback = nullptr; - - std::shared_ptr mailbox; -}; - -} // namespace mbgl diff --git a/src/mbgl/style/conversion/constant.cpp b/src/mbgl/style/conversion/constant.cpp new file mode 100644 index 0000000000..e837c4e70b --- /dev/null +++ b/src/mbgl/style/conversion/constant.cpp @@ -0,0 +1,94 @@ +#include + +namespace mbgl { +namespace style { +namespace conversion { + +optional Converter::operator()(const Convertible& value, Error& error) const { + optional converted = toBool(value); + if (!converted) { + error = { "value must be a boolean" }; + return {}; + } + return *converted; +} + +optional Converter::operator()(const Convertible& value, Error& error) const { + optional converted = toNumber(value); + if (!converted) { + error = { "value must be a number" }; + return {}; + } + return *converted; +} + +optional Converter::operator()(const Convertible& value, Error& error) const { + optional converted = toString(value); + if (!converted) { + error = { "value must be a string" }; + return {}; + } + return *converted; +} + +optional Converter::operator()(const Convertible& value, Error& error) const { + optional string = toString(value); + if (!string) { + error = { "value must be a string" }; + return {}; + } + + optional color = Color::parse(*string); + if (!color) { + error = { "value must be a valid color" }; + return {}; + } + + return *color; +} + +optional> Converter>::operator()(const Convertible& value, Error& error) const { + if (!isArray(value)) { + error = { "value must be an array" }; + return {}; + } + + std::vector result; + result.reserve(arrayLength(value)); + + for (std::size_t i = 0; i < arrayLength(value); ++i) { + optional number = toNumber(arrayMember(value, i)); + if (!number) { + error = { "value must be an array of numbers" }; + return {}; + } + result.push_back(*number); + } + + return result; +} + +optional> Converter>::operator()(const Convertible& value, Error& error) const { + if (!isArray(value)) { + error = { "value must be an array" }; + return {}; + } + + std::vector result; + result.reserve(arrayLength(value)); + + for (std::size_t i = 0; i < arrayLength(value); ++i) { + optional string = toString(arrayMember(value, i)); + if (!string) { + error = { "value must be an array of strings" }; + return {}; + } + result.push_back(*string); + } + + return result; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/coordinate.cpp b/src/mbgl/style/conversion/coordinate.cpp new file mode 100644 index 0000000000..9b2be3381e --- /dev/null +++ b/src/mbgl/style/conversion/coordinate.cpp @@ -0,0 +1,29 @@ +#include + +namespace mbgl { +namespace style { +namespace conversion { + +optional Converter::operator() (const Convertible& value, Error& error) const { + if (!isArray(value) || arrayLength(value) < 2 ) { + error = { "coordinate array must contain numeric longitude and latitude values" }; + return {}; + } + //Style spec uses GeoJSON convention for specifying coordinates + optional latitude = toDouble(arrayMember(value, 1)); + optional longitude = toDouble(arrayMember(value, 0)); + + if (!latitude || !longitude) { + error = { "coordinate array must contain numeric longitude and latitude values" }; + return {}; + } + if (*latitude < -90 || *latitude > 90 ){ + error = { "coordinate latitude must be between -90 and 90" }; + return {}; + } + return LatLng(*latitude, *longitude); +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp new file mode 100644 index 0000000000..bb7bb6ea98 --- /dev/null +++ b/src/mbgl/style/conversion/filter.cpp @@ -0,0 +1,248 @@ +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +static optional normalizeValue(const optional& value, Error& error) { + if (!value) { + error = { "filter expression value must be a boolean, number, or string" }; + return {}; + } else { + return *value; + } +} + +static optional toFeatureType(const Convertible& value, Error& error) { + optional type = toString(value); + if (!type) { + error = { "value for $type filter must be a string" }; + return {}; + } else if (*type == "Point") { + return FeatureType::Point; + } else if (*type == "LineString") { + return FeatureType::LineString; + } else if (*type == "Polygon") { + return FeatureType::Polygon; + } else { + error = { "value for $type filter must be Point, LineString, or Polygon" }; + return {}; + } +} + +static optional toFeatureIdentifier(const Convertible& value, Error& error) { + optional identifier = toValue(value); + if (!identifier) { + error = { "filter expression value must be a boolean, number, or string" }; + return {}; + } else { + return (*identifier).match( + [] (uint64_t t) -> optional { return { t }; }, + [] ( int64_t t) -> optional { return { t }; }, + [] ( double t) -> optional { return { t }; }, + [] (const std::string& t) -> optional { return { t }; }, + [&] (const auto&) -> optional { + error = { "filter expression value must be a boolean, number, or string" }; + return {}; + }); + } +} + +template +optional convertUnaryFilter(const Convertible& value, Error& error) { + if (arrayLength(value) < 2) { + error = { "filter expression must have 2 elements" }; + return {}; + } + + optional key = toString(arrayMember(value, 1)); + if (!key) { + error = { "filter expression key must be a string" }; + return {}; + } + + if (*key == "$id") { + return { IdentifierFilterType {} }; + } else { + return { FilterType { *key } }; + } +} + +template +optional convertEqualityFilter(const Convertible& value, Error& error) { + if (arrayLength(value) < 3) { + error = { "filter expression must have 3 elements" }; + return {}; + } + + optional key = toString(arrayMember(value, 1)); + if (!key) { + error = { "filter expression key must be a string" }; + return {}; + } + + if (*key == "$type") { + optional filterValue = toFeatureType(arrayMember(value, 2), error); + if (!filterValue) { + return {}; + } + + return { TypeFilterType { *filterValue } }; + + } else if (*key == "$id") { + optional filterValue = toFeatureIdentifier(arrayMember(value, 2), error); + if (!filterValue) { + return {}; + } + + return { IdentifierFilterType { *filterValue } }; + + } else { + optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); + if (!filterValue) { + return {}; + } + + return { FilterType { *key, *filterValue } }; + } +} + +template +optional convertBinaryFilter(const Convertible& value, Error& error) { + if (arrayLength(value) < 3) { + error = { "filter expression must have 3 elements" }; + return {}; + } + + optional key = toString(arrayMember(value, 1)); + if (!key) { + error = { "filter expression key must be a string" }; + return {}; + } + + optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); + if (!filterValue) { + return {}; + } + + return { FilterType { *key, *filterValue } }; +} + +template +optional convertSetFilter(const Convertible& value, Error& error) { + if (arrayLength(value) < 2) { + error = { "filter expression must at least 2 elements" }; + return {}; + } + + optional key = toString(arrayMember(value, 1)); + if (!key) { + error = { "filter expression key must be a string" }; + return {}; + } + + if (*key == "$type") { + std::vector values; + for (std::size_t i = 2; i < arrayLength(value); ++i) { + optional filterValue = toFeatureType(arrayMember(value, i), error); + if (!filterValue) { + return {}; + } + values.push_back(*filterValue); + } + + return { TypeFilterType { std::move(values) } }; + + } else if (*key == "$id") { + std::vector values; + for (std::size_t i = 2; i < arrayLength(value); ++i) { + optional filterValue = toFeatureIdentifier(arrayMember(value, i), error); + if (!filterValue) { + return {}; + } + values.push_back(*filterValue); + } + + return { IdentifierFilterType { std::move(values) } }; + + } else { + std::vector values; + for (std::size_t i = 2; i < arrayLength(value); ++i) { + optional filterValue = normalizeValue(toValue(arrayMember(value, i)), error); + if (!filterValue) { + return {}; + } + values.push_back(*filterValue); + } + + return { FilterType { *key, std::move(values) } }; + } +} + +template +optional convertCompoundFilter(const Convertible& value, Error& error) { + std::vector filters; + for (std::size_t i = 1; i < arrayLength(value); ++i) { + optional element = convert(arrayMember(value, i), error); + if (!element) { + return {}; + } + filters.push_back(*element); + } + + return { FilterType { std::move(filters) } }; +} + +optional Converter::operator()(const Convertible& value, Error& error) const { + if (!isArray(value)) { + error = { "filter expression must be an array" }; + return {}; + } + + if (arrayLength(value) < 1) { + error = { "filter expression must have at least 1 element" }; + return {}; + } + + optional op = toString(arrayMember(value, 0)); + if (!op) { + error = { "filter operator must be a string" }; + return {}; + } + + if (*op == "==") { + return convertEqualityFilter(value, error); + } else if (*op == "!=") { + return convertEqualityFilter(value, error); + } else if (*op == ">") { + return convertBinaryFilter(value, error); + } else if (*op == ">=") { + return convertBinaryFilter(value, error); + } else if (*op == "<") { + return convertBinaryFilter(value, error); + } else if (*op == "<=") { + return convertBinaryFilter(value, error); + } else if (*op == "in") { + return convertSetFilter(value, error); + } else if (*op == "!in") { + return convertSetFilter(value, error); + } else if (*op == "all") { + return convertCompoundFilter(value, error); + } else if (*op == "any") { + return convertCompoundFilter(value, error); + } else if (*op == "none") { + return convertCompoundFilter(value, error); + } else if (*op == "has") { + return convertUnaryFilter(value, error); + } else if (*op == "!has") { + return convertUnaryFilter(value, error); + } + + error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" }; + return {}; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/geojson.cpp b/src/mbgl/style/conversion/geojson.cpp index 8103e9014a..e39a1a80eb 100644 --- a/src/mbgl/style/conversion/geojson.cpp +++ b/src/mbgl/style/conversion/geojson.cpp @@ -1,26 +1,16 @@ #include #include -#include - -#include -#include namespace mbgl { namespace style { namespace conversion { -optional Converter::operator()(const std::string& value, Error& error) const { - return convertJSON(value, error); +optional Converter::operator()(const Convertible& value, Error& error) const { + return toGeoJSON(value, error); } -template <> -optional Converter::operator()(const JSValue& value, Error& error) const { - try { - return mapbox::geojson::convert(value); - } catch (const std::exception& ex) { - error = { ex.what() }; - return {}; - } +optional parseGeoJSON(const std::string& value, Error& error) { + return convertJSON(value, error); } } // namespace conversion diff --git a/src/mbgl/style/conversion/geojson_options.cpp b/src/mbgl/style/conversion/geojson_options.cpp new file mode 100644 index 0000000000..a2c5ed8816 --- /dev/null +++ b/src/mbgl/style/conversion/geojson_options.cpp @@ -0,0 +1,85 @@ +#include + +namespace mbgl { +namespace style { +namespace conversion { + +optional Converter::operator()(const Convertible& value, Error& error) const { + GeoJSONOptions options; + + const auto minzoomValue = objectMember(value, "minzoom"); + if (minzoomValue) { + if (toNumber(*minzoomValue)) { + options.minzoom = static_cast(*toNumber(*minzoomValue)); + } else { + error = { "GeoJSON source minzoom value must be a number" }; + return {}; + } + } + + const auto maxzoomValue = objectMember(value, "maxzoom"); + if (maxzoomValue) { + if (toNumber(*maxzoomValue)) { + options.maxzoom = static_cast(*toNumber(*maxzoomValue)); + } else { + error = { "GeoJSON source maxzoom value must be a number" }; + return {}; + } + } + + const auto bufferValue = objectMember(value, "buffer"); + if (bufferValue) { + if (toNumber(*bufferValue)) { + options.buffer = static_cast(*toNumber(*bufferValue)); + } else { + error = { "GeoJSON source buffer value must be a number" }; + return {}; + } + } + + const auto toleranceValue = objectMember(value, "tolerance"); + if (toleranceValue) { + if (toNumber(*toleranceValue)) { + options.tolerance = static_cast(*toNumber(*toleranceValue)); + } else { + error = { "GeoJSON source tolerance value must be a number" }; + return {}; + } + } + + const auto clusterValue = objectMember(value, "cluster"); + if (clusterValue) { + if (toBool(*clusterValue)) { + options.cluster = *toBool(*clusterValue); + } else { + error = { "GeoJSON source cluster value must be a boolean" }; + return {}; + } + } + + const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom"); + if (clusterMaxZoomValue) { + if (toNumber(*clusterMaxZoomValue)) { + options.clusterMaxZoom = static_cast(*toNumber(*clusterMaxZoomValue)); + } else { + error = { "GeoJSON source clusterMaxZoom value must be a number" }; + return {}; + } + } + + const auto clusterRadiusValue = objectMember(value, "clusterRadius"); + if (clusterRadiusValue) { + if (toNumber(*clusterRadiusValue)) { + options.clusterRadius = static_cast(*toNumber(*clusterRadiusValue)); + } else { + error = { "GeoJSON source clusterRadius value must be a number" }; + return {}; + } + } + + return { options }; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/json.hpp b/src/mbgl/style/conversion/json.hpp index 0817ac09df..7dd2378f6b 100644 --- a/src/mbgl/style/conversion/json.hpp +++ b/src/mbgl/style/conversion/json.hpp @@ -20,7 +20,7 @@ optional convertJSON(const std::string& json, Error& error, Args&&...args) { return {}; } - return convert(document, error, std::forward(args)...); + return convert(document, error, std::forward(args)...); } } // namespace conversion diff --git a/src/mbgl/style/conversion/layer.cpp b/src/mbgl/style/conversion/layer.cpp new file mode 100644 index 0000000000..0ca582f8dc --- /dev/null +++ b/src/mbgl/style/conversion/layer.cpp @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +optional setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value) { + static const auto setters = makeLayoutPropertySetters(); + auto it = setters.find(name); + if (it == setters.end()) { + return Error { "property not found" }; + } + return it->second(layer, value); +} + +optional setPaintProperty(Layer& layer, const std::string& name, const Convertible& value) { + static const auto setters = makePaintPropertySetters(); + auto it = setters.find(name); + if (it == setters.end()) { + return Error { "property not found" }; + } + return it->second(layer, value); +} + +optional setPaintProperties(Layer& layer, const Convertible& value) { + auto paintValue = objectMember(value, "paint"); + if (!paintValue) { + return {}; + } + return eachMember(*paintValue, [&] (const std::string& k, const Convertible& v) { + return setPaintProperty(layer, k, v); + }); +} + +template +optional> convertVectorLayer(const std::string& id, const Convertible& value, Error& error) { + auto sourceValue = objectMember(value, "source"); + if (!sourceValue) { + error = { "layer must have a source" }; + return {}; + } + + optional source = toString(*sourceValue); + if (!source) { + error = { "layer source must be a string" }; + return {}; + } + + std::unique_ptr layer = std::make_unique(id, *source); + + auto sourceLayerValue = objectMember(value, "source-layer"); + if (sourceLayerValue) { + optional sourceLayer = toString(*sourceLayerValue); + if (!sourceLayer) { + error = { "layer source-layer must be a string" }; + return {}; + } + layer->setSourceLayer(*sourceLayer); + } + + auto filterValue = objectMember(value, "filter"); + if (filterValue) { + optional filter = convert(*filterValue, error); + if (!filter) { + return {}; + } + layer->setFilter(*filter); + } + + return { std::move(layer) }; +} + +static optional> convertRasterLayer(const std::string& id, const Convertible& value, Error& error) { + auto sourceValue = objectMember(value, "source"); + if (!sourceValue) { + error = { "layer must have a source" }; + return {}; + } + + optional source = toString(*sourceValue); + if (!source) { + error = { "layer source must be a string" }; + return {}; + } + + return { std::make_unique(id, *source) }; +} + +static optional> convertBackgroundLayer(const std::string& id, const Convertible&, Error&) { + return { std::make_unique(id) }; +} + +optional> Converter>::operator()(const Convertible& value, Error& error) const { + if (!isObject(value)) { + error = { "layer must be an object" }; + return {}; + } + + auto idValue = objectMember(value, "id"); + if (!idValue) { + error = { "layer must have an id" }; + return {}; + } + + optional id = toString(*idValue); + if (!id) { + error = { "layer id must be a string" }; + return {}; + } + + auto typeValue = objectMember(value, "type"); + if (!typeValue) { + error = { "layer must have a type" }; + return {}; + } + + optional type = toString(*typeValue); + if (!type) { + error = { "layer type must be a string" }; + return {}; + } + + optional> converted; + + if (*type == "fill") { + converted = convertVectorLayer(*id, value, error); + } else if (*type == "fill-extrusion") { + converted = convertVectorLayer(*id, value, error); + } else if (*type == "line") { + converted = convertVectorLayer(*id, value, error); + } else if (*type == "circle") { + converted = convertVectorLayer(*id, value, error); + } else if (*type == "symbol") { + converted = convertVectorLayer(*id, value, error); + } else if (*type == "raster") { + converted = convertRasterLayer(*id, value, error); + } else if (*type == "background") { + converted = convertBackgroundLayer(*id, value, error); + } else { + error = { "invalid layer type" }; + return {}; + } + + if (!converted) { + return converted; + } + + std::unique_ptr layer = std::move(*converted); + + auto minzoomValue = objectMember(value, "minzoom"); + if (minzoomValue) { + optional minzoom = toNumber(*minzoomValue); + if (!minzoom) { + error = { "minzoom must be numeric" }; + return {}; + } + layer->setMinZoom(*minzoom); + } + + auto maxzoomValue = objectMember(value, "maxzoom"); + if (maxzoomValue) { + optional maxzoom = toNumber(*maxzoomValue); + if (!maxzoom) { + error = { "maxzoom must be numeric" }; + return {}; + } + layer->setMaxZoom(*maxzoom); + } + + auto layoutValue = objectMember(value, "layout"); + if (layoutValue) { + if (!isObject(*layoutValue)) { + error = { "layout must be an object" }; + return {}; + } + optional error_ = eachMember(*layoutValue, [&] (const std::string& k, const Convertible& v) { + return setLayoutProperty(*layer, k, v); + }); + if (error_) { + error = *error_; + return {}; + } + } + + optional error_ = setPaintProperties(*layer, value); + if (error_) { + error = *error_; + return {}; + } + + return std::move(layer); +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/light.cpp b/src/mbgl/style/conversion/light.cpp new file mode 100644 index 0000000000..f521f74386 --- /dev/null +++ b/src/mbgl/style/conversion/light.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +optional Converter::operator()(const Convertible& value, Error& error) const { + if (!isObject(value)) { + error = { "light must be an object" }; + return {}; + } + + Light light; + + const auto anchor = objectMember(value, "anchor"); + if (anchor) { + optional> convertedAnchor = + convert>(*anchor, error); + + if (convertedAnchor) { + light.setAnchor(*convertedAnchor); + } else { + return {}; + } + } + + const auto anchorTransition = objectMember(value, "anchor-transition"); + if (anchorTransition) { + optional transition = + convert(*anchorTransition, error); + if (transition) { + light.setAnchorTransition(*transition); + } else { + return {}; + } + } + + const auto color = objectMember(value, "color"); + if (color) { + optional> convertedColor = + convert>(*color, error); + + if (convertedColor) { + light.setColor(*convertedColor); + } else { + return {}; + } + } + + const auto colorTransition = objectMember(value, "color-transition"); + if (colorTransition) { + optional transition = + convert(*colorTransition, error); + if (transition) { + light.setColorTransition(*transition); + } else { + return {}; + } + } + + const auto position = objectMember(value, "position"); + if (position) { + optional> convertedPosition = + convert>(*position, error); + + if (convertedPosition) { + light.setPosition(*convertedPosition); + } else { + return {}; + } + } + + const auto positionTransition = objectMember(value, "position-transition"); + if (positionTransition) { + optional transition = + convert(*positionTransition, error); + if (transition) { + light.setPositionTransition(*transition); + } else { + return {}; + } + } + + const auto intensity = objectMember(value, "intensity"); + if (intensity) { + optional> convertedIntensity = + convert>(*intensity, error); + + if (convertedIntensity) { + light.setIntensity(*convertedIntensity); + } else { + return {}; + } + } + + const auto intensityTransition = objectMember(value, "intensity-transition"); + if (intensityTransition) { + optional transition = + convert(*intensityTransition, error); + if (transition) { + light.setIntensityTransition(*transition); + } else { + return {}; + } + } + + return { std::move(light) }; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/make_property_setters.hpp b/src/mbgl/style/conversion/make_property_setters.hpp new file mode 100644 index 0000000000..074d7eb730 --- /dev/null +++ b/src/mbgl/style/conversion/make_property_setters.hpp @@ -0,0 +1,209 @@ +#pragma once + +// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace conversion { + +inline auto makeLayoutPropertySetters() { + std::unordered_map result; + + result["visibility"] = &setVisibility; + + + result["line-cap"] = &setProperty, &LineLayer::setLineCap>; + result["line-join"] = &setProperty, &LineLayer::setLineJoin>; + result["line-miter-limit"] = &setProperty, &LineLayer::setLineMiterLimit>; + result["line-round-limit"] = &setProperty, &LineLayer::setLineRoundLimit>; + + result["symbol-placement"] = &setProperty, &SymbolLayer::setSymbolPlacement>; + result["symbol-spacing"] = &setProperty, &SymbolLayer::setSymbolSpacing>; + result["symbol-avoid-edges"] = &setProperty, &SymbolLayer::setSymbolAvoidEdges>; + result["icon-allow-overlap"] = &setProperty, &SymbolLayer::setIconAllowOverlap>; + result["icon-ignore-placement"] = &setProperty, &SymbolLayer::setIconIgnorePlacement>; + result["icon-optional"] = &setProperty, &SymbolLayer::setIconOptional>; + result["icon-rotation-alignment"] = &setProperty, &SymbolLayer::setIconRotationAlignment>; + result["icon-size"] = &setProperty, &SymbolLayer::setIconSize>; + result["icon-text-fit"] = &setProperty, &SymbolLayer::setIconTextFit>; + result["icon-text-fit-padding"] = &setProperty>, &SymbolLayer::setIconTextFitPadding>; + result["icon-image"] = &setProperty, &SymbolLayer::setIconImage>; + result["icon-rotate"] = &setProperty, &SymbolLayer::setIconRotate>; + result["icon-padding"] = &setProperty, &SymbolLayer::setIconPadding>; + result["icon-keep-upright"] = &setProperty, &SymbolLayer::setIconKeepUpright>; + result["icon-offset"] = &setProperty>, &SymbolLayer::setIconOffset>; + result["icon-anchor"] = &setProperty, &SymbolLayer::setIconAnchor>; + result["icon-pitch-alignment"] = &setProperty, &SymbolLayer::setIconPitchAlignment>; + result["text-pitch-alignment"] = &setProperty, &SymbolLayer::setTextPitchAlignment>; + result["text-rotation-alignment"] = &setProperty, &SymbolLayer::setTextRotationAlignment>; + result["text-field"] = &setProperty, &SymbolLayer::setTextField>; + result["text-font"] = &setProperty>, &SymbolLayer::setTextFont>; + result["text-size"] = &setProperty, &SymbolLayer::setTextSize>; + result["text-max-width"] = &setProperty, &SymbolLayer::setTextMaxWidth>; + result["text-line-height"] = &setProperty, &SymbolLayer::setTextLineHeight>; + result["text-letter-spacing"] = &setProperty, &SymbolLayer::setTextLetterSpacing>; + result["text-justify"] = &setProperty, &SymbolLayer::setTextJustify>; + result["text-anchor"] = &setProperty, &SymbolLayer::setTextAnchor>; + result["text-max-angle"] = &setProperty, &SymbolLayer::setTextMaxAngle>; + result["text-rotate"] = &setProperty, &SymbolLayer::setTextRotate>; + result["text-padding"] = &setProperty, &SymbolLayer::setTextPadding>; + result["text-keep-upright"] = &setProperty, &SymbolLayer::setTextKeepUpright>; + result["text-transform"] = &setProperty, &SymbolLayer::setTextTransform>; + result["text-offset"] = &setProperty>, &SymbolLayer::setTextOffset>; + result["text-allow-overlap"] = &setProperty, &SymbolLayer::setTextAllowOverlap>; + result["text-ignore-placement"] = &setProperty, &SymbolLayer::setTextIgnorePlacement>; + result["text-optional"] = &setProperty, &SymbolLayer::setTextOptional>; + + + + + + return result; +} + +inline auto makePaintPropertySetters() { + std::unordered_map result; + + result["fill-antialias"] = &setProperty, &FillLayer::setFillAntialias>; + result["fill-antialias-transition"] = &setTransition; + result["fill-opacity"] = &setProperty, &FillLayer::setFillOpacity>; + result["fill-opacity-transition"] = &setTransition; + result["fill-color"] = &setProperty, &FillLayer::setFillColor>; + result["fill-color-transition"] = &setTransition; + result["fill-outline-color"] = &setProperty, &FillLayer::setFillOutlineColor>; + result["fill-outline-color-transition"] = &setTransition; + result["fill-translate"] = &setProperty>, &FillLayer::setFillTranslate>; + result["fill-translate-transition"] = &setTransition; + result["fill-translate-anchor"] = &setProperty, &FillLayer::setFillTranslateAnchor>; + result["fill-translate-anchor-transition"] = &setTransition; + result["fill-pattern"] = &setProperty, &FillLayer::setFillPattern>; + result["fill-pattern-transition"] = &setTransition; + + result["line-opacity"] = &setProperty, &LineLayer::setLineOpacity>; + result["line-opacity-transition"] = &setTransition; + result["line-color"] = &setProperty, &LineLayer::setLineColor>; + result["line-color-transition"] = &setTransition; + result["line-translate"] = &setProperty>, &LineLayer::setLineTranslate>; + result["line-translate-transition"] = &setTransition; + result["line-translate-anchor"] = &setProperty, &LineLayer::setLineTranslateAnchor>; + result["line-translate-anchor-transition"] = &setTransition; + result["line-width"] = &setProperty, &LineLayer::setLineWidth>; + result["line-width-transition"] = &setTransition; + result["line-gap-width"] = &setProperty, &LineLayer::setLineGapWidth>; + result["line-gap-width-transition"] = &setTransition; + result["line-offset"] = &setProperty, &LineLayer::setLineOffset>; + result["line-offset-transition"] = &setTransition; + result["line-blur"] = &setProperty, &LineLayer::setLineBlur>; + result["line-blur-transition"] = &setTransition; + result["line-dasharray"] = &setProperty>, &LineLayer::setLineDasharray>; + result["line-dasharray-transition"] = &setTransition; + result["line-pattern"] = &setProperty, &LineLayer::setLinePattern>; + result["line-pattern-transition"] = &setTransition; + + result["icon-opacity"] = &setProperty, &SymbolLayer::setIconOpacity>; + result["icon-opacity-transition"] = &setTransition; + result["icon-color"] = &setProperty, &SymbolLayer::setIconColor>; + result["icon-color-transition"] = &setTransition; + result["icon-halo-color"] = &setProperty, &SymbolLayer::setIconHaloColor>; + result["icon-halo-color-transition"] = &setTransition; + result["icon-halo-width"] = &setProperty, &SymbolLayer::setIconHaloWidth>; + result["icon-halo-width-transition"] = &setTransition; + result["icon-halo-blur"] = &setProperty, &SymbolLayer::setIconHaloBlur>; + result["icon-halo-blur-transition"] = &setTransition; + result["icon-translate"] = &setProperty>, &SymbolLayer::setIconTranslate>; + result["icon-translate-transition"] = &setTransition; + result["icon-translate-anchor"] = &setProperty, &SymbolLayer::setIconTranslateAnchor>; + result["icon-translate-anchor-transition"] = &setTransition; + result["text-opacity"] = &setProperty, &SymbolLayer::setTextOpacity>; + result["text-opacity-transition"] = &setTransition; + result["text-color"] = &setProperty, &SymbolLayer::setTextColor>; + result["text-color-transition"] = &setTransition; + result["text-halo-color"] = &setProperty, &SymbolLayer::setTextHaloColor>; + result["text-halo-color-transition"] = &setTransition; + result["text-halo-width"] = &setProperty, &SymbolLayer::setTextHaloWidth>; + result["text-halo-width-transition"] = &setTransition; + result["text-halo-blur"] = &setProperty, &SymbolLayer::setTextHaloBlur>; + result["text-halo-blur-transition"] = &setTransition; + result["text-translate"] = &setProperty>, &SymbolLayer::setTextTranslate>; + result["text-translate-transition"] = &setTransition; + result["text-translate-anchor"] = &setProperty, &SymbolLayer::setTextTranslateAnchor>; + result["text-translate-anchor-transition"] = &setTransition; + + result["circle-radius"] = &setProperty, &CircleLayer::setCircleRadius>; + result["circle-radius-transition"] = &setTransition; + result["circle-color"] = &setProperty, &CircleLayer::setCircleColor>; + result["circle-color-transition"] = &setTransition; + result["circle-blur"] = &setProperty, &CircleLayer::setCircleBlur>; + result["circle-blur-transition"] = &setTransition; + result["circle-opacity"] = &setProperty, &CircleLayer::setCircleOpacity>; + result["circle-opacity-transition"] = &setTransition; + result["circle-translate"] = &setProperty>, &CircleLayer::setCircleTranslate>; + result["circle-translate-transition"] = &setTransition; + result["circle-translate-anchor"] = &setProperty, &CircleLayer::setCircleTranslateAnchor>; + result["circle-translate-anchor-transition"] = &setTransition; + result["circle-pitch-scale"] = &setProperty, &CircleLayer::setCirclePitchScale>; + result["circle-pitch-scale-transition"] = &setTransition; + result["circle-pitch-alignment"] = &setProperty, &CircleLayer::setCirclePitchAlignment>; + result["circle-pitch-alignment-transition"] = &setTransition; + result["circle-stroke-width"] = &setProperty, &CircleLayer::setCircleStrokeWidth>; + result["circle-stroke-width-transition"] = &setTransition; + result["circle-stroke-color"] = &setProperty, &CircleLayer::setCircleStrokeColor>; + result["circle-stroke-color-transition"] = &setTransition; + result["circle-stroke-opacity"] = &setProperty, &CircleLayer::setCircleStrokeOpacity>; + result["circle-stroke-opacity-transition"] = &setTransition; + + result["fill-extrusion-opacity"] = &setProperty, &FillExtrusionLayer::setFillExtrusionOpacity>; + result["fill-extrusion-opacity-transition"] = &setTransition; + result["fill-extrusion-color"] = &setProperty, &FillExtrusionLayer::setFillExtrusionColor>; + result["fill-extrusion-color-transition"] = &setTransition; + result["fill-extrusion-translate"] = &setProperty>, &FillExtrusionLayer::setFillExtrusionTranslate>; + result["fill-extrusion-translate-transition"] = &setTransition; + result["fill-extrusion-translate-anchor"] = &setProperty, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>; + result["fill-extrusion-translate-anchor-transition"] = &setTransition; + result["fill-extrusion-pattern"] = &setProperty, &FillExtrusionLayer::setFillExtrusionPattern>; + result["fill-extrusion-pattern-transition"] = &setTransition; + result["fill-extrusion-height"] = &setProperty, &FillExtrusionLayer::setFillExtrusionHeight>; + result["fill-extrusion-height-transition"] = &setTransition; + result["fill-extrusion-base"] = &setProperty, &FillExtrusionLayer::setFillExtrusionBase>; + result["fill-extrusion-base-transition"] = &setTransition; + + result["raster-opacity"] = &setProperty, &RasterLayer::setRasterOpacity>; + result["raster-opacity-transition"] = &setTransition; + result["raster-hue-rotate"] = &setProperty, &RasterLayer::setRasterHueRotate>; + result["raster-hue-rotate-transition"] = &setTransition; + result["raster-brightness-min"] = &setProperty, &RasterLayer::setRasterBrightnessMin>; + result["raster-brightness-min-transition"] = &setTransition; + result["raster-brightness-max"] = &setProperty, &RasterLayer::setRasterBrightnessMax>; + result["raster-brightness-max-transition"] = &setTransition; + result["raster-saturation"] = &setProperty, &RasterLayer::setRasterSaturation>; + result["raster-saturation-transition"] = &setTransition; + result["raster-contrast"] = &setProperty, &RasterLayer::setRasterContrast>; + result["raster-contrast-transition"] = &setTransition; + result["raster-fade-duration"] = &setProperty, &RasterLayer::setRasterFadeDuration>; + result["raster-fade-duration-transition"] = &setTransition; + + result["background-color"] = &setProperty, &BackgroundLayer::setBackgroundColor>; + result["background-color-transition"] = &setTransition; + result["background-pattern"] = &setProperty, &BackgroundLayer::setBackgroundPattern>; + result["background-pattern-transition"] = &setTransition; + result["background-opacity"] = &setProperty, &BackgroundLayer::setBackgroundOpacity>; + result["background-opacity-transition"] = &setTransition; + + return result; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/position.cpp b/src/mbgl/style/conversion/position.cpp new file mode 100644 index 0000000000..702d250dbf --- /dev/null +++ b/src/mbgl/style/conversion/position.cpp @@ -0,0 +1,22 @@ +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace conversion { + +optional Converter::operator()(const Convertible& value, Error& error) const { + optional> spherical = convert>(value, error); + + if (!spherical) { + return {}; + } + + return Position(*spherical); +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/property_setter.hpp b/src/mbgl/style/conversion/property_setter.hpp new file mode 100644 index 0000000000..9e382b9c38 --- /dev/null +++ b/src/mbgl/style/conversion/property_setter.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace conversion { + +using PropertySetter = optional (*) (Layer&, const Convertible&); + +template +optional setProperty(Layer& layer, const Convertible& value) { + auto* typedLayer = layer.as(); + if (!typedLayer) { + return Error { "layer doesn't support this property" }; + } + + Error error; + optional typedValue = convert(value, error); + if (!typedValue) { + return error; + } + + (typedLayer->*setter)(*typedValue); + return {}; +} + +template +optional setTransition(Layer& layer, const Convertible& value) { + auto* typedLayer = layer.as(); + if (!typedLayer) { + return Error { "layer doesn't support this property" }; + } + + Error error; + optional transition = convert(value, error); + if (!transition) { + return error; + } + + (typedLayer->*setter)(*transition); + return {}; +} + +inline optional setVisibility(Layer& layer, const Convertible& value) { + if (isUndefined(value)) { + layer.setVisibility(VisibilityType::Visible); + return {}; + } + + Error error; + optional visibility = convert(value, error); + if (!visibility) { + return error; + } + + layer.setVisibility(*visibility); + return {}; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/source.cpp b/src/mbgl/style/conversion/source.cpp new file mode 100644 index 0000000000..c10d0babcf --- /dev/null +++ b/src/mbgl/style/conversion/source.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace conversion { + +// A tile source can either specify a URL to TileJSON, or inline TileJSON. +static optional> convertURLOrTileset(const Convertible& value, Error& error) { + auto urlVal = objectMember(value, "url"); + if (!urlVal) { + optional tileset = convert(value, error); + if (!tileset) { + return {}; + } + return { *tileset }; + } + + optional url = toString(*urlVal); + if (!url) { + error = { "source url must be a string" }; + return {}; + } + + return { *url }; +} + +static optional> convertRasterSource(const std::string& id, + const Convertible& value, + Error& error) { + optional> urlOrTileset = convertURLOrTileset(value, error); + if (!urlOrTileset) { + return {}; + } + + uint16_t tileSize = util::tileSize; + auto tileSizeValue = objectMember(value, "tileSize"); + if (tileSizeValue) { + optional size = toNumber(*tileSizeValue); + if (!size || *size < 0 || *size > std::numeric_limits::max()) { + error = { "invalid tileSize" }; + return {}; + } + tileSize = *size; + } + + return { std::make_unique(id, std::move(*urlOrTileset), tileSize) }; +} + +static optional> convertVectorSource(const std::string& id, + const Convertible& value, + Error& error) { + optional> urlOrTileset = convertURLOrTileset(value, error); + if (!urlOrTileset) { + return {}; + } + + return { std::make_unique(id, std::move(*urlOrTileset)) }; +} + +static optional> convertGeoJSONSource(const std::string& id, + const Convertible& value, + Error& error) { + auto dataValue = objectMember(value, "data"); + if (!dataValue) { + error = { "GeoJSON source must have a data value" }; + return {}; + } + + optional options = convert(value, error); + if (!options) { + return {}; + } + + auto result = std::make_unique(id, *options); + + if (isObject(*dataValue)) { + optional geoJSON = convert(*dataValue, error); + if (!geoJSON) { + return {}; + } + result->setGeoJSON(std::move(*geoJSON)); + } else if (toString(*dataValue)) { + result->setURL(*toString(*dataValue)); + } else { + error = { "GeoJSON data must be a URL or an object" }; + return {}; + } + + return { std::move(result) }; +} + +static optional> convertImageSource(const std::string& id, + const Convertible& value, + Error& error) { + auto urlValue = objectMember(value, "url"); + if (!urlValue) { + error = { "Image source must have a url value" }; + return {}; + } + + auto urlString = toString(*urlValue); + if (!urlString) { + error = { "Image url must be a URL string" }; + return {}; + } + + auto coordinatesValue = objectMember(value, "coordinates"); + if (!coordinatesValue) { + error = { "Image source must have a coordinates values" }; + return {}; + } + + if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) { + error = { "Image coordinates must be an array of four longitude latitude pairs" }; + return {}; + } + + std::array coordinates; + for (std::size_t i=0; i < 4; i++) { + auto latLng = conversion::convert(arrayMember(*coordinatesValue,i), error); + if (!latLng) { + return {}; + } + coordinates[i] = *latLng; + } + auto result = std::make_unique(id, coordinates); + result->setURL(*urlString); + + return { std::move(result) }; +} + +optional> Converter>::operator()(const Convertible& value, Error& error, const std::string& id) const { + if (!isObject(value)) { + error = { "source must be an object" }; + return {}; + } + + auto typeValue = objectMember(value, "type"); + if (!typeValue) { + error = { "source must have a type" }; + return {}; + } + + optional type = toString(*typeValue); + if (!type) { + error = { "source type must be a string" }; + return {}; + } + + if (*type == "raster") { + return convertRasterSource(id, value, error); + } else if (*type == "vector") { + return convertVectorSource(id, value, error); + } else if (*type == "geojson") { + return convertGeoJSONSource(id, value, error); + } else if (*type == "image") { + return convertImageSource(id, value, error); + } else { + error = { "invalid source type" }; + return {}; + } +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp new file mode 100644 index 0000000000..b9383c41b8 --- /dev/null +++ b/src/mbgl/style/conversion/tileset.cpp @@ -0,0 +1,73 @@ +#include + +namespace mbgl { +namespace style { +namespace conversion { + +optional Converter::operator()(const Convertible& value, Error& error) const { + Tileset result; + + auto tiles = objectMember(value, "tiles"); + if (!tiles) { + error = { "source must have tiles" }; + return {}; + } + + if (!isArray(*tiles)) { + error = { "source tiles must be an array" }; + return {}; + } + + for (std::size_t i = 0; i < arrayLength(*tiles); i++) { + optional urlTemplate = toString(arrayMember(*tiles, i)); + if (!urlTemplate) { + error = { "source tiles member must be a string" }; + return {}; + } + result.tiles.push_back(std::move(*urlTemplate)); + } + + auto schemeValue = objectMember(value, "scheme"); + if (schemeValue) { + optional scheme = toString(*schemeValue); + if (scheme && *scheme == "tms") { + result.scheme = Tileset::Scheme::TMS; + } + } + + auto minzoomValue = objectMember(value, "minzoom"); + if (minzoomValue) { + optional minzoom = toNumber(*minzoomValue); + if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits::max()) { + error = { "invalid minzoom" }; + return {}; + } + result.zoomRange.min = *minzoom; + } + + auto maxzoomValue = objectMember(value, "maxzoom"); + if (maxzoomValue) { + optional maxzoom = toNumber(*maxzoomValue); + if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits::max()) { + error = { "invalid maxzoom" }; + return {}; + } + result.zoomRange.max = *maxzoom; + } + + auto attributionValue = objectMember(value, "attribution"); + if (attributionValue) { + optional attribution = toString(*attributionValue); + if (!attribution) { + error = { "source attribution must be a string" }; + return {}; + } + result.attribution = std::move(*attribution); + } + + return result; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/conversion/transition_options.cpp b/src/mbgl/style/conversion/transition_options.cpp new file mode 100644 index 0000000000..8a60c5bfd8 --- /dev/null +++ b/src/mbgl/style/conversion/transition_options.cpp @@ -0,0 +1,40 @@ +#include + +namespace mbgl { +namespace style { +namespace conversion { + +optional Converter::operator()(const Convertible& value, Error& error) const { + if (!isObject(value)) { + error = { "transition must be an object" }; + return {}; + } + + TransitionOptions result; + + auto duration = objectMember(value, "duration"); + if (duration) { + auto number = toNumber(*duration); + if (!number) { + error = { "duration must be a number" }; + return {}; + } + result.duration = { std::chrono::milliseconds(int64_t(*number)) }; + } + + auto delay = objectMember(value, "delay"); + if (delay) { + auto number = toNumber(*delay); + if (!number) { + error = { "delay must be a number" }; + return {}; + } + result.delay = { std::chrono::milliseconds(int64_t(*number)) }; + } + + return result; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp index a83897dbf5..10fce33986 100644 --- a/src/mbgl/style/parser.cpp +++ b/src/mbgl/style/parser.cpp @@ -1,11 +1,13 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -149,7 +151,7 @@ void Parser::parseSources(const JSValue& value) { } for (const auto& property : value.GetObject()) { - std::string id = *conversion::toString(property.name); + std::string id { property.name.GetString(), property.name.GetStringLength() }; conversion::Error error; optional> source = @@ -256,7 +258,7 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique } layer = reference->cloneRef(id); - conversion::setPaintProperties(*layer, value); + conversion::setPaintProperties(*layer, conversion::Convertible(&value)); } else { conversion::Error error; optional> converted = conversion::convert>(value, error); diff --git a/src/mbgl/style/rapidjson_conversion.hpp b/src/mbgl/style/rapidjson_conversion.hpp index 48a764ccb4..79bd9c928b 100644 --- a/src/mbgl/style/rapidjson_conversion.hpp +++ b/src/mbgl/style/rapidjson_conversion.hpp @@ -1,103 +1,125 @@ #pragma once #include -#include #include +#include +#include + namespace mbgl { namespace style { namespace conversion { -inline bool isUndefined(const JSValue& value) { - return value.IsNull(); -} - -inline bool isArray(const JSValue& value) { - return value.IsArray(); -} +template <> +class ConversionTraits { +public: + static bool isUndefined(const JSValue* value) { + return value->IsNull(); + } -inline std::size_t arrayLength(const JSValue& value) { - return value.Size(); -} + static bool isArray(const JSValue* value) { + return value->IsArray(); + } -inline const JSValue& arrayMember(const JSValue& value, std::size_t i) { - return value[rapidjson::SizeType(i)]; -} + static std::size_t arrayLength(const JSValue* value) { + return value->Size(); + } -inline bool isObject(const JSValue& value) { - return value.IsObject(); -} + static const JSValue* arrayMember(const JSValue* value, std::size_t i) { + return &(*value)[rapidjson::SizeType(i)]; + } -inline const JSValue* objectMember(const JSValue& value, const char * name) { - if (!value.HasMember(name)) { - return nullptr; + static bool isObject(const JSValue* value) { + return value->IsObject(); } - return &value[name]; -} -template -optional eachMember(const JSValue& value, Fn&& fn) { - assert(value.IsObject()); - for (const auto& property : value.GetObject()) { - optional result = - fn({ property.name.GetString(), property.name.GetStringLength() }, property.value); - if (result) { - return result; + static optional objectMember(const JSValue* value, const char * name) { + if (!value->HasMember(name)) { + return optional(); } + const JSValue* const& member = &(*value)[name]; + return {member}; } - return {}; -} -inline optional toBool(const JSValue& value) { - if (!value.IsBool()) { + template + static optional eachMember(const JSValue* value, Fn&& fn) { + assert(value->IsObject()); + for (const auto& property : value->GetObject()) { + optional result = + fn({ property.name.GetString(), property.name.GetStringLength() }, &property.value); + if (result) { + return result; + } + } return {}; } - return value.GetBool(); -} -inline optional toNumber(const JSValue& value) { - if (!value.IsNumber()) { - return {}; + static optional toBool(const JSValue* value) { + if (!value->IsBool()) { + return {}; + } + return value->GetBool(); } - return value.GetDouble(); -} -inline optional toDouble(const JSValue& value) { - if (!value.IsNumber()) { - return {}; + static optional toNumber(const JSValue* value) { + if (!value->IsNumber()) { + return {}; + } + return value->GetDouble(); } - return value.GetDouble(); -} -inline optional toString(const JSValue& value) { - if (!value.IsString()) { - return {}; + static optional toDouble(const JSValue* value) { + if (!value->IsNumber()) { + return {}; + } + return value->GetDouble(); + } + + static optional toString(const JSValue* value) { + if (!value->IsString()) { + return {}; + } + return {{ value->GetString(), value->GetStringLength() }}; } - return {{ value.GetString(), value.GetStringLength() }}; -} -inline optional toValue(const JSValue& value) { - switch (value.GetType()) { - case rapidjson::kNullType: - case rapidjson::kFalseType: - return { false }; + static optional toValue(const JSValue* value) { + switch (value->GetType()) { + case rapidjson::kNullType: + case rapidjson::kFalseType: + return { false }; - case rapidjson::kTrueType: - return { true }; + case rapidjson::kTrueType: + return { true }; - case rapidjson::kStringType: - return { std::string { value.GetString(), value.GetStringLength() } }; + case rapidjson::kStringType: + return { std::string { value->GetString(), value->GetStringLength() } }; - case rapidjson::kNumberType: - if (value.IsUint64()) return { value.GetUint64() }; - if (value.IsInt64()) return { value.GetInt64() }; - return { value.GetDouble() }; + case rapidjson::kNumberType: + if (value->IsUint64()) return { value->GetUint64() }; + if (value->IsInt64()) return { value->GetInt64() }; + return { value->GetDouble() }; - default: + default: + return {}; + } + } + + static optional toGeoJSON(const JSValue* value, Error& error) { + try { + return mapbox::geojson::convert(*value); + } catch (const std::exception& ex) { + error = { ex.what() }; return {}; + } } +}; + +template +optional convert(const JSValue& value, Error& error, Args&&...args) { + return convert(Convertible(&value), error, std::forward(args)...); } } // namespace conversion } // namespace style } // namespace mbgl + diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp index 37907d3f60..3214c6316e 100644 --- a/src/mbgl/style/style_impl.cpp +++ b/src/mbgl/style/style_impl.cpp @@ -204,9 +204,10 @@ Layer* Style::Impl::addLayer(std::unique_ptr layer, optional } layer->setObserver(this); + Layer* result = layers.add(std::move(layer), before); observer->onUpdate(); - return layers.add(std::move(layer), before); + return result; } std::unique_ptr Style::Impl::removeLayer(const std::string& id) { diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index d648d2e5ff..bbec899950 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -23,19 +23,19 @@ void GeoJSONTile::querySourceFeatures( const SourceQueryOptions& options) { // Ignore the sourceLayer, there is only one - auto layer = getData()->getLayer({}); - - if (layer) { - auto featureCount = layer->featureCount(); - for (std::size_t i = 0; i < featureCount; i++) { - auto feature = layer->getFeature(i); - - // Apply filter, if any - if (options.filter && !(*options.filter)(*feature)) { - continue; + if (auto tileData = getData()) { + if (auto layer = tileData->getLayer({})) { + auto featureCount = layer->featureCount(); + for (std::size_t i = 0; i < featureCount; i++) { + auto feature = layer->getFeature(i); + + // Apply filter, if any + if (options.filter && !(*options.filter)(*feature)) { + continue; + } + + result.push_back(convertFeature(*feature, id.canonical)); } - - result.push_back(convertFeature(*feature, id.canonical)); } } } diff --git a/src/mbgl/tile/tile_id.hpp b/src/mbgl/tile/tile_id.hpp deleted file mode 100644 index 811158e9b9..0000000000 --- a/src/mbgl/tile/tile_id.hpp +++ /dev/null @@ -1,276 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace mbgl { - -class OverscaledTileID; -class CanonicalTileID; -class UnwrappedTileID; - -// Has integer z/x/y coordinates -// All tiles must be derived from 0/0/0 (=no tiles outside of the main tile pyramid) -// Used for requesting data; represents data tiles that exist out there. -// z is never larger than the source's maxzoom -class CanonicalTileID { -public: - CanonicalTileID(uint8_t z, uint32_t x, uint32_t y); - bool operator==(const CanonicalTileID&) const; - bool operator!=(const CanonicalTileID&) const; - bool operator<(const CanonicalTileID&) const; - bool isChildOf(const CanonicalTileID&) const; - CanonicalTileID scaledTo(uint8_t z) const; - std::array children() const; - - const uint8_t z; - const uint32_t x; - const uint32_t y; -}; - -::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs); -namespace util { -std::string toString(const CanonicalTileID&); -} // namespace util - -// Has integer z/x/y coordinates -// overscaledZ describes the zoom level this tile is intented to represent, e.g. when parsing data -// z is never larger than the source's maxzoom -// z/x/y describe the -class OverscaledTileID { -public: - OverscaledTileID(uint8_t overscaledZ, int16_t wrap, CanonicalTileID); - OverscaledTileID(uint8_t overscaledZ, int16_t wrap, uint8_t z, uint32_t x, uint32_t y); - OverscaledTileID(uint8_t z, uint32_t x, uint32_t y); - explicit OverscaledTileID(const CanonicalTileID&); - explicit OverscaledTileID(CanonicalTileID&&); - bool operator==(const OverscaledTileID&) const; - bool operator!=(const OverscaledTileID&) const; - bool operator<(const OverscaledTileID&) const; - bool isChildOf(const OverscaledTileID&) const; - uint32_t overscaleFactor() const; - OverscaledTileID scaledTo(uint8_t z) const; - UnwrappedTileID toUnwrapped() const; - - const uint8_t overscaledZ; - const int16_t wrap; - const CanonicalTileID canonical; -}; - -::std::ostream& operator<<(::std::ostream& os, const OverscaledTileID& rhs); -namespace util { -std::string toString(const OverscaledTileID&); -} // namespace util - -// Has integer z/x/y coordinates -// wrap describes tiles that are left/right of the main tile pyramid, e.g. when wrapping the world -// Used for describing what position tiles are getting rendered at (= calc the matrix) -// z is never larger than the source's maxzoom -class UnwrappedTileID { -public: - UnwrappedTileID(uint8_t z, int64_t x, int64_t y); - UnwrappedTileID(int16_t wrap, CanonicalTileID); - bool operator==(const UnwrappedTileID&) const; - bool operator!=(const UnwrappedTileID&) const; - bool operator<(const UnwrappedTileID&) const; - bool isChildOf(const UnwrappedTileID&) const; - std::array children() const; - OverscaledTileID overscaleTo(uint8_t z) const; - float pixelsToTileUnits(float pixelValue, float zoom) const; - - const int16_t wrap; - const CanonicalTileID canonical; -}; - -::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs); -namespace util { -std::string toString(const UnwrappedTileID&); -} // namespace util - -inline CanonicalTileID::CanonicalTileID(uint8_t z_, uint32_t x_, uint32_t y_) : z(z_), x(x_), y(y_) { - assert(z <= 32); - assert(x < (1ull << z)); - assert(y < (1ull << z)); -} - -inline bool CanonicalTileID::operator==(const CanonicalTileID& rhs) const { - return z == rhs.z && x == rhs.x && y == rhs.y; -} - -inline bool CanonicalTileID::operator!=(const CanonicalTileID& rhs) const { - return z != rhs.z || x != rhs.x || y != rhs.y; -} - -inline bool CanonicalTileID::operator<(const CanonicalTileID& rhs) const { - return std::tie(z, x, y) < std::tie(rhs.z, rhs.x, rhs.y); -} - -inline bool CanonicalTileID::isChildOf(const CanonicalTileID& parent) const { - // We're first testing for z == 0, to avoid a 32 bit shift, which is undefined. - return parent.z == 0 || - (parent.z < z && parent.x == (x >> (z - parent.z)) && parent.y == (y >> (z - parent.z))); -} - -inline CanonicalTileID CanonicalTileID::scaledTo(uint8_t targetZ) const { - if (targetZ <= z) { - return { targetZ, x >> (z - targetZ), y >> (z - targetZ) }; // parent or same - } else { - return { targetZ, x << (targetZ - z), y << (targetZ - z) }; // child - } -} - -inline std::array CanonicalTileID::children() const { - const uint8_t childZ = z + 1; - const uint32_t childX = x * 2; - const uint32_t childY = y * 2; - return { { - { childZ, childX, childY }, - { childZ, childX, childY + 1 }, - { childZ, childX + 1, childY }, - { childZ, childX + 1, childY + 1 }, - } }; -} - -inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, CanonicalTileID canonical_) - : overscaledZ(overscaledZ_), wrap(wrap_), canonical(std::move(canonical_)) { - assert(overscaledZ >= canonical.z); -} - -inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, uint8_t z, uint32_t x, uint32_t y) - : overscaledZ(overscaledZ_), wrap(wrap_), canonical(z, x, y) { - assert(overscaledZ >= canonical.z); -} - -inline OverscaledTileID::OverscaledTileID(uint8_t z, uint32_t x, uint32_t y) - : overscaledZ(z), wrap(0), canonical(z, x, y) { -} - -inline OverscaledTileID::OverscaledTileID(const CanonicalTileID& canonical_) - : overscaledZ(canonical_.z), wrap(0), canonical(canonical_) { - assert(overscaledZ >= canonical.z); -} - -inline OverscaledTileID::OverscaledTileID(CanonicalTileID&& canonical_) - : overscaledZ(canonical_.z), wrap(0), canonical(std::forward(canonical_)) { - assert(overscaledZ >= canonical.z); -} - -inline bool OverscaledTileID::operator==(const OverscaledTileID& rhs) const { - return overscaledZ == rhs.overscaledZ && wrap == rhs.wrap &&canonical == rhs.canonical; -} - -inline bool OverscaledTileID::operator!=(const OverscaledTileID& rhs) const { - return overscaledZ != rhs.overscaledZ || wrap != rhs.wrap || canonical != rhs.canonical; -} - -inline bool OverscaledTileID::operator<(const OverscaledTileID& rhs) const { - return std::tie(overscaledZ, wrap, canonical) < std::tie(rhs.overscaledZ, rhs.wrap, rhs.canonical); -} - -inline uint32_t OverscaledTileID::overscaleFactor() const { - return 1u << (overscaledZ - canonical.z); -} - -inline bool OverscaledTileID::isChildOf(const OverscaledTileID& rhs) const { - return overscaledZ > rhs.overscaledZ && - (canonical == rhs.canonical || canonical.isChildOf(rhs.canonical)); -} - -inline OverscaledTileID OverscaledTileID::scaledTo(uint8_t z) const { - return { z, wrap, z >= canonical.z ? canonical : canonical.scaledTo(z) }; -} - -inline UnwrappedTileID OverscaledTileID::toUnwrapped() const { - return { wrap, canonical }; -} - -inline UnwrappedTileID::UnwrappedTileID(uint8_t z_, int64_t x_, int64_t y_) - : wrap((x_ < 0 ? x_ - (1ll << z_) + 1 : x_) / (1ll << z_)), - canonical( - z_, - static_cast(x_ - wrap * (1ll << z_)), - y_ < 0 ? 0 : std::min(static_cast(y_), static_cast(1ull << z_) - 1)) { -} - -inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, CanonicalTileID canonical_) - : wrap(wrap_), canonical(std::move(canonical_)) { -} - -inline bool UnwrappedTileID::operator==(const UnwrappedTileID& rhs) const { - return wrap == rhs.wrap && canonical == rhs.canonical; -} - -inline bool UnwrappedTileID::operator!=(const UnwrappedTileID& rhs) const { - return wrap != rhs.wrap || canonical != rhs.canonical; -} - -inline bool UnwrappedTileID::operator<(const UnwrappedTileID& rhs) const { - return std::tie(wrap, canonical) < std::tie(rhs.wrap, rhs.canonical); -} - -inline bool UnwrappedTileID::isChildOf(const UnwrappedTileID& parent) const { - return wrap == parent.wrap && canonical.isChildOf(parent.canonical); -} - -inline std::array UnwrappedTileID::children() const { - const uint8_t childZ = canonical.z + 1; - const uint32_t childX = canonical.x * 2; - const uint32_t childY = canonical.y * 2; - return { { - { wrap, { childZ, childX, childY } }, - { wrap, { childZ, childX, childY + 1 } }, - { wrap, { childZ, childX + 1, childY } }, - { wrap, { childZ, childX + 1, childY + 1 } }, - } }; -} - -inline OverscaledTileID UnwrappedTileID::overscaleTo(const uint8_t overscaledZ) const { - assert(overscaledZ >= canonical.z); - return { overscaledZ, wrap, canonical }; -} - -inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const float zoom) const { - return pixelValue * (util::EXTENT / (util::tileSize * std::pow(2, zoom - canonical.z))); -} - -} // namespace mbgl - -namespace std { - -template <> struct hash { - size_t operator()(const mbgl::CanonicalTileID &id) const { - std::size_t seed = 0; - boost::hash_combine(seed, id.x); - boost::hash_combine(seed, id.y); - boost::hash_combine(seed, id.z); - return seed; - } -}; - -template <> struct hash { - size_t operator()(const mbgl::UnwrappedTileID &id) const { - std::size_t seed = 0; - boost::hash_combine(seed, std::hash{}(id.canonical)); - boost::hash_combine(seed, id.wrap); - return seed; - } -}; - -template <> struct hash { - size_t operator()(const mbgl::OverscaledTileID &id) const { - std::size_t seed = 0; - boost::hash_combine(seed, std::hash{}(id.canonical)); - boost::hash_combine(seed, id.overscaledZ); - return seed; - } -}; - -} // namespace std - diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp deleted file mode 100644 index 572f46080e..0000000000 --- a/src/mbgl/util/thread.hpp +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mbgl { -namespace util { - -// Manages a thread with `Object`. - -// Upon creation of this object, it launches a thread and creates an object of type `Object` -// in that thread. When the `Thread<>` object is destructed, the destructor waits -// for thread termination. The `Thread<>` constructor blocks until the thread and -// the `Object` are fully created, so after the object creation, it's safe to obtain the -// `Object` stored in this thread. The thread created will always have low priority on -// the platforms that support setting thread priority. -// -// The following properties make this class different from `ThreadPool`: -// -// - Only one thread is created. -// - `Object` will live in a single thread, providing thread affinity. -// - It is safe to use `ThreadLocal` in an `Object` managed by `Thread<>` -// - A `RunLoop` is created for the `Object` thread. -// - `Object` can use `Timer` and do asynchronous I/O, like wait for sockets events. -// -template -class Thread : public Scheduler { -public: - template - Thread(const std::string& name, Args&&... args) { - std::promise running; - - thread = std::thread([&] { - platform::setCurrentThreadName(name); - platform::makeThreadLowPriority(); - - util::RunLoop loop_(util::RunLoop::Type::New); - loop = &loop_; - - object = std::make_unique>(*this, std::forward(args)...); - running.set_value(); - - loop->run(); - loop = nullptr; - }); - - running.get_future().get(); - } - - ~Thread() override { - MBGL_VERIFY_THREAD(tid); - - if (paused) { - resume(); - } - - std::promise joinable; - - // Kill the actor, so we don't get more - // messages posted on this scheduler after - // we delete the RunLoop. - loop->invoke([&] { - object.reset(); - joinable.set_value(); - }); - - joinable.get_future().get(); - - loop->stop(); - thread.join(); - } - - // Returns a non-owning reference to `Object` that - // can be used to send messages to `Object`. It is safe - // to the non-owning reference to outlive this object - // and be used after the `Thread<>` gets destroyed. - ActorRef> actor() const { - return object->self(); - } - - // Pauses the `Object` thread. It will prevent the object to wake - // up from events such as timers and file descriptor I/O. Messages - // sent to a paused `Object` will be queued and only processed after - // `resume()` is called. - void pause() { - MBGL_VERIFY_THREAD(tid); - - assert(!paused); - - paused = std::make_unique>(); - resumed = std::make_unique>(); - - auto pausing = paused->get_future(); - - loop->invoke([this] { - auto resuming = resumed->get_future(); - paused->set_value(); - resuming.get(); - }); - - pausing.get(); - } - - // Resumes the `Object` thread previously paused by `pause()`. - void resume() { - MBGL_VERIFY_THREAD(tid); - - assert(paused); - - resumed->set_value(); - - resumed.reset(); - paused.reset(); - } - -private: - MBGL_STORE_THREAD(tid); - - void schedule(std::weak_ptr mailbox) override { - { - std::lock_guard lock(mutex); - queue.push(mailbox); - } - - loop->invoke([this] { receive(); }); - } - - void receive() { - std::unique_lock lock(mutex); - - auto mailbox = queue.front(); - queue.pop(); - lock.unlock(); - - Mailbox::maybeReceive(mailbox); - } - - std::mutex mutex; - std::queue> queue; - std::thread thread; - std::unique_ptr> object; - - std::unique_ptr> paused; - std::unique_ptr> resumed; - - util::RunLoop* loop = nullptr; -}; - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index c81becb986..39b562d811 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -182,10 +182,10 @@ uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_) auto y1 = floor(sw.y/ tileSize_); auto y2 = floor((ne.y - 1) / tileSize_); - auto minX = std::fmax(std::min(x1, x2), 0); + auto minX = ::fmax(std::min(x1, x2), 0); auto maxX = std::max(x1, x2); auto minY = (std::pow(2, zoom) - 1) - std::max(y1, y2); - auto maxY = (std::pow(2, zoom) - 1) - std::fmax(std::min(y1, y2), 0); + auto maxY = (std::pow(2, zoom) - 1) - ::fmax(std::min(y1, y2), 0); return (maxX - minX + 1) * (maxY - minY + 1); } -- cgit v1.2.1