LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/flatgeobuf - packedrtree.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 173 271 63.8 %
Date: 2024-11-21 22:18:42 Functions: 13 28 46.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  FlatGeobuf
       4             :  * Purpose:  Packed RTree management
       5             :  * Author:   Björn Harrtell <bjorn at wololo dot org>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2018-2020, Björn Harrtell <bjorn at wololo dot org>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : // NOTE: The upstream of this file is in
      14             : // https://github.com/bjornharrtell/flatgeobuf/tree/master/src/cpp
      15             : 
      16             : #ifdef GDAL_COMPILATION
      17             : #include "cpl_port.h"
      18             : #else
      19             : #define CPL_IS_LSB 1
      20             : #endif
      21             : 
      22             : #include "packedrtree.h"
      23             : 
      24             : #include <algorithm>
      25             : #include <limits>
      26             : #include <map>
      27             : #include <unordered_map>
      28             : #include <iostream>
      29             : 
      30             : namespace FlatGeobuf
      31             : {
      32             : 
      33         483 : const NodeItem &NodeItem::expand(const NodeItem &r)
      34             : {
      35         483 :     if (r.minX < minX)
      36         376 :         minX = r.minX;
      37         483 :     if (r.minY < minY)
      38         359 :         minY = r.minY;
      39         483 :     if (r.maxX > maxX)
      40         380 :         maxX = r.maxX;
      41         483 :     if (r.maxY > maxY)
      42         371 :         maxY = r.maxY;
      43         483 :     return *this;
      44             : }
      45             : 
      46         348 : NodeItem NodeItem::create(uint64_t offset)
      47             : {
      48             :     return {std::numeric_limits<double>::infinity(),
      49             :             std::numeric_limits<double>::infinity(),
      50             :             -1 * std::numeric_limits<double>::infinity(),
      51         348 :             -1 * std::numeric_limits<double>::infinity(), offset};
      52             : }
      53             : 
      54         209 : bool NodeItem::intersects(const NodeItem &r) const
      55             : {
      56         209 :     if (maxX < r.minX)
      57          20 :         return false;
      58         189 :     if (maxY < r.minY)
      59          15 :         return false;
      60         174 :     if (minX > r.maxX)
      61          21 :         return false;
      62         153 :     if (minY > r.maxY)
      63           0 :         return false;
      64         153 :     return true;
      65             : }
      66             : 
      67         116 : std::vector<double> NodeItem::toVector()
      68             : {
      69         116 :     return std::vector<double>{minX, minY, maxX, maxY};
      70             : }
      71             : 
      72             : // Based on public domain code at
      73             : // https://github.com/rawrunprotected/hilbert_curves
      74         240 : uint32_t hilbert(uint32_t x, uint32_t y)
      75             : {
      76         240 :     uint32_t a = x ^ y;
      77         240 :     uint32_t b = 0xFFFF ^ a;
      78         240 :     uint32_t c = 0xFFFF ^ (x | y);
      79         240 :     uint32_t d = x & (y ^ 0xFFFF);
      80             : 
      81         240 :     uint32_t A = a | (b >> 1);
      82         240 :     uint32_t B = (a >> 1) ^ a;
      83         240 :     uint32_t C = ((c >> 1) ^ (b & (d >> 1))) ^ c;
      84         240 :     uint32_t D = ((a & (c >> 1)) ^ (d >> 1)) ^ d;
      85             : 
      86         240 :     a = A;
      87         240 :     b = B;
      88         240 :     c = C;
      89         240 :     d = D;
      90         240 :     A = ((a & (a >> 2)) ^ (b & (b >> 2)));
      91         240 :     B = ((a & (b >> 2)) ^ (b & ((a ^ b) >> 2)));
      92         240 :     C ^= ((a & (c >> 2)) ^ (b & (d >> 2)));
      93         240 :     D ^= ((b & (c >> 2)) ^ ((a ^ b) & (d >> 2)));
      94             : 
      95         240 :     a = A;
      96         240 :     b = B;
      97         240 :     c = C;
      98         240 :     d = D;
      99         240 :     A = ((a & (a >> 4)) ^ (b & (b >> 4)));
     100         240 :     B = ((a & (b >> 4)) ^ (b & ((a ^ b) >> 4)));
     101         240 :     C ^= ((a & (c >> 4)) ^ (b & (d >> 4)));
     102         240 :     D ^= ((b & (c >> 4)) ^ ((a ^ b) & (d >> 4)));
     103             : 
     104         240 :     a = A;
     105         240 :     b = B;
     106         240 :     c = C;
     107         240 :     d = D;
     108         240 :     C ^= ((a & (c >> 8)) ^ (b & (d >> 8)));
     109         240 :     D ^= ((b & (c >> 8)) ^ ((a ^ b) & (d >> 8)));
     110             : 
     111         240 :     a = C ^ (C >> 1);
     112         240 :     b = D ^ (D >> 1);
     113             : 
     114         240 :     uint32_t i0 = x ^ y;
     115         240 :     uint32_t i1 = b | (0xFFFF ^ (i0 | a));
     116             : 
     117         240 :     i0 = (i0 | (i0 << 8)) & 0x00FF00FF;
     118         240 :     i0 = (i0 | (i0 << 4)) & 0x0F0F0F0F;
     119         240 :     i0 = (i0 | (i0 << 2)) & 0x33333333;
     120         240 :     i0 = (i0 | (i0 << 1)) & 0x55555555;
     121             : 
     122         240 :     i1 = (i1 | (i1 << 8)) & 0x00FF00FF;
     123         240 :     i1 = (i1 | (i1 << 4)) & 0x0F0F0F0F;
     124         240 :     i1 = (i1 | (i1 << 2)) & 0x33333333;
     125         240 :     i1 = (i1 | (i1 << 1)) & 0x55555555;
     126             : 
     127         240 :     uint32_t value = ((i1 << 1) | i0);
     128             : 
     129         240 :     return value;
     130             : }
     131             : 
     132         240 : uint32_t hilbert(const NodeItem &r, uint32_t hilbertMax, const double minX,
     133             :                  const double minY, const double width, const double height)
     134             : {
     135         240 :     uint32_t x = 0;
     136         240 :     uint32_t y = 0;
     137             :     uint32_t v;
     138         240 :     if (width != 0.0)
     139         232 :         x = static_cast<uint32_t>(
     140         232 :             floor(hilbertMax * ((r.minX + r.maxX) / 2 - minX) / width));
     141         240 :     if (height != 0.0)
     142         228 :         y = static_cast<uint32_t>(
     143         228 :             floor(hilbertMax * ((r.minY + r.maxY) / 2 - minY) / height));
     144         240 :     v = hilbert(x, y);
     145         240 :     return v;
     146             : }
     147             : 
     148           0 : void hilbertSort(std::vector<std::shared_ptr<Item>> &items)
     149             : {
     150           0 :     NodeItem extent = calcExtent(items);
     151           0 :     const double minX = extent.minX;
     152           0 :     const double minY = extent.minY;
     153           0 :     const double width = extent.width();
     154           0 :     const double height = extent.height();
     155           0 :     std::sort(items.begin(), items.end(),
     156           0 :               [minX, minY, width, height](std::shared_ptr<Item> a,
     157           0 :                                           std::shared_ptr<Item> b)
     158             :               {
     159           0 :                   uint32_t ha = hilbert(a->nodeItem, HILBERT_MAX, minX, minY,
     160             :                                         width, height);
     161           0 :                   uint32_t hb = hilbert(b->nodeItem, HILBERT_MAX, minX, minY,
     162             :                                         width, height);
     163           0 :                   return ha > hb;
     164             :               });
     165           0 : }
     166             : 
     167           0 : void hilbertSort(std::vector<NodeItem> &items)
     168             : {
     169           0 :     NodeItem extent = calcExtent(items);
     170           0 :     const double minX = extent.minX;
     171           0 :     const double minY = extent.minY;
     172           0 :     const double width = extent.width();
     173           0 :     const double height = extent.height();
     174           0 :     std::sort(items.begin(), items.end(),
     175           0 :               [minX, minY, width, height](const NodeItem &a, const NodeItem &b)
     176             :               {
     177             :                   uint32_t ha =
     178           0 :                       hilbert(a, HILBERT_MAX, minX, minY, width, height);
     179             :                   uint32_t hb =
     180           0 :                       hilbert(b, HILBERT_MAX, minX, minY, width, height);
     181           0 :                   return ha > hb;
     182             :               });
     183           0 : }
     184             : 
     185           0 : NodeItem calcExtent(const std::vector<std::shared_ptr<Item>> &items)
     186             : {
     187             :     return std::accumulate(
     188             :         items.begin(), items.end(), NodeItem::create(0),
     189           0 :         [](NodeItem a, const std::shared_ptr<Item> &b) -> NodeItem
     190           0 :         { return a.expand(b->nodeItem); });
     191             : }
     192             : 
     193           0 : NodeItem calcExtent(const std::vector<NodeItem> &nodes)
     194             : {
     195             :     return std::accumulate(nodes.begin(), nodes.end(), NodeItem::create(0),
     196           0 :                            [](NodeItem a, const NodeItem &b) -> NodeItem
     197           0 :                            { return a.expand(b); });
     198             : }
     199             : 
     200         116 : void PackedRTree::init(const uint16_t nodeSize)
     201             : {
     202         116 :     if (nodeSize < 2)
     203           0 :         throw std::invalid_argument("Node size must be at least 2");
     204         116 :     if (_numItems == 0)
     205           0 :         throw std::invalid_argument("Cannot create empty tree");
     206         232 :     _nodeSize = std::min(std::max(nodeSize, static_cast<uint16_t>(2)),
     207         116 :                          static_cast<uint16_t>(65535));
     208         116 :     _levelBounds = generateLevelBounds(_numItems, _nodeSize);
     209         116 :     _numNodes = _levelBounds.front().second;
     210         116 :     _nodeItems = new NodeItem[static_cast<size_t>(_numNodes)];
     211         116 : }
     212             : 
     213             : std::vector<std::pair<uint64_t, uint64_t>>
     214         158 : PackedRTree::generateLevelBounds(const uint64_t numItems,
     215             :                                  const uint16_t nodeSize)
     216             : {
     217         158 :     if (nodeSize < 2)
     218           0 :         throw std::invalid_argument("Node size must be at least 2");
     219         158 :     if (numItems == 0)
     220           0 :         throw std::invalid_argument("Number of items must be greater than 0");
     221         158 :     if (numItems >
     222         158 :         std::numeric_limits<uint64_t>::max() - ((numItems / nodeSize) * 2))
     223           0 :         throw std::overflow_error("Number of items too large");
     224             : 
     225             :     // number of nodes per level in bottom-up order
     226         316 :     std::vector<uint64_t> levelNumNodes;
     227         158 :     uint64_t n = numItems;
     228         158 :     uint64_t numNodes = n;
     229         158 :     levelNumNodes.push_back(n);
     230           0 :     do
     231             :     {
     232         158 :         n = (n + nodeSize - 1) / nodeSize;
     233         158 :         numNodes += n;
     234         158 :         levelNumNodes.push_back(n);
     235         158 :     } while (n != 1);
     236             : 
     237             :     // bounds per level in reversed storage order (top-down)
     238         316 :     std::vector<uint64_t> levelOffsets;
     239         158 :     n = numNodes;
     240         474 :     for (auto size : levelNumNodes)
     241         316 :         levelOffsets.push_back(n -= size);
     242         158 :     std::vector<std::pair<uint64_t, uint64_t>> levelBounds;
     243         474 :     for (size_t i = 0; i < levelNumNodes.size(); i++)
     244         316 :         levelBounds.push_back(std::pair<uint64_t, uint64_t>(
     245         632 :             levelOffsets[i], levelOffsets[i] + levelNumNodes[i]));
     246         316 :     return levelBounds;
     247             : }
     248             : 
     249         116 : void PackedRTree::generateNodes()
     250             : {
     251         232 :     for (uint32_t i = 0; i < _levelBounds.size() - 1; i++)
     252             :     {
     253         116 :         auto pos = _levelBounds[i].first;
     254         116 :         auto end = _levelBounds[i].second;
     255         116 :         auto newpos = _levelBounds[i + 1].first;
     256         232 :         while (pos < end)
     257             :         {
     258         116 :             NodeItem node = NodeItem::create(pos);
     259         277 :             for (uint32_t j = 0; j < _nodeSize && pos < end; j++)
     260         161 :                 node.expand(_nodeItems[pos++]);
     261         116 :             _nodeItems[newpos++] = node;
     262             :         }
     263             :     }
     264         116 : }
     265             : 
     266           0 : void PackedRTree::fromData(const void *data)
     267             : {
     268           0 :     auto buf = reinterpret_cast<const uint8_t *>(data);
     269           0 :     const NodeItem *pn = reinterpret_cast<const NodeItem *>(buf);
     270           0 :     for (uint64_t i = 0; i < _numNodes; i++)
     271             :     {
     272           0 :         NodeItem n = *pn++;
     273           0 :         _nodeItems[i] = n;
     274           0 :         _extent.expand(n);
     275             :     }
     276           0 : }
     277             : 
     278           0 : PackedRTree::PackedRTree(const std::vector<std::shared_ptr<Item>> &items,
     279           0 :                          const NodeItem &extent, const uint16_t nodeSize)
     280           0 :     : _extent(extent), _numItems(items.size())
     281             : {
     282           0 :     init(nodeSize);
     283           0 :     for (size_t i = 0; i < _numItems; i++)
     284           0 :         _nodeItems[_numNodes - _numItems + i] = items[i]->nodeItem;
     285           0 :     generateNodes();
     286           0 : }
     287             : 
     288           0 : PackedRTree::PackedRTree(const std::vector<NodeItem> &nodes,
     289           0 :                          const NodeItem &extent, const uint16_t nodeSize)
     290           0 :     : _extent(extent), _numItems(nodes.size())
     291             : {
     292           0 :     init(nodeSize);
     293           0 :     for (size_t i = 0; i < _numItems; i++)
     294           0 :         _nodeItems[_numNodes - _numItems + i] = nodes[i];
     295           0 :     generateNodes();
     296           0 : }
     297             : 
     298           0 : PackedRTree::PackedRTree(const void *data, const uint64_t numItems,
     299           0 :                          const uint16_t nodeSize)
     300           0 :     : _extent(NodeItem::create(0)), _numItems(numItems)
     301             : {
     302           0 :     init(nodeSize);
     303           0 :     fromData(data);
     304           0 : }
     305             : 
     306         116 : PackedRTree::PackedRTree(std::function<void(NodeItem *)> fillNodeItems,
     307             :                          const uint64_t numItems, const NodeItem &extent,
     308         116 :                          const uint16_t nodeSize)
     309         116 :     : _extent(extent), _numItems(numItems)
     310             : {
     311         116 :     init(nodeSize);
     312         116 :     fillNodeItems(_nodeItems + _numNodes - _numItems);
     313         116 :     generateNodes();
     314         116 : }
     315             : 
     316             : std::vector<SearchResultItem>
     317           0 : PackedRTree::search(double minX, double minY, double maxX, double maxY) const
     318             : {
     319           0 :     uint64_t leafNodesOffset = _levelBounds.front().first;
     320           0 :     NodeItem n{minX, minY, maxX, maxY, 0};
     321           0 :     std::vector<SearchResultItem> results;
     322           0 :     std::unordered_map<uint64_t, uint64_t> queue;
     323           0 :     queue.insert(std::pair<uint64_t, uint64_t>(0, _levelBounds.size() - 1));
     324           0 :     while (queue.size() != 0)
     325             :     {
     326           0 :         auto next = queue.begin();
     327           0 :         uint64_t nodeIndex = next->first;
     328           0 :         uint64_t level = next->second;
     329           0 :         queue.erase(next);
     330           0 :         bool isLeafNode = nodeIndex >= _numNodes - _numItems;
     331             :         // find the end index of the node
     332             :         uint64_t end =
     333           0 :             std::min(static_cast<uint64_t>(nodeIndex + _nodeSize),
     334           0 :                      _levelBounds[static_cast<size_t>(level)].second);
     335             :         // search through child nodes
     336           0 :         for (uint64_t pos = nodeIndex; pos < end; pos++)
     337             :         {
     338           0 :             const auto &nodeItem = _nodeItems[static_cast<size_t>(pos)];
     339           0 :             if (!n.intersects(nodeItem))
     340           0 :                 continue;
     341           0 :             if (isLeafNode)
     342           0 :                 results.push_back({nodeItem.offset, pos - leafNodesOffset});
     343             :             else
     344             :                 queue.insert(
     345           0 :                     std::pair<uint64_t, uint64_t>(nodeItem.offset, level - 1));
     346             :         }
     347             :     }
     348           0 :     return results;
     349             : }
     350             : 
     351          33 : std::vector<SearchResultItem> PackedRTree::streamSearch(
     352             :     const uint64_t numItems, const uint16_t nodeSize, const NodeItem &item,
     353             :     const std::function<void(uint8_t *, size_t, size_t)> &readNode)
     354             : {
     355          66 :     auto levelBounds = generateLevelBounds(numItems, nodeSize);
     356          33 :     uint64_t leafNodesOffset = levelBounds.front().first;
     357          33 :     uint64_t numNodes = levelBounds.front().second;
     358          66 :     auto nodeItems = std::vector<NodeItem>(nodeSize);
     359          33 :     uint8_t *nodesBuf = reinterpret_cast<uint8_t *>(nodeItems.data());
     360             :     // use ordered search queue to make index traversal in sequential order
     361          66 :     std::map<uint64_t, uint64_t> queue;
     362          33 :     std::vector<SearchResultItem> results;
     363          33 :     queue.insert(std::pair<uint64_t, uint64_t>(0, levelBounds.size() - 1));
     364          94 :     while (queue.size() != 0)
     365             :     {
     366          61 :         auto next = queue.begin();
     367          61 :         uint64_t nodeIndex = next->first;
     368          61 :         uint64_t level = next->second;
     369          61 :         queue.erase(next);
     370          61 :         bool isLeafNode = nodeIndex >= numNodes - numItems;
     371             :         // find the end index of the node
     372         122 :         uint64_t end = std::min(static_cast<uint64_t>(nodeIndex + nodeSize),
     373          61 :                                 levelBounds[static_cast<size_t>(level)].second);
     374          61 :         uint64_t length = end - nodeIndex;
     375          61 :         readNode(nodesBuf, static_cast<size_t>(nodeIndex * sizeof(NodeItem)),
     376          61 :                  static_cast<size_t>(length * sizeof(NodeItem)));
     377             : #if !CPL_IS_LSB
     378             :         for (size_t i = 0; i < static_cast<size_t>(length); i++)
     379             :         {
     380             :             CPL_LSBPTR64(&nodeItems[i].minX);
     381             :             CPL_LSBPTR64(&nodeItems[i].minY);
     382             :             CPL_LSBPTR64(&nodeItems[i].maxX);
     383             :             CPL_LSBPTR64(&nodeItems[i].maxY);
     384             :             CPL_LSBPTR64(&nodeItems[i].offset);
     385             :         }
     386             : #endif
     387             :         // search through child nodes
     388         270 :         for (uint64_t pos = nodeIndex; pos < end; pos++)
     389             :         {
     390         209 :             uint64_t nodePos = pos - nodeIndex;
     391         209 :             const auto &nodeItem = nodeItems[static_cast<size_t>(nodePos)];
     392         209 :             if (!item.intersects(nodeItem))
     393          56 :                 continue;
     394         153 :             if (isLeafNode)
     395         125 :                 results.push_back({nodeItem.offset, pos - leafNodesOffset});
     396             :             else
     397             :                 queue.insert(
     398          28 :                     std::pair<uint64_t, uint64_t>(nodeItem.offset, level - 1));
     399             :         }
     400             :     }
     401          66 :     return results;
     402             : }
     403             : 
     404           0 : uint64_t PackedRTree::size() const
     405             : {
     406           0 :     return _numNodes * sizeof(NodeItem);
     407             : }
     408             : 
     409         164 : uint64_t PackedRTree::size(const uint64_t numItems, const uint16_t nodeSize)
     410             : {
     411         164 :     if (nodeSize < 2)
     412           0 :         throw std::invalid_argument("Node size must be at least 2");
     413         164 :     if (numItems == 0)
     414           0 :         throw std::invalid_argument("Number of items must be greater than 0");
     415             :     const uint16_t nodeSizeMin =
     416         328 :         std::min(std::max(nodeSize, static_cast<uint16_t>(2)),
     417         164 :                  static_cast<uint16_t>(65535));
     418             :     // limit so that resulting size in bytes can be represented by uint64_t
     419         164 :     if (numItems > static_cast<uint64_t>(1) << 56)
     420           0 :         throw std::overflow_error("Number of items must be less than 2^56");
     421         164 :     uint64_t n = numItems;
     422         164 :     uint64_t numNodes = n;
     423           0 :     do
     424             :     {
     425         164 :         n = (n + nodeSizeMin - 1) / nodeSizeMin;
     426         164 :         numNodes += n;
     427         164 :     } while (n != 1);
     428         164 :     return numNodes * sizeof(NodeItem);
     429             : }
     430             : 
     431         116 : void PackedRTree::streamWrite(
     432             :     const std::function<void(uint8_t *, size_t)> &writeData)
     433             : {
     434             : #if !CPL_IS_LSB
     435             :     for (size_t i = 0; i < static_cast<size_t>(_numNodes); i++)
     436             :     {
     437             :         CPL_LSBPTR64(&_nodeItems[i].minX);
     438             :         CPL_LSBPTR64(&_nodeItems[i].minY);
     439             :         CPL_LSBPTR64(&_nodeItems[i].maxX);
     440             :         CPL_LSBPTR64(&_nodeItems[i].maxY);
     441             :         CPL_LSBPTR64(&_nodeItems[i].offset);
     442             :     }
     443             : #endif
     444         116 :     writeData(reinterpret_cast<uint8_t *>(_nodeItems),
     445         116 :               static_cast<size_t>(_numNodes * sizeof(NodeItem)));
     446             : #if !CPL_IS_LSB
     447             :     for (size_t i = 0; i < static_cast<size_t>(_numNodes); i++)
     448             :     {
     449             :         CPL_LSBPTR64(&_nodeItems[i].minX);
     450             :         CPL_LSBPTR64(&_nodeItems[i].minY);
     451             :         CPL_LSBPTR64(&_nodeItems[i].maxX);
     452             :         CPL_LSBPTR64(&_nodeItems[i].maxY);
     453             :         CPL_LSBPTR64(&_nodeItems[i].offset);
     454             :     }
     455             : #endif
     456         116 : }
     457             : 
     458           0 : NodeItem PackedRTree::getExtent() const
     459             : {
     460           0 :     return _extent;
     461             : }
     462             : 
     463             : }  // namespace FlatGeobuf

Generated by: LCOV version 1.14