GEOS 3.16.0dev
DiscreteFrechetDistance.h
1/**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (c) 2021 Felix Obermaier
7 * Copyright (c) 2025 Paul Ramsey
8 *
9 * This is free software; you can redistribute and/or modify it under
10 * the terms of the GNU Lesser General Public Licence as published
11 * by the Free Software Foundation.
12 * See the COPYING file for more information.
13 *
14 **********************************************************************/
15
16#pragma once
17
18#include <geos/export.h>
19#include <geos/algorithm/distance/PointPairDistance.h>
20#include <geos/geom/CoordinateSequenceFilter.h>
21#include <geos/util/math.h>
22
23#include <algorithm> // for lower_bound
24#include <cstdint>
25#include <unordered_map>
26#include <array>
27#include <vector>
28#include <memory>
29
30// Forward declarations
31namespace geos {
32namespace geom {
33class Geometry;
34class Coordinate;
35class CoordinateSequence;
36}
37}
38
39namespace geos {
40namespace algorithm { // geos::algorithm
41namespace distance { // geos::algorithm::distance
42
76
77 using DistanceToPairMap = std::unordered_map<double, std::array<std::size_t, 2>>;
78
79public:
80
88 : g0(geom0)
89 , g1(geom1) {};
90
91
95 class GEOS_DLL MatrixStorage {
96
97 public:
98
99 std::size_t m_numRows;
100 std::size_t m_numCols;
101 double m_defaultValue;
102
109 MatrixStorage(std::size_t numRows, std::size_t numCols, double defaultValue)
110 : m_numRows(numRows)
111 , m_numCols(numCols)
112 , m_defaultValue(defaultValue) {};
113
114 virtual ~MatrixStorage() = 0;
115
122 virtual double get(std::size_t i, std::size_t j) const = 0;
123
130 virtual void set(std::size_t i, std::size_t j, double value) = 0;
131
139 virtual bool isValueSet(std::size_t i, std::size_t j) const = 0;
140 };
141
142
146 class RectMatrix : public MatrixStorage {
147
148 private:
149 std::vector<double> m_matrix;
150
151 public:
152
161 RectMatrix(std::size_t numRows, std::size_t numCols, double defaultValue)
162 : MatrixStorage(numRows, numCols, defaultValue)
163 {
164 m_matrix.resize(numRows * numCols, defaultValue);
165 };
166
167 double get(std::size_t i, std::size_t j) const override {
168 return m_matrix[i * m_numCols + j];
169 };
170
171 void set(std::size_t i, std::size_t j, double value) override {
172 m_matrix[i * m_numCols + j] = value;
173 };
174
175 bool isValueSet(std::size_t i, std::size_t j) const override {
176 return get(i, j) != m_defaultValue;
177 };
178 };
179
180
187 class CsrMatrix : public MatrixStorage {
188
189 private:
190 std::vector<double> m_v;
191 std::vector<std::size_t> m_ri;
192 std::vector<std::size_t> m_ci;
193
194 public:
195
196 CsrMatrix(std::size_t numRows, std::size_t numCols, double defaultValue, std::size_t expectedValues)
197 : MatrixStorage(numRows, numCols, defaultValue)
198 {
199 m_v.resize(expectedValues);
200 m_ci.resize(expectedValues);
201 m_ri.resize(numRows + 1);
202 };
203
204 CsrMatrix(std::size_t numRows, std::size_t numCols, double defaultValue)
205 : CsrMatrix(numRows, numCols, defaultValue, expectedValuesHeuristic(numRows, numCols))
206 {};
207
214 static std::size_t expectedValuesHeuristic(std::size_t numRows, std::size_t numCols) {
215 std::size_t max = std::max(numRows, numCols);
216 return max * max / 10;
217 };
218
232 std::pair<bool, std::size_t>
233 cppBinarySearch(const std::vector<std::size_t>& vec, std::size_t fromIndex, std::size_t toIndex, std::size_t key) const {
234 // Return not found on invalid input
235 if (fromIndex > toIndex || toIndex > vec.size())
236 return {false, 0};
237
238 // Define the iterators for the sub-range
239 auto first_it = vec.begin() + static_cast<std::ptrdiff_t>(fromIndex);
240 auto last_it = vec.begin() + static_cast<std::ptrdiff_t>(toIndex);
241
242 // Perform the binary search using std::lower_bound
243 auto it = std::lower_bound(first_it, last_it, key);
244
245 // Calculate the index relative to the *beginning of the original vector*
246 auto result_index = std::distance(vec.begin(), it);
247
248 // Determine if the element was found and return the appropriate value
249 if (it != last_it && *it == key) {
250 // Element found, return its index
251 return {true, result_index};
252 } else {
253 // Element not found, return the insertion point
254 return {false, result_index};
255 }
256 };
257
258 std::pair<bool, std::size_t> indexOf(std::size_t i, std::size_t j) const {
259 std::size_t cLow = m_ri[i];
260 std::size_t cHigh = m_ri[i+1];
261 if (cHigh <= cLow) return {false, cLow};
262 return cppBinarySearch(m_ci, cLow, cHigh, j);
263 };
264
265 double get(std::size_t i, std::size_t j) const override {
266 // get the index in the vector
267 std::pair<bool, std::size_t> vi = indexOf(i, j);
268 // if the vector index is negative, return default value
269 if (!vi.first)
270 return m_defaultValue;
271
272 return m_v[vi.second];
273 };
274
275 void set(std::size_t i, std::size_t j, double value) override {
276
277 // get the index in the vector
278 std::pair<bool, std::size_t> vi = indexOf(i, j);
279
280 // do we already have a value?
281 if (!vi.first)
282 {
283 // no, we don't, we need to ensure space!
284 ensureCapacity(m_ri[m_numRows] + 1);
285
286 // update row indices
287 for (std::size_t ii = i + 1; ii <= m_numRows; ii++)
288 m_ri[ii] += 1;
289
290 // move and update column indices, move values
291 std::size_t viv = vi.second;
292 for (std::size_t ii = m_ri[m_numRows]; ii > viv; ii--) {
293 m_ci[ii] = m_ci[ii - 1];
294 m_v[ii] = m_v[ii - 1];
295 }
296
297 // insert column index
298 m_ci[viv] = j;
299 }
300
301 // set the new value
302 m_v[vi.second] = value;
303 };
304
305 bool isValueSet(std::size_t i, std::size_t j) const override {
306 auto r = indexOf(i, j);
307 return r.first;
308 };
309
314 void ensureCapacity(std::size_t required) {
315 if (required < m_v.size())
316 return;
317
318 std::size_t increment = std::max(m_numRows, m_numCols);
319 m_v.resize(m_v.size() + increment);
320 m_ci.resize(m_v.size() + increment);
321 };
322 };
323
324
329
330 private:
331 std::unordered_map<std::size_t, double> m_matrix;
332
333 public:
334
341 HashMapMatrix(std::size_t numRows, std::size_t numCols, double defaultValue)
342 : MatrixStorage(numRows, numCols, defaultValue)
343 {};
344
345 double get(std::size_t i, std::size_t j) const override {
346 static constexpr std::size_t halfsz = sizeof(std::size_t) * 4;
347 std::size_t key = i << halfsz | j;
348 auto it_found = m_matrix.find(key);
349 if (it_found != m_matrix.end()) {
350 return (*it_found).second;
351 }
352 else {
353 return m_defaultValue;
354 }
355 };
356
357 void set(std::size_t i, std::size_t j, double value) override {
358 static constexpr std::size_t halfsz = sizeof(std::size_t) * 4;
359 std::size_t key = i << halfsz | j;
360 m_matrix[key] = value;
361 };
362
363 bool isValueSet(std::size_t i, std::size_t j) const override {
364 static constexpr std::size_t halfsz = sizeof(std::size_t) * 4;
365 std::size_t key = i << halfsz | j;
366 auto it_found = m_matrix.find(key);
367 return it_found != m_matrix.end();
368 };
369 };
370
371
380 static double distance(const geom::Geometry& geom0, const geom::Geometry& geom1);
381
392 static double distance(const geom::Geometry& geom0, const geom::Geometry& geom1, double densityFrac);
393
399 std::array<geom::CoordinateXY, 2> getCoordinates();
400
401 void setDensifyFraction(double dFrac);
402
403
404private:
405
406 // Members
407 const geom::Geometry& g0;
408 const geom::Geometry& g1;
409 std::unique_ptr<PointPairDistance> ptDist;
410 double densifyFraction = -1.0;
411
417 /* private */
418 double distance();
419
420 /*
421 * Utility method to ape Java behaviour
422 */
423 void putIfAbsent(
424 DistanceToPairMap& distanceToPair,
425 double key, std::array<std::size_t, 2> val);
426
434 static std::unique_ptr<MatrixStorage> createMatrixStorage(
435 std::size_t rows, std::size_t cols);
436
447 static std::unique_ptr<PointPairDistance> computeFrechet(
448 const geom::CoordinateSequence& coords0,
449 const geom::CoordinateSequence& coords1,
450 std::vector<std::size_t>& diagonal,
451 MatrixStorage& distances,
452 DistanceToPairMap& distanceToPair);
453
462 /* private */
463 static double getMinDistanceAtCorner(
464 MatrixStorage& matrix, std::size_t i, std::size_t j);
465
476 void computeCoordinateDistances(
477 const geom::CoordinateSequence& coords0, const geom::CoordinateSequence& coords1,
478 std::vector<std::size_t>& diagonal,
479 MatrixStorage& distances, DistanceToPairMap& distanceToPair);
480
490 static std::vector<std::size_t> bresenhamDiagonal(
491 std::size_t numCols, std::size_t numRows);
492
499 static std::unique_ptr<geom::CoordinateSequence> getDensifiedCoordinates(
500 const geom::Geometry& geom, double densifyFrac);
501
502
503 class DensifiedCoordinatesFilter : public geom::CoordinateSequenceFilter {
504
505 public:
506
507 DensifiedCoordinatesFilter(double fraction, geom::CoordinateSequence& coords)
508 : m_numSubSegs(static_cast<uint32_t>(util::round(1.0 / fraction)))
509 , m_coords(coords)
510 {};
511
512 void filter_ro(
513 const geom::CoordinateSequence& seq,
514 std::size_t index) override;
515
516 bool isGeometryChanged() const override {
517 return false;
518 }
519
520 bool isDone() const override {
521 return false;
522 }
523
524 private:
525
526 uint32_t m_numSubSegs;
527 geom::CoordinateSequence& m_coords;
528
529 };
530
531
532
533}; // DiscreteFrechetDistance
534
535} // geos::algorithm::distance
536} // geos::algorithm
537} // geos
static std::size_t expectedValuesHeuristic(std::size_t numRows, std::size_t numCols)
Definition DiscreteFrechetDistance.h:214
void ensureCapacity(std::size_t required)
Definition DiscreteFrechetDistance.h:314
void set(std::size_t i, std::size_t j, double value) override
Definition DiscreteFrechetDistance.h:275
bool isValueSet(std::size_t i, std::size_t j) const override
Definition DiscreteFrechetDistance.h:305
double get(std::size_t i, std::size_t j) const override
Definition DiscreteFrechetDistance.h:265
std::pair< bool, std::size_t > cppBinarySearch(const std::vector< std::size_t > &vec, std::size_t fromIndex, std::size_t toIndex, std::size_t key) const
Definition DiscreteFrechetDistance.h:233
double get(std::size_t i, std::size_t j) const override
Definition DiscreteFrechetDistance.h:345
void set(std::size_t i, std::size_t j, double value) override
Definition DiscreteFrechetDistance.h:357
HashMapMatrix(std::size_t numRows, std::size_t numCols, double defaultValue)
Definition DiscreteFrechetDistance.h:341
bool isValueSet(std::size_t i, std::size_t j) const override
Definition DiscreteFrechetDistance.h:363
virtual void set(std::size_t i, std::size_t j, double value)=0
virtual double get(std::size_t i, std::size_t j) const =0
MatrixStorage(std::size_t numRows, std::size_t numCols, double defaultValue)
Definition DiscreteFrechetDistance.h:109
virtual bool isValueSet(std::size_t i, std::size_t j) const =0
void set(std::size_t i, std::size_t j, double value) override
Definition DiscreteFrechetDistance.h:171
double get(std::size_t i, std::size_t j) const override
Definition DiscreteFrechetDistance.h:167
bool isValueSet(std::size_t i, std::size_t j) const override
Definition DiscreteFrechetDistance.h:175
RectMatrix(std::size_t numRows, std::size_t numCols, double defaultValue)
Definition DiscreteFrechetDistance.h:161
The Fréchet distance is a measure of similarity between curves. Thus, it can be used like the Hausdor...
Definition DiscreteFrechetDistance.h:75
static double distance(const geom::Geometry &geom0, const geom::Geometry &geom1, double densityFrac)
static double distance(const geom::Geometry &geom0, const geom::Geometry &geom1)
DiscreteFrechetDistance(const geom::Geometry &geom0, const geom::Geometry &geom1)
Definition DiscreteFrechetDistance.h:87
std::array< geom::CoordinateXY, 2 > getCoordinates()
Interface for classes which provide operations that can be applied to the coordinates in a Coordinate...
Definition CoordinateSequenceFilter.h:56
The internal representation of a list of coordinates inside a Geometry.
Definition CoordinateSequence.h:57
Basic implementation of Geometry, constructed and destructed by GeometryFactory.
Definition Geometry.h:201
Basic namespace for all GEOS functionalities.
Definition geos.h:38