diff --git a/include/coordinates_geom.h b/include/coordinates_geom.h index 279de459..1c035d9c 100644 --- a/include/coordinates_geom.h +++ b/include/coordinates_geom.h @@ -23,7 +23,9 @@ class TileBbox { TileBbox(TileCoordinates i, uint z, bool h, bool e); std::pair scaleLatpLon(double latp, double lon) const; - std::vector scaleRing(Ring const &src) const; + void scaleRing(Ring &dst, Ring const &src) const; + Ring scaleRing(Ring const &src) const; + void scaleGeometry(MultiPolygon &dst, MultiPolygon const &src) const; MultiPolygon scaleGeometry(MultiPolygon const &src) const; std::pair floorLatpLon(double latp, double lon) const; diff --git a/include/geometry/correct.hpp b/include/geometry/correct.hpp index 7adb5d34..fba96c69 100644 --- a/include/geometry/correct.hpp +++ b/include/geometry/correct.hpp @@ -10,6 +10,7 @@ * ---------------------------------------------------------------------------- */ +#include #include #include #include @@ -25,7 +26,7 @@ namespace impl { template static inline void result_combine(C &result, T &&new_element) { - result.push_back(new_element); + result.push_back(std::forward(new_element)); for(std::size_t i = 0; i < result.size() - 1; ) { if(!boost::geometry::intersects(result[i], result.back())) { @@ -96,6 +97,8 @@ static inline void dissolve_find_intersections( if(ring.empty()) return; boost::geometry::index::rtree, std::size_t >, boost::geometry::index::quadratic<16>> index; + std::vector output; + output.reserve(2); // Generate all by-pass intersections in the graph // Generate a list of all by-pass intersections @@ -112,7 +115,7 @@ static inline void dissolve_find_intersections( auto const &line_2 = iter.first; auto j = iter.second; - std::vector output; + output.clear(); boost::geometry::intersection(line_1, line_2, output); for(auto const &p: output) { @@ -281,9 +284,11 @@ static inline std::vector correct(ring_t const &ring, boost::geometry::o dissolve_find_intersections(new_ring, pseudo_vertices, start_keys); if(start_keys.empty()) { - if(std::abs(boost::geometry::area(new_ring)) > remove_spike_min_area) - return { new_ring }; - else + if(std::abs(boost::geometry::area(new_ring)) > remove_spike_min_area) { + std::vector result; + result.push_back(std::move(new_ring)); + return result; + } else return { }; } diff --git a/include/osm_lua_processing.h b/include/osm_lua_processing.h index 539c745c..b8d536c1 100644 --- a/include/osm_lua_processing.h +++ b/include/osm_lua_processing.h @@ -276,6 +276,8 @@ class OsmLuaProcessing { const inline Point getPoint() { return Point(lon/10000000.0,latp/10000000.0); } + + double projectedPolygonArea(const Polygon &p); OSMStore &osmStore; // global OSM store @@ -310,6 +312,7 @@ class OsmLuaProcessing { bool multiLinestringInited; MultiPolygon multiPolygonCache; bool multiPolygonInited; + geom::model::polygon areaPolygonCache; NodeID lastStoredGeometryId; OutputGeometryType lastStoredGeometryType; diff --git a/include/osm_store.h b/include/osm_store.h index 171e9386..a60830fb 100644 --- a/include/osm_store.h +++ b/include/osm_store.h @@ -332,6 +332,7 @@ class OSMStore template Polygon llListPolygon(WayIt begin, WayIt end) const { Polygon poly; + poly.outer().reserve(end - begin); fillPoints(poly.outer(), begin, end); boost::geometry::correct(poly); return poly; @@ -341,6 +342,7 @@ class OSMStore template Linestring llListLinestring(WayIt begin, WayIt end) const { Linestring ls; + ls.reserve(end - begin); fillPoints(ls, begin, end); return ls; } diff --git a/include/sharded_way_store.h b/include/sharded_way_store.h index 40a3d331..d57ebef6 100644 --- a/include/sharded_way_store.h +++ b/include/sharded_way_store.h @@ -14,6 +14,7 @@ class ShardedWayStore : public WayStore { void reopen() override; void batchStart() override; std::vector at(WayID wayid) const override; + void at(WayID wayid, std::vector& output) const override; bool requiresNodes() const override; void insertLatpLons(std::vector &newWays) override; void insertNodes(const std::vector>>& newWays) override; diff --git a/include/sorted_way_store.h b/include/sorted_way_store.h index b99ba7de..2df99249 100644 --- a/include/sorted_way_store.h +++ b/include/sorted_way_store.h @@ -88,6 +88,7 @@ class SortedWayStore: public WayStore { void reopen() override; void batchStart() override; std::vector at(WayID wayid) const override; + void at(WayID wayid, std::vector& output) const override; bool requiresNodes() const override { return true; } void insertLatpLons(std::vector &newWays) override; void insertNodes(const std::vector>>& newWays) override; @@ -107,6 +108,7 @@ class SortedWayStore: public WayStore { ); static std::vector decodeWay(uint16_t flags, const uint8_t* input); + static void decodeWay(uint16_t flags, const uint8_t* input, std::vector& output); private: bool compressWays; diff --git a/include/tile_data.h b/include/tile_data.h index 012cab93..cd85b608 100644 --- a/include/tile_data.h +++ b/include/tile_data.h @@ -114,7 +114,7 @@ inline OutputObjectID outputObjectWithId(const OutputObjectXYI template void collectLowZoomObjectsForTile( const unsigned int& indexZoom, - typename std::vector> objects, + const typename std::vector>& objects, unsigned int zoom, const TileCoordinates& dstIndex, std::vector& output diff --git a/include/way_store.h b/include/way_store.h index 36862344..1898ea11 100644 --- a/include/way_store.h +++ b/include/way_store.h @@ -15,6 +15,7 @@ class WayStore { // meaningful for SortedWayStore virtual void batchStart() = 0; virtual std::vector at(WayID wayid) const = 0; + virtual void at(WayID wayid, std::vector& output) const = 0; virtual bool requiresNodes() const = 0; virtual void insertLatpLons(std::vector& newWays) = 0; virtual void insertNodes(const std::vector>>& newWays) = 0; diff --git a/include/way_stores.h b/include/way_stores.h index 0f94e845..0502ddc6 100644 --- a/include/way_stores.h +++ b/include/way_stores.h @@ -15,6 +15,7 @@ class BinarySearchWayStore: public WayStore { void reopen() override; void batchStart() override {} std::vector at(WayID wayid) const override; + void at(WayID wayid, std::vector& output) const override; bool requiresNodes() const override { return false; } void insertLatpLons(std::vector &newWays) override; void insertNodes(const std::vector>>& newWays) override; diff --git a/src/coordinates_geom.cpp b/src/coordinates_geom.cpp index 0a5b2410..54344299 100644 --- a/src/coordinates_geom.cpp +++ b/src/coordinates_geom.cpp @@ -33,10 +33,10 @@ pair TileBbox::scaleLatpLon(double latp, double lon) const { // Scaling with naive self-intersection check - if we've added the new point // within the last 5 points, then backtrack to the last time we added it -std::vector TileBbox::scaleRing(Ring const &src) const { - std::vector points; +void TileBbox::scaleRing(Ring &points, Ring const &src) const { + points.clear(); points.reserve(src.size()); - for(auto &i: src) { + for(auto const &i: src) { auto scaled = scaleLatpLon(i.y(), i.x()); // -> .first is x, .second is y bool found = false; for (size_t j=1; j<5; j++) { @@ -48,36 +48,46 @@ std::vector TileBbox::scaleRing(Ring const &src) const { } if (!found) points.push_back(Point(scaled.first,scaled.second)); } +} + +Ring TileBbox::scaleRing(Ring const &src) const { + Ring points; + scaleRing(points, src); return points; } -MultiPolygon TileBbox::scaleGeometry(MultiPolygon const &src) const { - MultiPolygon dst; - for(auto poly: src) { - Polygon p; +void TileBbox::scaleGeometry(MultiPolygon &dst, MultiPolygon const &src) const { + if (dst.size() < src.size()) + dst.resize(src.size()); + + size_t polygonCount = 0; + for(auto const &poly: src) { + Polygon &p = dst[polygonCount]; // Copy the outer ring - std::vector points = scaleRing(poly.outer()); - if (points.size()<4) continue; - Ring outer; - geom::append(outer,points); - geom::append(p,outer); + scaleRing(p.outer(), poly.outer()); + if (p.outer().size()<4) + continue; // Copy the inner rings - int num_rings = 0; - for(auto &r: poly.inners()) { - points = scaleRing(r); - if (points.size()<4) continue; - Ring inner; - geom::append(inner,points); - num_rings++; - geom::interior_rings(p).resize(num_rings); - geom::append(p, inner, num_rings-1); + if (p.inners().size() < poly.inners().size()) + p.inners().resize(poly.inners().size()); + size_t innerCount = 0; + for(auto const &r: poly.inners()) { + Ring &points = p.inners()[innerCount]; + scaleRing(points, r); + if (points.size()>=4) + innerCount++; } - - // Add to multipolygon - dst.push_back(p); + p.inners().resize(innerCount); + polygonCount++; } + dst.resize(polygonCount); +} + +MultiPolygon TileBbox::scaleGeometry(MultiPolygon const &src) const { + MultiPolygon dst; + scaleGeometry(dst, src); return dst; } diff --git a/src/geom.cpp b/src/geom.cpp index 69633372..ceb2a3b7 100644 --- a/src/geom.cpp +++ b/src/geom.cpp @@ -197,10 +197,20 @@ char bit_code(Point const &p, Box const &bbox) { } // Sutherland-Hodgeman polygon clipping algorithm -void fast_clip(Ring &points, Box const &bbox) { +void fast_clip(Ring &points, Box const &bbox, Ring &result) { // clip against each side of the clip rectangle for (char edge = 1; edge <= 8; edge *= 2) { - Ring result; + bool needsClip = false; + for (auto const &p: points) { + if (bit_code(p, bbox) & edge) { + needsClip = true; + break; + } + } + if (!needsClip) continue; + + result.clear(); + result.reserve(points.size() + 4); Point prev = points[points.size() - 1]; bool prevInside = (bit_code(prev, bbox) & edge)==0; @@ -214,20 +224,26 @@ void fast_clip(Ring &points, Box const &bbox) { prev = p; prevInside = inside; } - points = std::move(result); + points.swap(result); if (points.size()==0) break; } } +void fast_clip(Ring &points, Box const &bbox) { + Ring result; + fast_clip(points, bbox, result); +} + // Wrappers for polygon/multipolygon void fast_clip(Polygon &polygon, Box const &bbox) { - fast_clip(polygon.outer(), bbox); + Ring result; + fast_clip(polygon.outer(), bbox, result); if (polygon.outer().empty()) { polygon.inners().resize(0); return; } for (auto &inner: polygon.inners()) { - fast_clip(inner, bbox); + fast_clip(inner, bbox, result); } polygon.inners().erase(std::remove_if( polygon.inners().begin(), polygon.inners().end(), diff --git a/src/osm_lua_processing.cpp b/src/osm_lua_processing.cpp index bc5271a4..076505b2 100644 --- a/src/osm_lua_processing.cpp +++ b/src/osm_lua_processing.cpp @@ -512,21 +512,43 @@ void reverse_project(DegPoint& p) { geom::set<1>(p, latp2lat(geom::get<1>(p))); } +template +void projectRing(DstRing& dst, const SrcRing& src) { + dst.resize(src.size()); + for (std::size_t i = 0; i < src.size(); ++i) { + geom::set<0>(dst[i], geom::get<0>(src[i])); + geom::set<1>(dst[i], latp2lat(geom::get<1>(src[i]))); + } +} + +#if BOOST_VERSION >= 106700 +double OsmLuaProcessing::projectedPolygonArea(const Polygon &p) { + areaPolygonCache.inners().resize(p.inners().size()); + projectRing(areaPolygonCache.outer(), p.outer()); + for (std::size_t i = 0; i < p.inners().size(); ++i) { + projectRing(areaPolygonCache.inners()[i], p.inners()[i]); + } + + geom::strategy::area::spherical<> sph_strategy(RadiusMeter); + return geom::area(areaPolygonCache, sph_strategy); +} +#endif + // Returns area double OsmLuaProcessing::Area() { if (!IsClosed()) return 0; #if BOOST_VERSION >= 106700 - geom::strategy::area::spherical<> sph_strategy(RadiusMeter); if (isRelation) { // Boost won't calculate area of a multipolygon, so we just total up the member polygons - return multiPolygonArea(multiPolygonCached()); + double totalArea = 0; + const MultiPolygon &mp = multiPolygonCached(); + for (MultiPolygon::const_iterator it = mp.begin(); it != mp.end(); ++it) { + totalArea += projectedPolygonArea(*it); + } + return totalArea; } else if (isWay) { - // Reproject back into lat/lon and then run Boo - geom::model::polygon p; - geom::assign(p,polygonCached()); - geom::for_each_point(p, reverse_project); - return geom::area(p, sph_strategy); + return projectedPolygonArea(polygonCached()); } #else if (isRelation) { @@ -652,10 +674,12 @@ void OsmLuaProcessing::Layer(const string &layerName, bool area) { } else if (isWay) { //Is there a more efficient way to do this? - Linestring ls = linestringCached(); + const Linestring &ls = linestringCached(); Polygon p; + p.outer().reserve(ls.size()); geom::assign_points(p, ls); - mp.push_back(p); + mp.reserve(1); + mp.push_back(std::move(p)); auto correctionResult = CorrectGeometry(mp); if(correctionResult == CorrectGeometryResult::Invalid) return; @@ -872,8 +896,10 @@ Point OsmLuaProcessing::calculateCentroid(CentroidAlgorithm algorithm) { geom::centroid(ls, centroid); } } else { + const Linestring &ls = linestringCached(); Polygon p; - geom::assign_points(p, linestringCached()); + p.outer().reserve(ls.size()); + geom::assign_points(p, ls); if (algorithm == CentroidAlgorithm::Polylabel) { // CONSIDER: pick precision intelligently diff --git a/src/osm_mem_tiles.cpp b/src/osm_mem_tiles.cpp index bc038767..0e9a6f07 100644 --- a/src/osm_mem_tiles.cpp +++ b/src/osm_mem_tiles.cpp @@ -4,6 +4,7 @@ using namespace std; thread_local GeometryCache linestringCache; +thread_local std::vector wayNodes; OsmMemTiles::OsmMemTiles( size_t threadNum, @@ -34,6 +35,7 @@ LatpLon OsmMemTiles::buildNodeGeometry( Linestring& ls = getOrBuildLinestring(objectID); Point centroid; Polygon p; + p.outer().reserve(ls.size()); geom::assign_points(p, ls); geom::centroid(p, centroid); return LatpLon{(int32_t)(centroid.y()*10000000.0), (int32_t)(centroid.x()*10000000.0)}; @@ -58,23 +60,39 @@ Geometry OsmMemTiles::buildWayGeometry( if(ls.empty()) return out; + Box extBox = bbox.getExtendBox(); + const double minX = extBox.min_corner().x(), maxX = extBox.max_corner().x(); + const double minY = extBox.min_corner().y(), maxY = extBox.max_corner().y(); + auto pointInsideExtBox = [minX, maxX, minY, maxY](const Point& p) { + return p.x() >= minX && p.x() <= maxX && p.y() >= minY && p.y() <= maxY; + }; + bool needsIntersection = !pointInsideExtBox(ls[0]); + Linestring current_ls; + current_ls.reserve(ls.size()); geom::append(current_ls, ls[0]); for(size_t i = 1; i < ls.size(); ++i) { - if(!geom::intersects(Linestring({ ls[i-1], ls[i] }), bbox.clippingBox)) { + boost::geometry::model::segment segment(ls[i-1], ls[i]); + if(!geom::intersects(segment, bbox.clippingBox)) { if(current_ls.size() > 1) out.push_back(std::move(current_ls)); current_ls.clear(); + current_ls.reserve(ls.size() - i); } geom::append(current_ls, ls[i]); + if (!needsIntersection) + needsIntersection = !pointInsideExtBox(ls[i]); } if(current_ls.size() > 1) out.push_back(std::move(current_ls)); + if (!needsIntersection) + return out; + MultiLinestring result; - geom::intersection(out, bbox.getExtendBox(), result); + geom::intersection(out, extBox, result); return result; } @@ -83,9 +101,10 @@ Geometry OsmMemTiles::buildWayGeometry( } void OsmMemTiles::populateLinestring(Linestring& ls, NodeID objectID) const { - std::vector nodes = wayStore.at(OSM_ID(objectID)); + wayStore.at(OSM_ID(objectID), wayNodes); + ls.reserve(wayNodes.size()); - for (const LatpLon& node : nodes) { + for (const LatpLon& node : wayNodes) { boost::geometry::range::push_back(ls, boost::geometry::make(node.lon/10000000.0, node.latp/10000000.0)); } } @@ -122,8 +141,10 @@ void OsmMemTiles::populateMultiPolygon(MultiPolygon& dst, NodeID objectID) { Linestring ls; populateLinestring(ls, objectID); Polygon p; + p.outer().reserve(ls.size()); geom::assign_points(p, ls); - dst.push_back(p); + dst.reserve(dst.size() + 1); + dst.push_back(std::move(p)); } void OsmMemTiles::Clear() { diff --git a/src/sharded_way_store.cpp b/src/sharded_way_store.cpp index d9741082..35ad99a5 100644 --- a/src/sharded_way_store.cpp +++ b/src/sharded_way_store.cpp @@ -24,16 +24,22 @@ void ShardedWayStore::batchStart() { } std::vector ShardedWayStore::at(WayID wayid) const { + std::vector rv; + at(wayid, rv); + return rv; +} + +void ShardedWayStore::at(WayID wayid, std::vector& output) const { for (int i = 0; i < shards(); i++) { size_t index = (lastWayShard + i) % shards(); if (stores[index]->contains(0, wayid)) { lastWayShard = index; - return stores[index]->at(wayid); + stores[index]->at(wayid, output); + return; } } - // Superfluous return to silence a compiler warning - return stores[shards() - 1]->at(wayid); + stores[shards() - 1]->at(wayid, output); } bool ShardedWayStore::requiresNodes() const { @@ -78,4 +84,3 @@ const WayStore& ShardedWayStore::shard(size_t shard) const { } size_t ShardedWayStore::shards() const { return nodeStore.shards(); } - diff --git a/src/sorted_node_store.cpp b/src/sorted_node_store.cpp index 6ea6e8af..1b9c452e 100644 --- a/src/sorted_node_store.cpp +++ b/src/sorted_node_store.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "sorted_node_store.h" #include "external/libpopcnt.h" #include "external/streamvbyte.h" @@ -14,15 +15,26 @@ namespace SortedNodeStoreTypes { const uint16_t ChunkSize = 256; const uint16_t ChunkAlignment = 16; const uint32_t ChunkCompressed = 1 << 31; + const uint8_t ChunkCacheSize = 4; + + struct CachedChunk { + CachedChunk(): id(-1) {} + + int64_t id; + std::array lons; + std::array latps; + }; struct ThreadStorage { ThreadStorage(): collectingOrphans(true), groupStart(-1), localNodes(nullptr), - cachedChunk(-1), arenaSpace(0), - arenaPtr(nullptr) {} + arenaPtr(nullptr) { + for (uint8_t i = 0; i < ChunkCacheSize; ++i) + cacheOrder[i] = i; + } // When SortedNodeStore first starts, it's not confident that it has seen an // entire segment, so it's in "collecting orphans" mode. Once it crosses a // threshold of 64K elements, it ceases to be in this mode. @@ -33,9 +45,8 @@ namespace SortedNodeStoreTypes { uint64_t groupStart = -1; std::vector* localNodes = nullptr; - int64_t cachedChunk = -1; - std::vector cacheChunkLons; - std::vector cacheChunkLatps; + std::array cacheChunks; + std::array cacheOrder; uint32_t arenaSpace = 0; char* arenaPtr = nullptr; @@ -53,6 +64,13 @@ namespace SortedNodeStoreTypes { auto& rv = threadStorage.back(); return rv.second; } + + void promoteCacheSlot(ThreadStorage& tls, size_t orderIndex) { + const uint8_t slot = tls.cacheOrder[orderIndex]; + for (size_t i = orderIndex; i > 0; --i) + tls.cacheOrder[i] = tls.cacheOrder[i - 1]; + tls.cacheOrder[0] = slot; + } } using namespace SortedNodeStoreTypes; @@ -166,25 +184,40 @@ LatpLon SortedNodeStore::at(const NodeID id) const { const size_t neededChunk = groupIndex * ChunkSize + chunk; - // Really naive caching strategy - just cache the last-used chunk. - // Probably good enough? + // Keep a few recently decoded chunks per worker. Way geometry tends to + // revisit adjacent chunks, but not always the immediately preceding one. ThreadStorage& tls = s(this); - if (tls.cachedChunk != neededChunk) { - tls.cachedChunk = neededChunk; - tls.cacheChunkLons.reserve(256); - tls.cacheChunkLatps.reserve(256); + size_t orderIndex = ChunkCacheSize; + for (size_t i = 0; i < ChunkCacheSize; ++i) { + if (tls.cacheChunks[tls.cacheOrder[i]].id == neededChunk) { + orderIndex = i; + break; + } + } + + size_t cacheSlot; + if (orderIndex == ChunkCacheSize) { + cacheSlot = tls.cacheOrder[ChunkCacheSize - 1]; + CachedChunk& cached = tls.cacheChunks[cacheSlot]; + cached.id = neededChunk; uint8_t* latpData = ptr->data; uint8_t* lonData = ptr->data + latpSize; uint32_t recovdata[256] = {0}; streamvbyte_decode(latpData, recovdata, n); - tls.cacheChunkLatps[0] = ptr->firstLatp; - zigzag_delta_decode(recovdata, &tls.cacheChunkLatps[1], n, tls.cacheChunkLatps[0]); + cached.latps[0] = ptr->firstLatp; + zigzag_delta_decode(recovdata, &cached.latps[1], n, cached.latps[0]); streamvbyte_decode(lonData, recovdata, n); - tls.cacheChunkLons[0] = ptr->firstLon; - zigzag_delta_decode(recovdata, &tls.cacheChunkLons[1], n, tls.cacheChunkLons[0]); + cached.lons[0] = ptr->firstLon; + zigzag_delta_decode(recovdata, &cached.lons[1], n, cached.lons[0]); + + promoteCacheSlot(tls, ChunkCacheSize - 1); + } else { + cacheSlot = tls.cacheOrder[orderIndex]; + if (orderIndex != 0) + promoteCacheSlot(tls, orderIndex); } size_t nodeOffset = 0; @@ -195,7 +228,8 @@ LatpLon SortedNodeStore::at(const NodeID id) const { if (!(ptr->nodeMask[nodeMaskByte] & (1 << nodeMaskBit))) throw std::out_of_range("SortedNodeStore: node " + std::to_string(id) + " missing, no node"); - return { tls.cacheChunkLatps[nodeOffset], tls.cacheChunkLons[nodeOffset] }; + const CachedChunk& cached = tls.cacheChunks[cacheSlot]; + return { cached.latps[nodeOffset], cached.lons[nodeOffset] }; } UncompressedChunkInfo* ptr = (UncompressedChunkInfo*)basePtr; diff --git a/src/sorted_way_store.cpp b/src/sorted_way_store.cpp index 05785dc4..daba19c3 100644 --- a/src/sorted_way_store.cpp +++ b/src/sorted_way_store.cpp @@ -28,6 +28,7 @@ namespace SortedWayStoreTypes { uint64_t groupStart; std::vector>>* localWays; std::vector encodedWay; + std::vector decodedWay; }; thread_local std::deque> threadStorage; @@ -136,6 +137,12 @@ bool SortedWayStore::contains(size_t shard, WayID id) const { } std::vector SortedWayStore::at(WayID id) const { + std::vector rv; + at(id, rv); + return rv; +} + +void SortedWayStore::at(WayID id, std::vector& rv) const { const size_t groupIndex = id / (GroupSize * ChunkSize); const size_t chunk = (id % (GroupSize * ChunkSize)) / ChunkSize; const uint64_t chunkMaskByte = chunk / 8; @@ -192,11 +199,13 @@ std::vector SortedWayStore::at(WayID id) const { wayPtr = (EncodedWay*)(endOfWayOffsetPtr + chunkPtr->wayOffsets[wayOffset] * LargeWayAlignment); } - std::vector nodes = SortedWayStore::decodeWay(wayPtr->flags, wayPtr->data); - std::vector rv; + ThreadStorage& tls = s(this); + SortedWayStore::decodeWay(wayPtr->flags, wayPtr->data, tls.decodedWay); + const std::vector& nodes = tls.decodedWay; + rv.clear(); + rv.reserve(nodes.size()); for (const NodeID& node : nodes) rv.push_back(nodeStore.at(node)); - return rv; } void SortedWayStore::insertLatpLons(std::vector &newWays) { @@ -316,11 +325,18 @@ void SortedWayStore::collectOrphans(const std::vector SortedWayStore::decodeWay(uint16_t flags, const uint8_t* input) { std::vector rv; + decodeWay(flags, input, rv); + return rv; +}; + +void SortedWayStore::decodeWay(uint16_t flags, const uint8_t* input, std::vector& rv) { + rv.clear(); bool isCompressed = flags & CompressedWay; bool isClosed = flags & ClosedWay; const uint16_t length = flags & 0b0000011111111111; + rv.reserve(length + (isClosed ? 1 : 0)); if (!(flags & UniformUpperBits)) { // The nodes don't all share the same upper int; unpack which @@ -366,7 +382,6 @@ std::vector SortedWayStore::decodeWay(uint16_t flags, const uint8_t* inp if (isClosed) rv.push_back(rv[0]); - return rv; }; uint16_t SortedWayStore::encodeWay(const std::vector& way, std::vector& output, bool compress) { diff --git a/src/tile_data.cpp b/src/tile_data.cpp index 10c2f11b..29004c93 100644 --- a/src/tile_data.cpp +++ b/src/tile_data.cpp @@ -227,23 +227,37 @@ Geometry TileDataSource::buildWayGeometry(OutputGeometryType const geomType, if(ls.empty()) return out; + Box extBox = bbox.getExtendBox(); + const double minX = extBox.min_corner().x(), maxX = extBox.max_corner().x(); + const double minY = extBox.min_corner().y(), maxY = extBox.max_corner().y(); + auto pointInsideExtBox = [minX, maxX, minY, maxY](const Point& p) { + return p.x() >= minX && p.x() <= maxX && p.y() >= minY && p.y() <= maxY; + }; + bool needsIntersection = !pointInsideExtBox(ls[0]); + Linestring current_ls; geom::append(current_ls, ls[0]); for(size_t i = 1; i < ls.size(); ++i) { - if(!geom::intersects(Linestring({ ls[i-1], ls[i] }), bbox.clippingBox)) { + boost::geometry::model::segment segment(ls[i-1], ls[i]); + if(!geom::intersects(segment, bbox.clippingBox)) { if(current_ls.size() > 1) out.push_back(std::move(current_ls)); current_ls.clear(); } geom::append(current_ls, ls[i]); + if (!needsIntersection) + needsIntersection = !pointInsideExtBox(ls[i]); } if(current_ls.size() > 1) out.push_back(std::move(current_ls)); + if (!needsIntersection) + return out; + MultiLinestring result; - geom::intersection(out, bbox.getExtendBox(), result); + geom::intersection(out, extBox, result); return result; } @@ -326,7 +340,10 @@ Geometry TileDataSource::buildWayGeometry(OutputGeometryType const geomType, } MultiPolygon mp; - geom::assign(mp, input); + if (cachedClip == nullptr) + mp = std::move(uncached); + else + geom::assign(mp, input); fast_clip(mp, box); geom::correct(mp); geom::validity_failure_type failure = geom::validity_failure_type::no_failure; @@ -339,7 +356,13 @@ Geometry TileDataSource::buildWayGeometry(OutputGeometryType const geomType, } if (!valid && (failure==geom::failure_self_intersections || failure==geom::failure_intersecting_interiors)) { MultiPolygon output; - geom::intersection(input, box, output); + if (cachedClip == nullptr) { + MultiPolygon original; + populateMultiPolygon(original, objectID); + geom::intersection(original, box, output); + } else { + geom::intersection(input, box, output); + } geom::correct(output); // retry with Boost intersection if fast_clip has caused self-intersections @@ -477,7 +500,7 @@ void TileDataSource::addGeometryToIndex( const std::vector& outputs, const uint64_t id ) { - for (Linestring ls : geom) { + for (const auto& ls : geom) { unordered_set tileSet; insertIntermediateTiles(ls, indexZoom, tileSet); for (auto it = tileSet.begin(); it != tileSet.end(); ++it) { @@ -496,7 +519,7 @@ void TileDataSource::addGeometryToIndex( ) { unordered_set tileSet; bool singleOuter = geom.size()==1; - for (Polygon poly : geom) { + for (const auto& poly : geom) { unordered_set tileSetTmp; insertIntermediateTiles(poly.outer(), indexZoom, tileSetTmp); fillCoveredTiles(tileSetTmp); @@ -593,5 +616,15 @@ NodeID TileDataSource::storeMultiLinestring(const MultiLinestring& src) { void TileDataSource::populateMultiPolygon(MultiPolygon& dst, NodeID objectID) { const auto &input = retrieveMultiPolygon(objectID); - boost::geometry::assign(dst, input); + dst.resize(input.size()); + for(std::size_t i = 0; i < input.size(); ++i) { + dst[i].outer().resize(input[i].outer().size()); + boost::geometry::assign(dst[i].outer(), input[i].outer()); + + dst[i].inners().resize(input[i].inners().size()); + for(std::size_t j = 0; j < input[i].inners().size(); ++j) { + dst[i].inners()[j].resize(input[i].inners()[j].size()); + boost::geometry::assign(dst[i].inners()[j], input[i].inners()[j]); + } + } } diff --git a/src/tile_worker.cpp b/src/tile_worker.cpp index 7e894332..cebbfd6b 100644 --- a/src/tile_worker.cpp +++ b/src/tile_worker.cpp @@ -10,6 +10,7 @@ using namespace std; extern bool verbose; thread_local bool enabledUserSignal = false; +thread_local MultiPolygon scaledMultiPolygon; typedef std::vector::const_iterator OutputObjectsConstIt; typedef std::pair OutputObjectsConstItPair; @@ -220,7 +221,8 @@ void writeMultiPolygon( unsigned simplifyAlgo, const MultiPolygon& mp ) { - MultiPolygon current = bbox.scaleGeometry(mp); + bbox.scaleGeometry(scaledMultiPolygon, mp); + MultiPolygon ¤t = scaledMultiPolygon; if (simplifyLevel>0) { if (simplifyAlgo == LayerDef::VISVALINGAM) { current = simplifyVis(current, simplifyLevel/bbox.xscale); diff --git a/src/visvalingam.cpp b/src/visvalingam.cpp index 7ae43e8f..1b534b93 100644 --- a/src/visvalingam.cpp +++ b/src/visvalingam.cpp @@ -49,6 +49,10 @@ struct visItem { struct minHeap { std::vector h; + void Reserve(size_t size) { + h.reserve(size); + } + void Push(visItem *item) { item->index = h.size(); h.push_back(item); @@ -142,9 +146,9 @@ struct minHeap { template static double doubleTriangleArea(GeometryType const &ls, int start, int i1, int i2, int i3) { - Point a = ls[i1 + start]; - Point b = ls[i2 + start]; - Point c = ls[i3 + start]; + Point const &a = ls[i1 + start]; + Point const &b = ls[i2 + start]; + Point const &c = ls[i3 + start]; return std::abs((b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x())); } @@ -158,6 +162,7 @@ GeometryType visvalingam(const GeometryType &ls, double threshold, size_t retain // build the initial minheap linked list. minHeap heap; + heap.Reserve(end - start); visItem linkedListStart; linkedListStart.area = INFINITY; @@ -232,6 +237,7 @@ GeometryType visvalingam(const GeometryType &ls, double threshold, size_t retain } GeometryType output; + output.reserve(end - start - removed); visItem *item = &linkedListStart; while (item != NULL) { output.emplace_back(ls[item->pointIndex + start]); @@ -250,6 +256,7 @@ Polygon simplifyVis(const Polygon &p, double max_distance) { Polygon output; double threshold = max_distance * max_distance * 4; output.outer() = visvalingam(p.outer(), threshold, 4); + output.inners().reserve(p.inners().size()); for (const auto &ring : p.inners()) { output.inners().emplace_back(visvalingam(ring, threshold, 4)); } @@ -257,6 +264,7 @@ Polygon simplifyVis(const Polygon &p, double max_distance) { } MultiPolygon simplifyVis(const MultiPolygon &mp, double max_distance) { MultiPolygon output; + output.reserve(mp.size()); for (const auto &p : mp) { output.emplace_back(simplifyVis(p, max_distance)); } diff --git a/src/way_stores.cpp b/src/way_stores.cpp index 790ad816..86431d2e 100644 --- a/src/way_stores.cpp +++ b/src/way_stores.cpp @@ -23,8 +23,14 @@ bool BinarySearchWayStore::contains(size_t shard, WayID id) const { } std::vector BinarySearchWayStore::at(WayID wayid) const { + std::vector rv; + at(wayid, rv); + return rv; +} + +void BinarySearchWayStore::at(WayID wayid, std::vector& rv) const { std::lock_guard lock(mutex); - + auto iter = std::lower_bound(mLatpLonLists->begin(), mLatpLonLists->end(), wayid, [](auto const &e, auto wayid) { return e.first < wayid; }); @@ -32,12 +38,11 @@ std::vector BinarySearchWayStore::at(WayID wayid) const { if(iter == mLatpLonLists->end() || iter->first != wayid) throw std::out_of_range("Could not find way with id " + std::to_string(wayid)); - std::vector rv; + rv.clear(); rv.reserve(iter->second.size()); // TODO: copy iter->second to rv more efficiently for (const LatpLon& el : iter->second) rv.push_back(el); - return rv; } void BinarySearchWayStore::insertLatpLons(std::vector &newWays) {