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