summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli@qt.io>2017-08-10 11:37:56 +0200
committerPaolo Angelelli <paolo.angelelli@qt.io>2017-08-10 11:38:31 +0200
commit93e0c9cbca1a66b2fc6ec1d2b12e25b5e734aef0 (patch)
tree65479a233204bbdf790794192976a8b347c3406a /src
parent677f7f76d4a0023d278b4d031d620fe63c3be27a (diff)
parentc832af789766fcebd8cfb15e53ce14f36278ca6d (diff)
downloadqtlocation-93e0c9cbca1a66b2fc6ec1d2b12e25b5e734aef0.tar.gz
Merge remote-tracking branch 'origin/5.9' into dev
Conflicts: tests/auto/declarative_ui/tst_map.qml Change-Id: If408b8242b39f78926b5373393f3b1f65b262499
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/earcut/LICENSE15
-rw-r--r--src/3rdparty/earcut/earcut.hpp779
-rw-r--r--src/3rdparty/earcut/qt_attribution.json13
-rw-r--r--src/location/declarativemaps/qdeclarativecirclemapitem.cpp29
-rw-r--r--src/location/declarativemaps/qdeclarativecirclemapitem_p.h1
-rw-r--r--src/location/declarativemaps/qdeclarativegeomap.cpp63
-rw-r--r--src/location/declarativemaps/qdeclarativegeomap_p.h2
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapquickitem.cpp9
-rw-r--r--src/location/declarativemaps/qdeclarativepolygonmapitem.cpp89
-rw-r--r--src/location/declarativemaps/qdeclarativepolylinemapitem.cpp115
-rw-r--r--src/location/declarativemaps/qquickgeomapgesturearea.cpp2
-rw-r--r--src/location/declarativemaps/qquickgeomapgesturearea_p.h4
-rw-r--r--src/location/location.pro1
-rw-r--r--src/location/maps/qgeomappingmanager.cpp6
-rw-r--r--src/location/maps/qgeoprojection.cpp114
-rw-r--r--src/location/maps/qgeoprojection_p.h7
-rw-r--r--src/location/maps/qgeoroute.cpp4
-rw-r--r--src/plugins/geoservices/osm/qgeotileproviderosm.cpp2
-rw-r--r--src/positioning/qlocationutils_p.h2
-rw-r--r--src/src.pro3
20 files changed, 1025 insertions, 235 deletions
diff --git a/src/3rdparty/earcut/LICENSE b/src/3rdparty/earcut/LICENSE
new file mode 100644
index 00000000..8bafb577
--- /dev/null
+++ b/src/3rdparty/earcut/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/src/3rdparty/earcut/earcut.hpp b/src/3rdparty/earcut/earcut.hpp
new file mode 100644
index 00000000..287be028
--- /dev/null
+++ b/src/3rdparty/earcut/earcut.hpp
@@ -0,0 +1,779 @@
+#pragma once
+#ifndef EARCUT_HPP
+#define EARCUT_HPP
+
+#include <array>
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <memory>
+#include <vector>
+
+namespace mapbox {
+
+namespace util {
+
+template <std::size_t I, typename T> struct nth {
+
+ inline static typename std::tuple_element<I, T>::type
+ get(const T& t) { return std::get<I>(t); }
+};
+
+
+}
+
+namespace detail {
+
+template <typename N = uint32_t>
+class Earcut {
+public:
+ std::vector<N> indices;
+ N vertices = 0;
+
+ template <typename Polygon>
+ 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 <typename Ring> 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 <typename Polygon> 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 <typename Point> Node* insertNode(N i, const Point& p, Node* last);
+ void removeNode(Node* p);
+
+ bool hashing;
+ double minX, maxX;
+ double minY, maxY;
+ double size;
+
+ template <typename T, typename Alloc = std::allocator<T>>
+ class ObjectPool {
+ public:
+ ObjectPool() { }
+ ObjectPool(std::size_t blockSize_) {
+ reset(blockSize_);
+ }
+ ~ObjectPool() {
+ clear();
+ }
+ template <typename... Args>
+ T* construct(Args&&... args) {
+ if (currentIndex >= blockSize) {
+ currentBlock = alloc.allocate(blockSize);
+ allocations.emplace_back(currentBlock);
+ currentIndex = 0;
+ }
+ T* object = &currentBlock[currentIndex++];
+ alloc.construct(object, std::forward<Args>(args)...);
+ return object;
+ }
+ void reset(std::size_t newBlockSize) {
+ for (auto allocation : allocations) alloc.deallocate(allocation, blockSize);
+ allocations.clear();
+ blockSize = std::max<std::size_t>(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<T*> allocations;
+ Alloc alloc;
+ };
+ ObjectPool<Node> nodes;
+};
+
+template <typename N> template <typename Polygon>
+void Earcut<N>::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<int>(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 <typename N> template <typename Ring>
+typename Earcut<N>::Node*
+Earcut<N>::linkedList(const Ring& points, const bool clockwise) {
+ using Point = typename Ring::value_type;
+ double sum = 0;
+ const int len = static_cast<int>(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 N>
+typename Earcut<N>::Node*
+Earcut<N>::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 <typename N>
+void Earcut<N>::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 <typename N>
+bool Earcut<N>::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 <typename N>
+bool Earcut<N>::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 N>
+typename Earcut<N>::Node*
+Earcut<N>::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 <typename N>
+void Earcut<N>::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 <typename N> template <typename Polygon>
+typename Earcut<N>::Node*
+Earcut<N>::eliminateHoles(const Polygon& points, Node* outerNode) {
+ const size_t len = points.size();
+
+ std::vector<Node*> 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 <typename N>
+void Earcut<N>::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 N>
+typename Earcut<N>::Node*
+Earcut<N>::findHoleBridge(Node* hole, Node* outerNode) {
+ Node* p = outerNode;
+ double hx = hole->x;
+ double hy = hole->y;
+ double qx = -std::numeric_limits<double>::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<double>::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 <typename N>
+void Earcut<N>::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 N>
+typename Earcut<N>::Node*
+Earcut<N>::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 <typename N>
+int32_t Earcut<N>::zOrder(const double x_, const double y_) {
+ // coords are transformed into non-negative 15-bit integer range
+ int32_t x = static_cast<int32_t>(32767.0 * (x_ - minX) / size);
+ int32_t y = static_cast<int32_t>(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 N>
+typename Earcut<N>::Node*
+Earcut<N>::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 <typename N>
+bool Earcut<N>::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 <typename N>
+bool Earcut<N>::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 <typename N>
+double Earcut<N>::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 <typename N>
+bool Earcut<N>::equals(const Node* p1, const Node* p2) {
+ return p1->x == p2->x && p1->y == p2->y;
+}
+
+// check if two segments intersect
+template <typename N>
+bool Earcut<N>::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 <typename N>
+bool Earcut<N>::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 <typename N>
+bool Earcut<N>::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 <typename N>
+bool Earcut<N>::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 N>
+typename Earcut<N>::Node*
+Earcut<N>::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 <typename N> template <typename Point>
+typename Earcut<N>::Node*
+Earcut<N>::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 <typename N>
+void Earcut<N>::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 <typename N = uint32_t, typename Polygon>
+std::vector<N> earcut(const Polygon& poly) {
+ mapbox::detail::Earcut<N> earcut;
+ earcut(poly);
+ return earcut.indices;
+}
+}
+#endif //EARCUT_HPP
diff --git a/src/3rdparty/earcut/qt_attribution.json b/src/3rdparty/earcut/qt_attribution.json
new file mode 100644
index 00000000..0e52989c
--- /dev/null
+++ b/src/3rdparty/earcut/qt_attribution.json
@@ -0,0 +1,13 @@
+{
+ "Id": "earcut",
+ "Name": "Earcut Polygon Triangulation Library",
+ "QDocModule": "qtlocation",
+ "QtUsage": "Used in the QML plugin of Qt Location.",
+
+ "Description": "A C++ port of earcut.js, a fast, header-only polygon triangulation library.",
+ "Homepage": "https://github.com/mapbox/earcut.hpp",
+ "LicenseId": "ISC",
+ "License": "ISC License",
+ "LicenseFile": "LICENSE",
+ "Copyright": "Copyright (c) 2015 Mapbox"
+}
diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem.cpp b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp
index 274225c0..5f002bf9 100644
--- a/src/location/declarativemaps/qdeclarativecirclemapitem.cpp
+++ b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp
@@ -278,7 +278,8 @@ static bool crossEarthPole(const QGeoCoordinate &center, qreal distance)
static void calculatePeripheralPoints(QList<QGeoCoordinate> &path,
const QGeoCoordinate &center,
qreal distance,
- int steps)
+ int steps,
+ QGeoCoordinate &leftBound)
{
// Calculate points based on great-circle distance
// Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function
@@ -287,15 +288,17 @@ static void calculatePeripheralPoints(QList<QGeoCoordinate> &path,
// pre-calculations
steps = qMax(steps, 3);
qreal centerLon = center.longitude();
+ qreal minLon = centerLon;
qreal latRad = QLocationUtils::radians(center.latitude());
qreal lonRad = QLocationUtils::radians(centerLon);
qreal cosLatRad = std::cos(latRad);
qreal sinLatRad = std::sin(latRad);
- qreal ratio = (distance / (QLocationUtils::earthMeanRadius()));
+ qreal ratio = (distance / QLocationUtils::earthMeanRadius());
qreal cosRatio = std::cos(ratio);
qreal sinRatio = std::sin(ratio);
qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
+ int idx = 0;
for (int i = 0; i < steps; ++i) {
qreal azimuthRad = 2 * M_PI * i / steps;
qreal resultLatRad = std::asin(sinLatRad_x_cosRatio
@@ -306,7 +309,17 @@ static void calculatePeripheralPoints(QList<QGeoCoordinate> &path,
qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad));
path << QGeoCoordinate(lat2, lon2, center.altitude());
+ // Consider only points in the left half of the circle for the left bound.
+ if (azimuthRad > M_PI) {
+ if (lon2 > centerLon) // if point and center are on different hemispheres
+ lon2 -= 360;
+ if (lon2 < minLon) {
+ minLon = lon2;
+ idx = i;
+ }
+ }
}
+ leftBound = path.at(idx);
}
QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent)
@@ -480,8 +493,10 @@ void QDeclarativeCircleMapItem::updatePolish()
int pathCount = circlePath.size();
bool preserve = preserveCircleGeometry(circlePath, circle_.center(), circle_.radius());
- geometry_.setPreserveGeometry(true, circle_.boundingGeoRectangle().topLeft()); // to set the geoLeftBound_
- geometry_.setPreserveGeometry(preserve, circle_.boundingGeoRectangle().topLeft());
+ // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft());
+ // to fix QTBUG-62154
+ geometry_.setPreserveGeometry(true, leftBound_); // to set the geoLeftBound_
+ geometry_.setPreserveGeometry(preserve, leftBound_);
bool invertedCircle = false;
if (crossEarthPole(circle_.center(), circle_.radius()) && circlePath.size() == pathCount) {
@@ -506,8 +521,8 @@ void QDeclarativeCircleMapItem::updatePolish()
std::reverse(closedPath.begin(), closedPath.end());
}
- borderGeometry_.setPreserveGeometry(true, circle_.boundingGeoRectangle().topLeft()); // to set the geoLeftBound_
- borderGeometry_.setPreserveGeometry(preserve, circle_.boundingGeoRectangle().topLeft());
+ borderGeometry_.setPreserveGeometry(true, leftBound_);
+ borderGeometry_.setPreserveGeometry(preserve, leftBound_);
// Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail.
const QGeoCoordinate &geometryOrigin = geometry_.origin();
@@ -553,7 +568,7 @@ void QDeclarativeCircleMapItem::updateCirclePath()
if (!map())
return;
QList<QGeoCoordinate> path;
- calculatePeripheralPoints(path, circle_.center(), circle_.radius(), CircleSamples);
+ calculatePeripheralPoints(path, circle_.center(), circle_.radius(), CircleSamples, leftBound_);
circlePath_.clear();
for (const QGeoCoordinate &c : path)
circlePath_ << map()->geoProjection().geoToMapProjection(c);
diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem_p.h b/src/location/declarativemaps/qdeclarativecirclemapitem_p.h
index 511e3b17..15774427 100644
--- a/src/location/declarativemaps/qdeclarativecirclemapitem_p.h
+++ b/src/location/declarativemaps/qdeclarativecirclemapitem_p.h
@@ -121,6 +121,7 @@ private:
QDeclarativeMapLineProperties border_;
QColor color_;
QList<QDoubleVector2D> circlePath_;
+ QGeoCoordinate leftBound_;
bool dirtyMaterial_;
QGeoMapCircleGeometry geometry_;
QGeoMapPolylineGeometry borderGeometry_;
diff --git a/src/location/declarativemaps/qdeclarativegeomap.cpp b/src/location/declarativemaps/qdeclarativegeomap.cpp
index 19bc57ce..37cfc303 100644
--- a/src/location/declarativemaps/qdeclarativegeomap.cpp
+++ b/src/location/declarativemaps/qdeclarativegeomap.cpp
@@ -255,6 +255,8 @@ QDeclarativeGeoMap::~QDeclarativeGeoMap()
delete m_copyrights.data();
m_copyrights.clear();
+
+ delete m_map;
}
/*!
@@ -293,17 +295,17 @@ void QDeclarativeGeoMap::onMapChildrenChanged()
copyrights = m_copyrights.data();
- connect(m_map.data(), SIGNAL(copyrightsChanged(QImage)),
+ connect(m_map, SIGNAL(copyrightsChanged(QImage)),
copyrights, SLOT(copyrightsChanged(QImage)));
- connect(m_map.data(), SIGNAL(copyrightsChanged(QImage)),
+ connect(m_map, SIGNAL(copyrightsChanged(QImage)),
this, SIGNAL(copyrightsChanged(QImage)));
- connect(m_map.data(), SIGNAL(copyrightsChanged(QString)),
+ connect(m_map, SIGNAL(copyrightsChanged(QString)),
copyrights, SLOT(copyrightsChanged(QString)));
- connect(m_map.data(), SIGNAL(copyrightsChanged(QString)),
+ connect(m_map, SIGNAL(copyrightsChanged(QString)),
this, SIGNAL(copyrightsChanged(QString)));
- connect(m_map.data(), SIGNAL(copyrightsStyleSheetChanged(QString)),
+ connect(m_map, SIGNAL(copyrightsStyleSheetChanged(QString)),
copyrights, SLOT(onCopyrightsStyleSheetChanged(QString)));
connect(copyrights, SIGNAL(linkActivated(QString)),
@@ -739,7 +741,7 @@ void QDeclarativeGeoMap::onCameraCapabilitiesChanged(const QGeoCameraCapabilitie
*/
void QDeclarativeGeoMap::mappingManagerInitialized()
{
- m_map = QPointer<QGeoMap>(m_mappingManager->createMap(this));
+ m_map = m_mappingManager->createMap(this);
if (!m_map)
return;
@@ -784,11 +786,11 @@ void QDeclarativeGeoMap::mappingManagerInitialized()
QImage copyrightImage;
if (!m_initialized && width() > 0 && height() > 0) {
QMetaObject::Connection copyrightStringCatcherConnection =
- connect(m_map.data(),
+ connect(m_map,
QOverload<const QString &>::of(&QGeoMap::copyrightsChanged),
[&copyrightString](const QString &copy){ copyrightString = copy; });
QMetaObject::Connection copyrightImageCatcherConnection =
- connect(m_map.data(),
+ connect(m_map,
QOverload<const QImage &>::of(&QGeoMap::copyrightsChanged),
[&copyrightImage](const QImage &copy){ copyrightImage = copy; });
m_map->setViewportSize(QSize(width(), height()));
@@ -800,28 +802,28 @@ void QDeclarativeGeoMap::mappingManagerInitialized()
m_copyrights = new QDeclarativeGeoMapCopyrightNotice(this);
m_copyrights->onCopyrightsStyleSheetChanged(m_map->copyrightsStyleSheet());
- connect(m_map.data(), SIGNAL(copyrightsChanged(QImage)),
+ connect(m_map, SIGNAL(copyrightsChanged(QImage)),
m_copyrights.data(), SLOT(copyrightsChanged(QImage)));
- connect(m_map.data(), SIGNAL(copyrightsChanged(QImage)),
+ connect(m_map, SIGNAL(copyrightsChanged(QImage)),
this, SIGNAL(copyrightsChanged(QImage)));
- connect(m_map.data(), SIGNAL(copyrightsChanged(QString)),
+ connect(m_map, SIGNAL(copyrightsChanged(QString)),
m_copyrights.data(), SLOT(copyrightsChanged(QString)));
- connect(m_map.data(), SIGNAL(copyrightsChanged(QString)),
+ connect(m_map, SIGNAL(copyrightsChanged(QString)),
this, SIGNAL(copyrightsChanged(QString)));
if (!copyrightString.isEmpty())
- emit m_map.data()->copyrightsChanged(copyrightString);
+ emit m_map->copyrightsChanged(copyrightString);
else if (!copyrightImage.isNull())
- emit m_map.data()->copyrightsChanged(copyrightImage);
+ emit m_map->copyrightsChanged(copyrightImage);
- connect(m_map.data(), SIGNAL(copyrightsStyleSheetChanged(QString)),
+ connect(m_map, SIGNAL(copyrightsStyleSheetChanged(QString)),
m_copyrights.data(), SLOT(onCopyrightsStyleSheetChanged(QString)));
connect(m_copyrights.data(), SIGNAL(linkActivated(QString)),
this, SIGNAL(copyrightLinkActivated(QString)));
- connect(m_map.data(), &QGeoMap::sgNodeChanged, this, &QQuickItem::update);
- connect(m_map.data(), &QGeoMap::cameraCapabilitiesChanged, this, &QDeclarativeGeoMap::onCameraCapabilitiesChanged);
+ connect(m_map, &QGeoMap::sgNodeChanged, this, &QQuickItem::update);
+ connect(m_map, &QGeoMap::cameraCapabilitiesChanged, this, &QDeclarativeGeoMap::onCameraCapabilitiesChanged);
// set visibility of copyright notice
m_copyrights->setCopyrightsVisible(m_copyrightsVisible);
@@ -1340,20 +1342,21 @@ QGeoShape QDeclarativeGeoMap::visibleRegion() const
const QList<QDoubleVector2D> &visibleRegion = m_map->geoProjection().visibleRegion();
QGeoPath path;
- for (const QDoubleVector2D &c: visibleRegion)
+ for (int i = 0; i < visibleRegion.size(); ++i) {
+ const QDoubleVector2D &c = visibleRegion.at(i);
+ // If a segment spans more than half of the map longitudinally, split in 2.
+ if (i && qAbs(visibleRegion.at(i-1).x() - c.x()) >= 0.5) { // This assumes a segment is never >= 1.0 (whole map span)
+ QDoubleVector2D extraPoint = (visibleRegion.at(i-1) + c) * 0.5;
+ path.addCoordinate(m_map->geoProjection().wrappedMapProjectionToGeo(extraPoint));
+ }
path.addCoordinate(m_map->geoProjection().wrappedMapProjectionToGeo(c));
-
- QGeoRectangle vr = path.boundingGeoRectangle();
-
- bool empty = vr.topLeft().latitude() == vr.bottomRight().latitude() ||
- qFuzzyCompare(vr.topLeft().longitude(), vr.bottomRight().longitude()); // QTBUG-57690
-
- if (empty) {
- vr.setTopLeft(QGeoCoordinate(vr.topLeft().latitude(), -180));
- vr.setBottomRight(QGeoCoordinate(vr.bottomRight().latitude(), 180));
+ }
+ if (visibleRegion.size() >= 2 && qAbs(visibleRegion.last().x() - visibleRegion.first().x()) >= 0.5) {
+ QDoubleVector2D extraPoint = (visibleRegion.last() + visibleRegion.first()) * 0.5;
+ path.addCoordinate(m_map->geoProjection().wrappedMapProjectionToGeo(extraPoint));
}
- return vr;
+ return path.boundingGeoRectangle();
}
/*!
@@ -1440,7 +1443,7 @@ void QDeclarativeGeoMap::fitViewportToGeoShape()
QGeoCoordinate centerCoordinate = m_map->geoProjection().mapProjectionToGeo(center);
// position camera to the center of bounding box
- setCenter(centerCoordinate);
+ setProperty("center", QVariant::fromValue(centerCoordinate)); // not using setCenter(centerCoordinate) to honor a possible animation set on the center property
// if the shape is empty we just change center position, not zoom
double bboxWidth = (bottomRightPoint.x() - topLeftPoint.x()) * m_map->mapWidth();
@@ -1453,7 +1456,7 @@ void QDeclarativeGeoMap::fitViewportToGeoShape()
bboxHeight / (height() - margins));
zoomRatio = std::log(zoomRatio) / std::log(2.0);
double newZoom = qMax<double>(minimumZoomLevel(), zoomLevel() - zoomRatio);
- setZoomLevel(newZoom);
+ setProperty("zoomLevel", QVariant::fromValue(newZoom)); // not using setZoomLevel(newZoom) to honor a possible animation set on the zoomLevel property
}
diff --git a/src/location/declarativemaps/qdeclarativegeomap_p.h b/src/location/declarativemaps/qdeclarativegeomap_p.h
index 90e73420..5c568d8f 100644
--- a/src/location/declarativemaps/qdeclarativegeomap_p.h
+++ b/src/location/declarativemaps/qdeclarativegeomap_p.h
@@ -258,7 +258,7 @@ private:
QList<QDeclarativeGeoMapType *> m_supportedMapTypes;
QList<QDeclarativeGeoMapItemView *> m_mapViews;
QQuickGeoMapGestureArea *m_gestureArea;
- QPointer<QGeoMap> m_map;
+ QGeoMap* m_map = nullptr;
QPointer<QDeclarativeGeoMapCopyrightNotice> m_copyrights;
QList<QPointer<QDeclarativeGeoMapItemBase> > m_mapItems;
QList<QPointer<QDeclarativeGeoMapItemGroup> > m_mapItemGroups;
diff --git a/src/location/declarativemaps/qdeclarativegeomapquickitem.cpp b/src/location/declarativemaps/qdeclarativegeomapquickitem.cpp
index 86d67cf8..7c0764aa 100644
--- a/src/location/declarativemaps/qdeclarativegeomapquickitem.cpp
+++ b/src/location/declarativemaps/qdeclarativegeomapquickitem.cpp
@@ -159,7 +159,7 @@ QDeclarativeGeoMapQuickItem::~QDeclarativeGeoMapQuickItem() {}
*/
void QDeclarativeGeoMapQuickItem::setCoordinate(const QGeoCoordinate &coordinate)
{
- if (coordinate_ == coordinate || !coordinate.isValid())
+ if (coordinate_ == coordinate)
return;
coordinate_ = coordinate;
@@ -378,6 +378,13 @@ void QDeclarativeGeoMapQuickItem::updatePolish()
this, SLOT(polishAndUpdate()));
}
+ if (!coordinate_.isValid()) {
+ opacityContainer_->setVisible(false);
+ return;
+ } else {
+ opacityContainer_->setVisible(true);
+ }
+
QScopedValueRollback<bool> rollback(updatingGeometry_);
updatingGeometry_ = true;
diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp
index daa1a9fc..48f66423 100644
--- a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp
+++ b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp
@@ -53,6 +53,8 @@
/* poly2tri triangulator includes */
#include <clip2tri.h>
+#include <earcut.hpp>
+#include <array>
QT_BEGIN_NAMESPACE
@@ -185,7 +187,7 @@ void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map,
// 2)
QList<QList<QDoubleVector2D> > clippedPaths;
- const QList<QDoubleVector2D> &visibleRegion = map.geoProjection().visibleRegion();
+ const QList<QDoubleVector2D> &visibleRegion = map.geoProjection().projectableRegion();
if (visibleRegion.size()) {
c2t::clip2tri clipper;
clipper.addSubjectPath(QClipperUtils::qListToPath(wrappedPath), true);
@@ -254,16 +256,6 @@ void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map)
return;
}
- QDoubleVector2D origin = map.geoProjection().coordinateToItemPosition(srcOrigin_, false);
-
- // Create the viewport rect in the same coordinate system
- // as the actual points
- QRectF viewport(0, 0, map.viewportWidth(), map.viewportHeight());
- viewport.translate(-1 * origin.toPointF());
-
- QPainterPath vpPath;
- vpPath.addRect(viewport);
-
// The geometry has already been clipped against the visible region projection in wrapped mercator space.
QPainterPath ppi = srcPath_;
clear();
@@ -272,57 +264,50 @@ void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map)
if (ppi.elementCount() < 3)
return;
- // Intersection between the viewport and a concave polygon can create multiple polygons
- // joined by a line at the viewport border, and poly2tri does not triangulate this very well
- // so use the full src path if the resulting polygon is concave.
- if (clipToViewport_) {
- int changeInX = 0;
- int changeInY = 0;
- QPainterPath::Element e1 = ppi.elementAt(1);
- QPainterPath::Element e = ppi.elementAt(0);
- QVector2D edgeA(e1.x - e.x ,e1.y - e.y);
- for (int i = 2; i <= ppi.elementCount(); ++i) {
- e = ppi.elementAt(i % ppi.elementCount());
- if (e.x == e1.x && e.y == e1.y)
- continue;
- QVector2D edgeB(e.x - e1.x, e.y - e1.y);
- if ((edgeA.x() < 0) == (edgeB.x() >= 0))
- changeInX++;
- if ((edgeA.y() < 0) == (edgeB.y() >= 0))
- changeInY++;
- edgeA = edgeB;
- e1 = e;
- }
- if (changeInX > 2 || changeInY > 2) // polygon is concave
- ppi = srcPath_;
- }
-
// translate the path into top-left-centric coordinates
QRectF bb = ppi.boundingRect();
ppi.translate(-bb.left(), -bb.top());
firstPointOffset_ = -1 * bb.topLeft();
ppi.closeSubpath();
-
screenOutline_ = ppi;
- QTriangleSet ts = qTriangulate(ppi);
- qreal *vx = ts.vertices.data();
-
- screenIndices_.reserve(ts.indices.size());
- screenVertices_.reserve(ts.vertices.size());
+ using Coord = double;
+ using N = uint32_t;
+ using Point = std::array<Coord, 2>;
+
+ std::vector<std::vector<Point>> polygon;
+ polygon.push_back(std::vector<Point>());
+ std::vector<Point> &poly = polygon.front();
+ // ... fill polygon structure with actual data
+
+ for (int i = 0; i < ppi.elementCount(); ++i) {
+ const QPainterPath::Element e = ppi.elementAt(i);
+ if (e.isMoveTo() || i == ppi.elementCount() - 1
+ || (qAbs(e.x - poly.front()[0]) < 0.1
+ && qAbs(e.y - poly.front()[1]) < 0.1)) {
+ Point p = { e.x, e.y };
+ poly.push_back( p );
+ } else if (e.isLineTo()) {
+ Point p = { e.x, e.y };
+ poly.push_back( p );
+ } else {
+ qWarning("Unhandled element type in polygon painterpath");
+ }
+ }
- if (ts.indices.type() == QVertexIndexVector::UnsignedInt) {
- const quint32 *ix = reinterpret_cast<const quint32 *>(ts.indices.data());
- for (int i = 0; i < (ts.indices.size()/3*3); ++i)
- screenIndices_ << ix[i];
- } else {
- const quint16 *ix = reinterpret_cast<const quint16 *>(ts.indices.data());
- for (int i = 0; i < (ts.indices.size()/3*3); ++i)
- screenIndices_ << ix[i];
+ if (poly.size() > 2) {
+ // Run tessellation
+ // Returns array of indices that refer to the vertices of the input polygon.
+ // Three subsequent indices form a triangle.
+ screenVertices_.clear();
+ screenIndices_.clear();
+ for (const auto &p : poly)
+ screenVertices_ << QPointF(p[0], p[1]);
+ std::vector<N> indices = mapbox::earcut<N>(polygon);
+ for (const auto &i: indices)
+ screenIndices_ << quint32(i);
}
- for (int i = 0; i < (ts.vertices.size()/2*2); i += 2)
- screenVertices_ << QPointF(vx[i], vx[i + 1]);
screenBounds_ = ppi.boundingRect();
}
diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
index 72c43a49..a97271aa 100644
--- a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
+++ b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
@@ -226,7 +226,7 @@ QList<QList<QDoubleVector2D> > QGeoMapPolylineGeometry::clipPath(const QGeoMap &
// 2)
QList<QList<QDoubleVector2D> > clippedPaths;
- const QList<QDoubleVector2D> &visibleRegion = map.geoProjection().visibleRegion();
+ const QList<QDoubleVector2D> &visibleRegion = map.geoProjection().projectableRegion();
if (visibleRegion.size()) {
c2t::clip2tri clipper;
clipper.addSubjectPath(QClipperUtils::qListToPath(wrappedPath), false);
@@ -337,120 +337,7 @@ void QGeoMapPolylineGeometry::updateSourcePoints(const QGeoMap &map,
}
////////////////////////////////////////////////////////////////////////////
-#if 0 // Old polyline to viewport clipping code. Retaining it for now.
-/* Polyline clip */
-
-enum ClipPointType {
- InsidePoint = 0x00,
- LeftPoint = 0x01,
- RightPoint = 0x02,
- BottomPoint = 0x04,
- TopPoint = 0x08
-};
-
-static inline int clipPointType(qreal x, qreal y, const QRectF &rect)
-{
- int type = InsidePoint;
- if (x < rect.left())
- type |= LeftPoint;
- else if (x > rect.right())
- type |= RightPoint;
- if (y < rect.top())
- type |= TopPoint;
- else if (y > rect.bottom())
- type |= BottomPoint;
- return type;
-}
-
-static void clipSegmentToRect(qreal x0, qreal y0, qreal x1, qreal y1,
- const QRectF &clipRect,
- QVector<qreal> &outPoints,
- QVector<QPainterPath::ElementType> &outTypes)
-{
- int type0 = clipPointType(x0, y0, clipRect);
- int type1 = clipPointType(x1, y1, clipRect);
- bool accept = false;
-
- while (true) {
- if (!(type0 | type1)) {
- accept = true;
- break;
- } else if (type0 & type1) {
- break;
- } else {
- qreal x = 0.0;
- qreal y = 0.0;
- int outsideType = type0 ? type0 : type1;
-
- if (outsideType & BottomPoint) {
- x = x0 + (x1 - x0) * (clipRect.bottom() - y0) / (y1 - y0);
- y = clipRect.bottom() - 0.1;
- } else if (outsideType & TopPoint) {
- x = x0 + (x1 - x0) * (clipRect.top() - y0) / (y1 - y0);
- y = clipRect.top() + 0.1;
- } else if (outsideType & RightPoint) {
- y = y0 + (y1 - y0) * (clipRect.right() - x0) / (x1 - x0);
- x = clipRect.right() - 0.1;
- } else if (outsideType & LeftPoint) {
- y = y0 + (y1 - y0) * (clipRect.left() - x0) / (x1 - x0);
- x = clipRect.left() + 0.1;
- }
-
- if (outsideType == type0) {
- x0 = x;
- y0 = y;
- type0 = clipPointType(x0, y0, clipRect);
- } else {
- x1 = x;
- y1 = y;
- type1 = clipPointType(x1, y1, clipRect);
- }
- }
- }
-
- if (accept) {
- if (outPoints.size() >= 2) {
- qreal lastX, lastY;
- lastY = outPoints.at(outPoints.size() - 1);
- lastX = outPoints.at(outPoints.size() - 2);
-
- if (!qFuzzyCompare(lastY, y0) || !qFuzzyCompare(lastX, x0)) {
- outTypes << QPainterPath::MoveToElement;
- outPoints << x0 << y0;
- }
- } else {
- outTypes << QPainterPath::MoveToElement;
- outPoints << x0 << y0;
- }
-
- outTypes << QPainterPath::LineToElement;
- outPoints << x1 << y1;
- }
-}
-
-static void clipPathToRect(const QVector<qreal> &points,
- const QVector<QPainterPath::ElementType> &types,
- const QRectF &clipRect,
- QVector<qreal> &outPoints,
- QVector<QPainterPath::ElementType> &outTypes)
-{
- outPoints.clear();
- outPoints.reserve(points.size());
- outTypes.clear();
- outTypes.reserve(types.size());
- qreal lastX, lastY;
- for (int i = 0; i < types.size(); ++i) {
- if (i > 0 && types[i] != QPainterPath::MoveToElement) {
- qreal x = points[i * 2], y = points[i * 2 + 1];
- clipSegmentToRect(lastX, lastY, x, y, clipRect, outPoints, outTypes);
- }
-
- lastX = points[i * 2];
- lastY = points[i * 2 + 1];
- }
-}
-#endif
/*!
\internal
*/
diff --git a/src/location/declarativemaps/qquickgeomapgesturearea.cpp b/src/location/declarativemaps/qquickgeomapgesturearea.cpp
index 39fd2397..fc2debc3 100644
--- a/src/location/declarativemaps/qquickgeomapgesturearea.cpp
+++ b/src/location/declarativemaps/qquickgeomapgesturearea.cpp
@@ -498,7 +498,7 @@ QQuickGeoMapGestureArea::QQuickGeoMapGestureArea(QDeclarativeGeoMap *map)
/*!
\internal
*/
-void QQuickGeoMapGestureArea::setMap(QPointer<QGeoMap> map)
+void QQuickGeoMapGestureArea::setMap(QGeoMap *map)
{
if (m_map || !map)
return;
diff --git a/src/location/declarativemaps/qquickgeomapgesturearea_p.h b/src/location/declarativemaps/qquickgeomapgesturearea_p.h
index 5daa39cf..0bc774d3 100644
--- a/src/location/declarativemaps/qquickgeomapgesturearea_p.h
+++ b/src/location/declarativemaps/qquickgeomapgesturearea_p.h
@@ -178,7 +178,7 @@ public:
void setMaximumZoomLevel(qreal max);
qreal maximumZoomLevel() const;
- void setMap(QPointer<QGeoMap> map);
+ void setMap(QGeoMap* map);
bool preventStealing() const;
void setPreventStealing(bool prevent);
@@ -267,7 +267,7 @@ private:
void updateFlickParameters(const QPointF &pos);
private:
- QPointer<QGeoMap> m_map;
+ QGeoMap* m_map;
QDeclarativeGeoMap *m_declarativeMap;
bool m_enabled;
diff --git a/src/location/location.pro b/src/location/location.pro
index 5dc89a63..a951ebd6 100644
--- a/src/location/location.pro
+++ b/src/location/location.pro
@@ -10,6 +10,7 @@ CONFIG += simd optimize_full
# 3rdparty headers produce warnings with MSVC
msvc: CONFIG -= warning_clean
+INCLUDEPATH += ../3rdparty/earcut
INCLUDEPATH += ../3rdparty/poly2tri
INCLUDEPATH += ../3rdparty/clipper
INCLUDEPATH += ../3rdparty/clip2tri
diff --git a/src/location/maps/qgeomappingmanager.cpp b/src/location/maps/qgeomappingmanager.cpp
index cf040beb..d73050f7 100644
--- a/src/location/maps/qgeomappingmanager.cpp
+++ b/src/location/maps/qgeomappingmanager.cpp
@@ -133,10 +133,8 @@ int QGeoMappingManager::managerVersion() const
*/
QGeoMap *QGeoMappingManager::createMap(QObject *parent)
{
- QGeoMap * map = d_ptr->engine->createMap();
- if (map)
- connect(parent, &QObject::destroyed,map, &QGeoMap::deleteLater);
- return map;
+ Q_UNUSED(parent)
+ return d_ptr->engine->createMap();
}
QList<QGeoMapType> QGeoMappingManager::supportedMapTypes() const
diff --git a/src/location/maps/qgeoprojection.cpp b/src/location/maps/qgeoprojection.cpp
index 7fefba27..218d806b 100644
--- a/src/location/maps/qgeoprojection.cpp
+++ b/src/location/maps/qgeoprojection.cpp
@@ -212,14 +212,25 @@ QDoubleVector2D QGeoProjectionWebMercator::wrappedMapProjectionToItemPosition(co
QDoubleVector2D QGeoProjectionWebMercator::itemPositionToWrappedMapProjection(const QDoubleVector2D &itemPosition) const
{
QDoubleVector2D pos = itemPosition;
- // when the camera is tilted, picking a point above the horizon returns a coordinate behind the camera
- if (pos.y() < m_minimumUnprojectableY)
- pos.setY(m_minimumUnprojectableY);
pos *= QDoubleVector2D(m_1_viewportWidth, m_1_viewportHeight);
pos *= 2.0;
pos -= QDoubleVector2D(1.0,1.0);
- return viewportToWrappedMapProjection(pos);
+ double s;
+ QDoubleVector2D res = viewportToWrappedMapProjection(pos, s);
+
+ // a positive s means a point behind the camera. So do it again, after clamping Y. See QTBUG-61813
+ if (s > 0.0) {
+ pos = itemPosition;
+ // when the camera is tilted, picking a point above the horizon returns a coordinate behind the camera
+ pos.setY(m_minimumUnprojectableY);
+ pos *= QDoubleVector2D(m_1_viewportWidth, m_1_viewportHeight);
+ pos *= 2.0;
+ pos -= QDoubleVector2D(1.0,1.0);
+ res = viewportToWrappedMapProjection(pos, s);
+ }
+
+ return res;
}
/* Default implementations */
@@ -317,10 +328,23 @@ QList<QDoubleVector2D> QGeoProjectionWebMercator::visibleRegion() const
return m_visibleRegion;
}
+QList<QDoubleVector2D> QGeoProjectionWebMercator::projectableRegion() const
+{
+ if (m_visibleRegionDirty)
+ const_cast<QGeoProjectionWebMercator *>(this)->updateVisibleRegion();
+ return m_projectableRegion;
+}
+
+QDoubleVector2D QGeoProjectionWebMercator::viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition) const
+{
+ double s;
+ return viewportToWrappedMapProjection(itemPosition, s);
+}
+
/*
actual implementation of itemPositionToWrappedMapProjection
*/
-QDoubleVector2D QGeoProjectionWebMercator::viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition) const
+QDoubleVector2D QGeoProjectionWebMercator::viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition, double &s) const
{
QDoubleVector2D pos = itemPosition;
pos *= QDoubleVector2D(m_halfWidth, m_halfHeight);
@@ -332,7 +356,7 @@ QDoubleVector2D QGeoProjectionWebMercator::viewportToWrappedMapProjection(const
QDoubleVector3D ray = p - m_eye;
ray.normalize();
- return (xyPlane.lineIntersection(m_eye, ray) / m_sideLength).toVector2D();
+ return (xyPlane.lineIntersection(m_eye, ray, s) / m_sideLength).toVector2D();
}
void QGeoProjectionWebMercator::setupCamera()
@@ -344,6 +368,8 @@ void QGeoProjectionWebMercator::setupCamera()
int intZoomLevel = static_cast<int>(std::floor(m_cameraData.zoomLevel()));
m_sideLength = (1 << intZoomLevel) * defaultTileSize;
m_center = m_centerMercator * m_sideLength;
+ //aperture(90 / 2) = 1
+ m_aperture = tan(QLocationUtils::radians(m_cameraData.fieldOfView()) * 0.5);
double f = m_viewportHeight;
double z = std::pow(2.0, m_cameraData.zoomLevel() - intZoomLevel) * defaultTileSize;
@@ -352,15 +378,13 @@ void QGeoProjectionWebMercator::setupCamera()
double z_mercator = std::pow(2.0, m_cameraData.zoomLevel()) * defaultTileSize;
double altitude_mercator = f / (2.0 * z_mercator);
- //aperture(90 / 2) = 1
- m_aperture = tan(QLocationUtils::radians(m_cameraData.fieldOfView()) * 0.5);
// calculate eye
m_eye = m_center;
m_eye.setZ(altitude * defaultTileSize / m_aperture);
// And in mercator space
m_eyeMercator = m_centerMercator;
- m_eyeMercator.setZ(altitude_mercator);
+ m_eyeMercator.setZ(altitude_mercator / m_aperture);
m_view = m_eye - m_center;
QDoubleVector3D side = QDoubleVector3D::normal(m_view, QDoubleVector3D(0.0, 1.0, 0.0));
@@ -396,7 +420,7 @@ void QGeoProjectionWebMercator::setupCamera()
m_eyeMercator = mTiltMercator * m_viewMercator + m_centerMercator;
}
- m_view = m_eye - m_center;
+ m_view = m_eye - m_center; // ToDo: this should be inverted (center - eye), and the rest should follow
m_viewNormalized = m_view.normalized();
m_up = QDoubleVector3D::normal(m_view, m_side);
@@ -449,7 +473,7 @@ void QGeoProjectionWebMercator::setupCamera()
m_transformation.scale(m_sideLength, m_sideLength, 1.0);
m_centerNearPlane = m_eye + m_viewNormalized;
- m_centerNearPlaneMercator = m_eyeMercator + m_viewNormalized * m_nearPlaneMercator;
+ m_centerNearPlaneMercator = m_eyeMercator - m_viewNormalized * m_nearPlaneMercator;
// The method does not support tilting angles >= 90.0 or < 0.
@@ -478,11 +502,17 @@ void QGeoProjectionWebMercator::updateVisibleRegion()
QDoubleVector2D bl = viewportToWrappedMapProjection(QDoubleVector2D(-1, 1 ));
QDoubleVector2D br = viewportToWrappedMapProjection(QDoubleVector2D( 1, 1 ));
+ // To make sure that what is returned can be safely converted back to lat/lon without risking overlaps
+ double mapLeftLongitude = QLocationUtils::mapLeftLongitude(m_cameraData.center().longitude());
+ double mapRightLongitude = QLocationUtils::mapRightLongitude(m_cameraData.center().longitude());
+ double leftX = geoToWrappedMapProjection(QGeoCoordinate(0, mapLeftLongitude)).x();
+ double rightX = geoToWrappedMapProjection(QGeoCoordinate(0, mapRightLongitude)).x();
+
QList<QDoubleVector2D> mapRect;
- mapRect.push_back(QDoubleVector2D(-1.0, 1.0));
- mapRect.push_back(QDoubleVector2D( 2.0, 1.0));
- mapRect.push_back(QDoubleVector2D( 2.0, 0.0));
- mapRect.push_back(QDoubleVector2D(-1.0, 0.0));
+ mapRect.push_back(QDoubleVector2D(leftX, 1.0));
+ mapRect.push_back(QDoubleVector2D(rightX, 1.0));
+ mapRect.push_back(QDoubleVector2D(rightX, 0.0));
+ mapRect.push_back(QDoubleVector2D(leftX, 0.0));
QList<QDoubleVector2D> viewportRect;
viewportRect.push_back(bl);
@@ -499,6 +529,52 @@ void QGeoProjectionWebMercator::updateVisibleRegion()
m_visibleRegion.clear();
if (res.size())
m_visibleRegion = QClipperUtils::pathToQList(res[0]); // Intersection between two convex quadrilaterals should always be a single polygon
+
+ m_projectableRegion.clear();
+ mapRect.clear();
+ // The full map rectangle in extended mercator space
+ mapRect.push_back(QDoubleVector2D(-1.0, 1.0));
+ mapRect.push_back(QDoubleVector2D( 2.0, 1.0));
+ mapRect.push_back(QDoubleVector2D( 2.0, 0.0));
+ mapRect.push_back(QDoubleVector2D(-1.0, 0.0));
+ if (m_cameraData.tilt() == 0) {
+ m_projectableRegion = mapRect;
+ } else {
+ QGeoProjectionWebMercator::Plane nearPlane(m_centerNearPlaneMercator, m_viewNormalized);
+ Line2D nearPlaneXYIntersection = nearPlane.planeXYIntersection();
+ double squareHalfSide = qMax(5.0, nearPlaneXYIntersection.m_point.length());
+ QDoubleVector2D viewDirectionProjected = -m_viewNormalized.toVector2D().normalized();
+
+
+ QDoubleVector2D tl = nearPlaneXYIntersection.m_point
+ - squareHalfSide * nearPlaneXYIntersection.m_direction
+ + 2 * squareHalfSide * viewDirectionProjected;
+ QDoubleVector2D tr = nearPlaneXYIntersection.m_point
+ + squareHalfSide * nearPlaneXYIntersection.m_direction
+ + 2 * squareHalfSide * viewDirectionProjected;
+ QDoubleVector2D bl = nearPlaneXYIntersection.m_point
+ - squareHalfSide * nearPlaneXYIntersection.m_direction;
+ QDoubleVector2D br = nearPlaneXYIntersection.m_point
+ + squareHalfSide * nearPlaneXYIntersection.m_direction;
+
+ QList<QDoubleVector2D> projectableRect;
+ projectableRect.push_back(bl);
+ projectableRect.push_back(br);
+ projectableRect.push_back(tr);
+ projectableRect.push_back(tl);
+
+
+ c2t::clip2tri clipperProjectable;
+ clipperProjectable.clearClipper();
+ clipperProjectable.addSubjectPath(QClipperUtils::qListToPath(mapRect), true);
+ clipperProjectable.addClipPolygon(QClipperUtils::qListToPath(projectableRect));
+
+ Paths resProjectable = clipperProjectable.execute(c2t::clip2tri::Intersection);
+ if (resProjectable.size())
+ m_projectableRegion = QClipperUtils::pathToQList(resProjectable[0]); // Intersection between two convex quadrilaterals should always be a single polygon
+ else
+ m_projectableRegion = viewportRect;
+ }
}
QGeoCameraData QGeoProjectionWebMercator::cameraData() const
@@ -544,9 +620,15 @@ QGeoProjectionWebMercator::Plane::Plane(const QDoubleVector3D &planePoint, const
QDoubleVector3D QGeoProjectionWebMercator::Plane::lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection) const
{
+ double s;
+ return lineIntersection(linePoint, lineDirection, s);
+}
+
+QDoubleVector3D QGeoProjectionWebMercator::Plane::lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection, double &s) const
+{
QDoubleVector3D w = linePoint - m_point;
// s = -n.dot(w) / n.dot(u). p = p0 + su; u is lineDirection
- double s = QDoubleVector3D::dotProduct(-m_normal, w) / QDoubleVector3D::dotProduct(m_normal, lineDirection);
+ s = QDoubleVector3D::dotProduct(-m_normal, w) / QDoubleVector3D::dotProduct(m_normal, lineDirection);
return linePoint + lineDirection * s;
}
diff --git a/src/location/maps/qgeoprojection_p.h b/src/location/maps/qgeoprojection_p.h
index d3be1177..ca81df3a 100644
--- a/src/location/maps/qgeoprojection_p.h
+++ b/src/location/maps/qgeoprojection_p.h
@@ -73,6 +73,7 @@ public:
virtual bool isProjectable(const QDoubleVector2D &wrappedProjection) const = 0;
virtual QList<QDoubleVector2D> visibleRegion() const = 0;
+ virtual QList<QDoubleVector2D> projectableRegion() const = 0;
// Conversion methods for QGeoCoordinate <-> screen.
// This currently assumes that the "MapProjection" space is [0, 1][0, 1] for every type of possibly supported map projection
@@ -132,9 +133,9 @@ public:
bool isProjectable(const QDoubleVector2D &wrappedProjection) const Q_DECL_OVERRIDE;
QList<QDoubleVector2D> visibleRegion() const Q_DECL_OVERRIDE;
-
+ QList<QDoubleVector2D> projectableRegion() const Q_DECL_OVERRIDE;
inline QDoubleVector2D viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition) const;
-
+ inline QDoubleVector2D viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition, double &s) const;
private:
void setupCamera();
void updateVisibleRegion();
@@ -158,6 +159,7 @@ public:
Plane(const QDoubleVector3D &planePoint, const QDoubleVector3D &planeNormal);
QDoubleVector3D lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection) const;
+ inline QDoubleVector3D lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection, double &s) const;
Line2D planeXYIntersection() const;
bool isValid() const;
@@ -208,6 +210,7 @@ private:
Line2D m_nearPlaneMapIntersection;
QList<QDoubleVector2D> m_visibleRegion;
+ QList<QDoubleVector2D> m_projectableRegion;
bool m_visibleRegionDirty;
Q_DISABLE_COPY(QGeoProjectionWebMercator)
diff --git a/src/location/maps/qgeoroute.cpp b/src/location/maps/qgeoroute.cpp
index 52fa4a5e..facf1949 100644
--- a/src/location/maps/qgeoroute.cpp
+++ b/src/location/maps/qgeoroute.cpp
@@ -104,7 +104,7 @@ QGeoRoute &QGeoRoute::operator= (const QGeoRoute & other)
*/
bool QGeoRoute::operator ==(const QGeoRoute &other) const
{
- return (d_ptr.constData() == other.d_ptr.constData());
+ return (*d_ptr.constData() == *other.d_ptr.constData());
}
/*!
@@ -112,7 +112,7 @@ bool QGeoRoute::operator ==(const QGeoRoute &other) const
*/
bool QGeoRoute::operator !=(const QGeoRoute &other) const
{
- return (d_ptr.constData() != other.d_ptr.constData());
+ return !(*d_ptr.constData() == *other.d_ptr.constData());
}
/*!
diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
index e736f6b7..95e5d44b 100644
--- a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
+++ b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp
@@ -173,6 +173,8 @@ void QGeoTileProviderOsm::disableRedirection()
for (TileProvider *p: m_providerList) {
if (p->isValid() && !found) {
m_provider = p;
+ m_providerId = m_providerList.indexOf(p);
+ m_status = Resolved;
found = true;
}
p->disconnect(this);
diff --git a/src/positioning/qlocationutils_p.h b/src/positioning/qlocationutils_p.h
index 75c4b7f4..69cf0fee 100644
--- a/src/positioning/qlocationutils_p.h
+++ b/src/positioning/qlocationutils_p.h
@@ -55,7 +55,7 @@
#include <qmath.h>
#include <QtPositioning/QGeoCoordinate>
-static const double offsetEpsilon = 0.0000000000001;
+static const double offsetEpsilon = 1e-12; // = 0.000000000001
static const double leftOffset = -180.0 + offsetEpsilon;
static const double rightOffset = 180.0 - offsetEpsilon;
diff --git a/src/src.pro b/src/src.pro
index d0a1ee4e..0c0bf50b 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -7,8 +7,6 @@ SUBDIRS += positioning
positioning.depends = 3rdparty/clip2tri
qtHaveModule(quick) {
- plugins.depends += positioning
-
SUBDIRS += location
location.depends += positioning 3rdparty/clip2tri
@@ -18,6 +16,7 @@ qtHaveModule(quick) {
imports.depends += positioning location
}
+plugins.depends += positioning
SUBDIRS += plugins
!android:contains(QT_CONFIG, private_tests) {