contrek 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +4 -1
- data/README.md +218 -38
- data/contrek.gemspec +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +2 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +52 -148
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.cpp +12 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.cpp +46 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.h +29 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/GeoLocalization.h +32 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/PngSource.cpp +103 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/PngSource.h +40 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/RasterSource.h +22 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/RasterStreamer.h +92 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/TiffSource.cpp +301 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/streaming/TiffSource.h +51 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/FinderUtils.cpp +6 -8
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +7 -2
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +3 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +6 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/GeoJsonStreamingMerger.h +51 -6
- data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +83 -0
- data/ext/cpp_polygon_finder/extconf.rb +9 -0
- data/lib/contrek/bitmaps/bitmap.rb +2 -2
- data/lib/contrek/bitmaps/chunky_bitmap.rb +23 -8
- data/lib/contrek/bitmaps/png_bitmap.rb +4 -0
- data/lib/contrek/bitmaps/rgba_bitmap.rb +50 -0
- data/lib/contrek/bitmaps/streaming/raster_streamer.rb +66 -0
- data/lib/contrek/bitmaps/streaming/sources/ascii.rb +33 -0
- data/lib/contrek/bitmaps/streaming/sources/base.rb +27 -0
- data/lib/contrek/bitmaps/streaming/sources/png.rb +105 -0
- data/lib/contrek/bitmaps/streaming/sources/tiff.rb +349 -0
- data/lib/contrek/finder/concurrent/finder.rb +3 -2
- data/lib/contrek/finder/concurrent/geo_json_streaming_merger.rb +19 -2
- data/lib/contrek/finder/polygon_finder.rb +6 -3
- data/lib/contrek/version.rb +1 -1
- data/lib/contrek.rb +6 -0
- metadata +30 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* TiffSource.cpp
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025-2026 Emanuele Cesaroni
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the GNU Affero General Public License v3 (AGPLv3).
|
|
7
|
+
* See the LICENSE file in this directory for the full license text.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "TiffSource.h"
|
|
11
|
+
|
|
12
|
+
#include <geotiff/geotiff.h>
|
|
13
|
+
#include <geotiff/geotiffio.h>
|
|
14
|
+
#include <geotiff/geokeys.h>
|
|
15
|
+
#include <geotiff/xtiffio.h>
|
|
16
|
+
#include <tiffio.h>
|
|
17
|
+
#include <stdexcept>
|
|
18
|
+
#include <string>
|
|
19
|
+
|
|
20
|
+
static const uint32_t TIFFTAG_MODELPIXELSCALE = 33550;
|
|
21
|
+
static const uint32_t TIFFTAG_MODELTIEPOINT = 33922;
|
|
22
|
+
static const uint32_t TIFFTAG_MODELTRANSFORMATION = 34264;
|
|
23
|
+
|
|
24
|
+
TiffSource::TiffSource(const std::string& filepath, bool suppress_warnings)
|
|
25
|
+
: tiff_(nullptr),
|
|
26
|
+
width_(0),
|
|
27
|
+
height_(0),
|
|
28
|
+
bits_per_sample_(0),
|
|
29
|
+
samples_per_pixel_(0),
|
|
30
|
+
photometric_(0),
|
|
31
|
+
planar_config_(0),
|
|
32
|
+
sample_format_(0),
|
|
33
|
+
current_row_(0) {
|
|
34
|
+
if (suppress_warnings) {
|
|
35
|
+
TIFFSetWarningHandler(nullptr);
|
|
36
|
+
}
|
|
37
|
+
tiff_ = XTIFFOpen(filepath.c_str(), "r");
|
|
38
|
+
|
|
39
|
+
if (!tiff_) {
|
|
40
|
+
throw std::runtime_error("Unable to open TIFF: " + filepath);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
read_image_info();
|
|
45
|
+
read_geo_localization();
|
|
46
|
+
|
|
47
|
+
const tmsize_t scanline_size = TIFFScanlineSize(tiff_);
|
|
48
|
+
if (scanline_size <= 0) throw std::runtime_error("Invalid TIFF scanline size");
|
|
49
|
+
|
|
50
|
+
source_row_.resize(static_cast<std::size_t>(scanline_size));
|
|
51
|
+
}
|
|
52
|
+
catch (...) {
|
|
53
|
+
XTIFFClose(tiff_);
|
|
54
|
+
tiff_ = nullptr;
|
|
55
|
+
throw;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
TiffSource::~TiffSource() {
|
|
60
|
+
if (tiff_) {
|
|
61
|
+
XTIFFClose(tiff_);
|
|
62
|
+
tiff_ = nullptr;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
uint32_t TiffSource::width() const {
|
|
67
|
+
return width_;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
uint32_t TiffSource::height() const {
|
|
71
|
+
return height_;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
uint32_t TiffSource::get_bytes_per_pixel() const {
|
|
75
|
+
return 4;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const GeoLocalization& TiffSource::geo_localization() const {
|
|
79
|
+
return geo_localization_;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
void TiffSource::read_image_info() {
|
|
83
|
+
if (!TIFFGetField(tiff_, TIFFTAG_IMAGEWIDTH, &width_)) throw std::runtime_error("Unable to read TIFF width");
|
|
84
|
+
if (!TIFFGetField(tiff_, TIFFTAG_IMAGELENGTH, &height_)) throw std::runtime_error("Unable to read TIFF height");
|
|
85
|
+
|
|
86
|
+
TIFFGetFieldDefaulted(tiff_, TIFFTAG_BITSPERSAMPLE, &bits_per_sample_);
|
|
87
|
+
TIFFGetFieldDefaulted(tiff_, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel_);
|
|
88
|
+
|
|
89
|
+
if (!TIFFGetField(tiff_, TIFFTAG_PHOTOMETRIC, &photometric_)) throw std::runtime_error("Unable to read TIFF photometric");
|
|
90
|
+
|
|
91
|
+
TIFFGetFieldDefaulted(tiff_, TIFFTAG_PLANARCONFIG, &planar_config_);
|
|
92
|
+
TIFFGetFieldDefaulted(tiff_, TIFFTAG_SAMPLEFORMAT, &sample_format_);
|
|
93
|
+
|
|
94
|
+
if (TIFFIsTiled(tiff_)) throw std::runtime_error("Tiled TIFF is not supported yet");
|
|
95
|
+
if (planar_config_ != PLANARCONFIG_CONTIG) throw std::runtime_error("Unsupported TIFF planar configuration");
|
|
96
|
+
if (bits_per_sample_ != 8 && bits_per_sample_ != 16) throw std::runtime_error("Unsupported TIFF bits per sample");
|
|
97
|
+
if (samples_per_pixel_ != 1 && samples_per_pixel_ != 3 && samples_per_pixel_ != 4) throw std::runtime_error("Unsupported TIFF samples per pixel");
|
|
98
|
+
if (sample_format_ != SAMPLEFORMAT_UINT) throw std::runtime_error("Unsupported TIFF sample format");
|
|
99
|
+
|
|
100
|
+
if (photometric_ != PHOTOMETRIC_MINISWHITE &&
|
|
101
|
+
photometric_ != PHOTOMETRIC_MINISBLACK &&
|
|
102
|
+
photometric_ != PHOTOMETRIC_RGB) {
|
|
103
|
+
throw std::runtime_error("Unsupported TIFF photometric");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void TiffSource::read_geo_localization() {
|
|
108
|
+
double* matrix = nullptr;
|
|
109
|
+
uint16_t matrix_count = 0;
|
|
110
|
+
|
|
111
|
+
if (TIFFGetField(tiff_, TIFFTAG_MODELTRANSFORMATION, &matrix_count, &matrix) && matrix && matrix_count >= 16) {
|
|
112
|
+
read_model_transformation(matrix, matrix_count);
|
|
113
|
+
} else {
|
|
114
|
+
read_pixel_scale_and_tiepoint();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (geo_localization_.valid) read_crs();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
void TiffSource::read_model_transformation(double* matrix, uint16_t count) {
|
|
121
|
+
if (!matrix || count < 16) return;
|
|
122
|
+
|
|
123
|
+
GeoTransform& transform = geo_localization_.transform;
|
|
124
|
+
|
|
125
|
+
transform.x_pixel_size = matrix[0];
|
|
126
|
+
transform.x_row_offset = matrix[1];
|
|
127
|
+
transform.x_origin = matrix[3];
|
|
128
|
+
|
|
129
|
+
transform.y_column_offset = matrix[4];
|
|
130
|
+
transform.y_pixel_size = matrix[5];
|
|
131
|
+
transform.y_origin = matrix[7];
|
|
132
|
+
|
|
133
|
+
geo_localization_.valid = true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
void TiffSource::read_pixel_scale_and_tiepoint() {
|
|
137
|
+
double* scale = nullptr;
|
|
138
|
+
double* tiepoint = nullptr;
|
|
139
|
+
|
|
140
|
+
uint16_t scale_count = 0;
|
|
141
|
+
uint16_t tiepoint_count = 0;
|
|
142
|
+
|
|
143
|
+
if (!TIFFGetField(tiff_, TIFFTAG_MODELPIXELSCALE, &scale_count, &scale)) return;
|
|
144
|
+
if (!TIFFGetField(tiff_, TIFFTAG_MODELTIEPOINT, &tiepoint_count, &tiepoint)) return;
|
|
145
|
+
if (!scale || !tiepoint || scale_count < 2 || tiepoint_count < 6) return;
|
|
146
|
+
|
|
147
|
+
const double pixel_x = tiepoint[0];
|
|
148
|
+
const double pixel_y = tiepoint[1];
|
|
149
|
+
|
|
150
|
+
const double model_x = tiepoint[3];
|
|
151
|
+
const double model_y = tiepoint[4];
|
|
152
|
+
|
|
153
|
+
const double x_pixel_size = scale[0];
|
|
154
|
+
const double y_pixel_size = scale[1];
|
|
155
|
+
|
|
156
|
+
GeoTransform& transform = geo_localization_.transform;
|
|
157
|
+
|
|
158
|
+
transform.x_origin = model_x - pixel_x * x_pixel_size;
|
|
159
|
+
transform.y_origin = model_y + pixel_y * y_pixel_size;
|
|
160
|
+
|
|
161
|
+
transform.x_pixel_size = x_pixel_size;
|
|
162
|
+
transform.y_pixel_size = -y_pixel_size;
|
|
163
|
+
|
|
164
|
+
transform.x_row_offset = 0.0;
|
|
165
|
+
transform.y_column_offset = 0.0;
|
|
166
|
+
|
|
167
|
+
geo_localization_.valid = true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
void TiffSource::read_crs() {
|
|
171
|
+
GTIF* gtif = GTIFNew(tiff_);
|
|
172
|
+
if (!gtif) return;
|
|
173
|
+
|
|
174
|
+
uint16_t model_type = 0;
|
|
175
|
+
uint16_t code = 0;
|
|
176
|
+
|
|
177
|
+
if (GTIFKeyGet(gtif, GTModelTypeGeoKey, &model_type, 0, 1) == 1) {
|
|
178
|
+
if (model_type == ModelTypeProjected) {
|
|
179
|
+
if (GTIFKeyGet(gtif, ProjectedCSTypeGeoKey, &code, 0, 1) == 1) {
|
|
180
|
+
geo_localization_.crs.authority = "EPSG";
|
|
181
|
+
geo_localization_.crs.code = static_cast<int>(code);
|
|
182
|
+
}
|
|
183
|
+
} else if (model_type == ModelTypeGeographic) {
|
|
184
|
+
if (GTIFKeyGet(gtif, GeographicTypeGeoKey, &code, 0, 1) == 1) {
|
|
185
|
+
geo_localization_.crs.authority = "EPSG";
|
|
186
|
+
geo_localization_.crs.code = static_cast<int>(code);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
GTIFFree(gtif);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
bool TiffSource::read_next_row(unsigned char* destination, std::size_t row_size) {
|
|
195
|
+
if (current_row_ >= height_) return false;
|
|
196
|
+
|
|
197
|
+
const std::size_t expected_row_size = static_cast<std::size_t>(width_) * get_bytes_per_pixel();
|
|
198
|
+
|
|
199
|
+
if (row_size != expected_row_size) {
|
|
200
|
+
throw std::runtime_error("Invalid TIFF destination row size");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (TIFFReadScanline(tiff_, source_row_.data(), current_row_, 0) < 0) {
|
|
204
|
+
throw std::runtime_error("TIFFReadScanline failed at row " + std::to_string(current_row_));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
convert_row(destination);
|
|
208
|
+
|
|
209
|
+
++current_row_;
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
void TiffSource::convert_row(unsigned char* destination) {
|
|
214
|
+
if (samples_per_pixel_ == 4 && bits_per_sample_ == 8) {
|
|
215
|
+
for (uint32_t x = 0; x < width_; ++x) {
|
|
216
|
+
const std::size_t source_offset = static_cast<std::size_t>(x) * 4;
|
|
217
|
+
const std::size_t destination_offset = static_cast<std::size_t>(x) * 4;
|
|
218
|
+
|
|
219
|
+
destination[destination_offset] = source_row_[source_offset];
|
|
220
|
+
destination[destination_offset + 1] = source_row_[source_offset + 1];
|
|
221
|
+
destination[destination_offset + 2] = source_row_[source_offset + 2];
|
|
222
|
+
destination[destination_offset + 3] = source_row_[source_offset + 3];
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (samples_per_pixel_ == 3 && bits_per_sample_ == 8) {
|
|
228
|
+
for (uint32_t x = 0; x < width_; ++x) {
|
|
229
|
+
const std::size_t source_offset = static_cast<std::size_t>(x) * 3;
|
|
230
|
+
const std::size_t destination_offset = static_cast<std::size_t>(x) * 4;
|
|
231
|
+
|
|
232
|
+
destination[destination_offset] = source_row_[source_offset];
|
|
233
|
+
destination[destination_offset + 1] = source_row_[source_offset + 1];
|
|
234
|
+
destination[destination_offset + 2] = source_row_[source_offset + 2];
|
|
235
|
+
destination[destination_offset + 3] = 255;
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (samples_per_pixel_ == 1 && bits_per_sample_ == 8) {
|
|
241
|
+
for (uint32_t x = 0; x < width_; ++x) {
|
|
242
|
+
unsigned char value = source_row_[x];
|
|
243
|
+
|
|
244
|
+
if (photometric_ == PHOTOMETRIC_MINISWHITE) value = static_cast<unsigned char>(255 - value);
|
|
245
|
+
|
|
246
|
+
const std::size_t destination_offset = static_cast<std::size_t>(x) * 4;
|
|
247
|
+
|
|
248
|
+
destination[destination_offset] = value;
|
|
249
|
+
destination[destination_offset + 1] = value;
|
|
250
|
+
destination[destination_offset + 2] = value;
|
|
251
|
+
destination[destination_offset + 3] = 255;
|
|
252
|
+
}
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const uint16_t* source16 = reinterpret_cast<const uint16_t*>(source_row_.data());
|
|
257
|
+
|
|
258
|
+
if (samples_per_pixel_ == 4 && bits_per_sample_ == 16) {
|
|
259
|
+
for (uint32_t x = 0; x < width_; ++x) {
|
|
260
|
+
const std::size_t source_offset = static_cast<std::size_t>(x) * 4;
|
|
261
|
+
const std::size_t destination_offset = static_cast<std::size_t>(x) * 4;
|
|
262
|
+
|
|
263
|
+
destination[destination_offset] = static_cast<unsigned char>(source16[source_offset] >> 8);
|
|
264
|
+
destination[destination_offset + 1] = static_cast<unsigned char>(source16[source_offset + 1] >> 8);
|
|
265
|
+
destination[destination_offset + 2] = static_cast<unsigned char>(source16[source_offset + 2] >> 8);
|
|
266
|
+
destination[destination_offset + 3] = static_cast<unsigned char>(source16[source_offset + 3] >> 8);
|
|
267
|
+
}
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (samples_per_pixel_ == 3 && bits_per_sample_ == 16) {
|
|
272
|
+
for (uint32_t x = 0; x < width_; ++x) {
|
|
273
|
+
const std::size_t source_offset = static_cast<std::size_t>(x) * 3;
|
|
274
|
+
const std::size_t destination_offset = static_cast<std::size_t>(x) * 4;
|
|
275
|
+
|
|
276
|
+
destination[destination_offset] = static_cast<unsigned char>(source16[source_offset] >> 8);
|
|
277
|
+
destination[destination_offset + 1] = static_cast<unsigned char>(source16[source_offset + 1] >> 8);
|
|
278
|
+
destination[destination_offset + 2] = static_cast<unsigned char>(source16[source_offset + 2] >> 8);
|
|
279
|
+
destination[destination_offset + 3] = 255;
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (samples_per_pixel_ == 1 && bits_per_sample_ == 16) {
|
|
285
|
+
for (uint32_t x = 0; x < width_; ++x) {
|
|
286
|
+
unsigned char value = static_cast<unsigned char>(source16[x] >> 8);
|
|
287
|
+
|
|
288
|
+
if (photometric_ == PHOTOMETRIC_MINISWHITE) value = static_cast<unsigned char>(255 - value);
|
|
289
|
+
|
|
290
|
+
const std::size_t destination_offset = static_cast<std::size_t>(x) * 4;
|
|
291
|
+
|
|
292
|
+
destination[destination_offset] = value;
|
|
293
|
+
destination[destination_offset + 1] = value;
|
|
294
|
+
destination[destination_offset + 2] = value;
|
|
295
|
+
destination[destination_offset + 3] = 255;
|
|
296
|
+
}
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
throw std::runtime_error("Unsupported TIFF pixel format");
|
|
301
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* TiffSource.h
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025-2026 Emanuele Cesaroni
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the GNU Affero General Public License v3 (AGPLv3).
|
|
7
|
+
* See the LICENSE file in this directory for the full license text.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#pragma once
|
|
11
|
+
|
|
12
|
+
#include "RasterSource.h"
|
|
13
|
+
#include "GeoLocalization.h"
|
|
14
|
+
|
|
15
|
+
#include <cstddef>
|
|
16
|
+
#include <cstdint>
|
|
17
|
+
#include <string>
|
|
18
|
+
#include <vector>
|
|
19
|
+
|
|
20
|
+
struct tiff;
|
|
21
|
+
typedef struct tiff TIFF;
|
|
22
|
+
|
|
23
|
+
class TiffSource : public RasterSource {
|
|
24
|
+
public:
|
|
25
|
+
explicit TiffSource(const std::string& filepath, bool suppress_warnings = false);
|
|
26
|
+
~TiffSource() override;
|
|
27
|
+
uint32_t width() const override;
|
|
28
|
+
uint32_t height() const override;
|
|
29
|
+
uint32_t get_bytes_per_pixel() const override;
|
|
30
|
+
bool read_next_row(unsigned char* destination, std::size_t row_size) override;
|
|
31
|
+
const GeoLocalization& geo_localization() const;
|
|
32
|
+
|
|
33
|
+
private:
|
|
34
|
+
void read_image_info();
|
|
35
|
+
void read_geo_localization();
|
|
36
|
+
void read_pixel_scale_and_tiepoint();
|
|
37
|
+
void read_model_transformation(double* matrix, uint16_t count);
|
|
38
|
+
void read_crs();
|
|
39
|
+
void convert_row(unsigned char* destination);
|
|
40
|
+
TIFF* tiff_;
|
|
41
|
+
uint32_t width_;
|
|
42
|
+
uint32_t height_;
|
|
43
|
+
uint16_t bits_per_sample_;
|
|
44
|
+
uint16_t samples_per_pixel_;
|
|
45
|
+
uint16_t photometric_;
|
|
46
|
+
uint16_t planar_config_;
|
|
47
|
+
uint16_t sample_format_;
|
|
48
|
+
uint32_t current_row_;
|
|
49
|
+
std::vector<unsigned char> source_row_;
|
|
50
|
+
GeoLocalization geo_localization_;
|
|
51
|
+
};
|
|
@@ -17,18 +17,17 @@ void FinderUtils::sanitize_options(pf_Options& options, const Options& incoming_
|
|
|
17
17
|
const Identifier versus = incoming_options.get<Identifier>("versus", options.versus == Node::A ? "a" : "o");
|
|
18
18
|
options.versus = versus == "a" ? Node::A : Node::O;
|
|
19
19
|
options.number_of_tiles = std::max(1, static_cast<int>(incoming_options.get<std::int64_t>(
|
|
20
|
-
"number_of_tiles",
|
|
21
|
-
options.number_of_tiles)));
|
|
20
|
+
"number_of_tiles", options.number_of_tiles)));
|
|
22
21
|
options.connectivity_offset = incoming_options.get<std::int64_t>(
|
|
23
|
-
"connectivity",
|
|
24
|
-
options.connectivity_offset == 1 ? 8 : 4) == 8;
|
|
22
|
+
"connectivity", options.connectivity_offset == 1 ? 8 : 4) == 8;
|
|
25
23
|
options.treemap = incoming_options.get<bool>("treemap", options.treemap);
|
|
26
24
|
options.named_sequences = incoming_options.get<bool>(
|
|
27
|
-
"named_sequences",
|
|
28
|
-
options.named_sequences);
|
|
25
|
+
"named_sequences", options.named_sequences);
|
|
29
26
|
options.bounds = incoming_options.get<bool>("bounds", options.bounds);
|
|
30
27
|
options.unsafe_mode = incoming_options.get<bool>("unsafe_mode", options.unsafe_mode);
|
|
31
28
|
options.deterministic = incoming_options.get<bool>("deterministic", options.deterministic);
|
|
29
|
+
options.processing_height = static_cast<int>(incoming_options.get<std::int64_t>(
|
|
30
|
+
"processing_height", options.processing_height));
|
|
32
31
|
|
|
33
32
|
if (const Options* compress = incoming_options.get_options("compress")) {
|
|
34
33
|
options.compress_uniq = compress->get<bool>("uniq", options.compress_uniq);
|
|
@@ -39,8 +38,7 @@ void FinderUtils::sanitize_options(pf_Options& options, const Options& incoming_
|
|
|
39
38
|
if (compress->contains("visvalingam_tolerance")) {
|
|
40
39
|
options.compress_visvalingam = true;
|
|
41
40
|
options.compress_visvalingam_tolerance = static_cast<float>(compress->get<double>(
|
|
42
|
-
"visvalingam_tolerance",
|
|
43
|
-
options.compress_visvalingam_tolerance));
|
|
41
|
+
"visvalingam_tolerance", options.compress_visvalingam_tolerance));
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include <list>
|
|
12
12
|
#include <map>
|
|
13
13
|
#include <ctime>
|
|
14
|
+
#include <cmath>
|
|
14
15
|
#include <typeinfo>
|
|
15
16
|
#include <string>
|
|
16
17
|
#include <vector>
|
|
@@ -40,7 +41,11 @@ PolygonFinder::PolygonFinder(Bitmap *bitmap,
|
|
|
40
41
|
{ this->rgb_matcher = dynamic_cast<RGBMatcher*>(matcher);
|
|
41
42
|
FinderUtils::sanitize_options(this->options, options);
|
|
42
43
|
|
|
43
|
-
this->
|
|
44
|
+
this->processing_height = std::abs(this->options.processing_height == 0 ? source_bitmap->h() : this->options.processing_height);
|
|
45
|
+
if (this->processing_height > source_bitmap->h()) {
|
|
46
|
+
throw std::invalid_argument("Option processing_height must be less or equal than image height!");
|
|
47
|
+
}
|
|
48
|
+
this->node_cluster = new NodeCluster(this->processing_height, source_bitmap->w(), &this->options);
|
|
44
49
|
|
|
45
50
|
//= SCAN ==============//
|
|
46
51
|
cpu_timer.start();
|
|
@@ -106,7 +111,7 @@ ProcessResult* PolygonFinder::process_info() {
|
|
|
106
111
|
pr->benchmarks = std::move(this->reports);
|
|
107
112
|
pr->treemap = this->node_cluster->treemap;
|
|
108
113
|
pr->width = this->source_bitmap->w();
|
|
109
|
-
pr->height = this->
|
|
114
|
+
pr->height = this->processing_height;
|
|
110
115
|
pr->has_bounds = this->node_cluster->options->bounds;
|
|
111
116
|
pr->versus = this->options.versus;
|
|
112
117
|
pr->options = this->incoming_options_;
|
|
@@ -54,6 +54,7 @@ struct pf_Options {
|
|
|
54
54
|
bool unsafe_mode = false;
|
|
55
55
|
bool bounds = false;
|
|
56
56
|
bool deterministic = false;
|
|
57
|
+
int processing_height = 0;
|
|
57
58
|
int connectivity_offset = 0;
|
|
58
59
|
float compress_visvalingam_tolerance = 10.0;
|
|
59
60
|
int number_of_tiles = 1;
|
|
@@ -159,6 +160,7 @@ class PolygonFinder {
|
|
|
159
160
|
protected:
|
|
160
161
|
int start_x;
|
|
161
162
|
int end_x;
|
|
163
|
+
int processing_height;
|
|
162
164
|
Options incoming_options_;
|
|
163
165
|
|
|
164
166
|
private:
|
|
@@ -173,7 +175,7 @@ class PolygonFinder {
|
|
|
173
175
|
|
|
174
176
|
template <typename M, typename F>
|
|
175
177
|
void run_loop(M* specific_matcher, F&& fetch_color, int offset) {
|
|
176
|
-
int img_h = this->
|
|
178
|
+
int img_h = this->processing_height;
|
|
177
179
|
int bpp = this->source_bitmap->get_bytes_per_pixel();
|
|
178
180
|
|
|
179
181
|
for (int y = 0; y < img_h; y++) {
|
|
@@ -32,6 +32,9 @@ Finder::Finder(int number_of_threads, Bitmap *bitmap, Matcher *matcher, const Op
|
|
|
32
32
|
{ cpu_timer.start();
|
|
33
33
|
FinderUtils::sanitize_options(this->options_, options);
|
|
34
34
|
|
|
35
|
+
if (this->options_.processing_height != 0) {
|
|
36
|
+
this->height = std::abs(this->options_.processing_height);
|
|
37
|
+
}
|
|
35
38
|
double cw = static_cast<double>(this->maximum_width_) / this->options_.number_of_tiles;
|
|
36
39
|
if (cw < 1.0) {
|
|
37
40
|
throw std::runtime_error("One pixel tile width minimum!");
|
|
@@ -54,6 +57,9 @@ Finder::Finder(int number_of_threads, Bitmap *bitmap, Matcher *matcher, const Op
|
|
|
54
57
|
if (this->options_.treemap) {
|
|
55
58
|
base_arguments["treemap"] = true;
|
|
56
59
|
}
|
|
60
|
+
if (this->options_.processing_height != 0) {
|
|
61
|
+
base_arguments["processing_height"] = this->options_.processing_height;
|
|
62
|
+
}
|
|
57
63
|
|
|
58
64
|
CpuTimer t;
|
|
59
65
|
t.start();
|
data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/GeoJsonStreamingMerger.h
CHANGED
|
@@ -10,12 +10,51 @@
|
|
|
10
10
|
#pragma once
|
|
11
11
|
#include <vector>
|
|
12
12
|
#include <string>
|
|
13
|
+
#include <functional>
|
|
14
|
+
#include <stdexcept>
|
|
15
|
+
#include <iomanip>
|
|
16
|
+
#include <limits>
|
|
13
17
|
#include "StreamingMerger.h"
|
|
14
18
|
|
|
15
19
|
class GeoJsonStreamingMerger : public StreamingMerger {
|
|
16
20
|
private:
|
|
17
21
|
unsigned int target_value;
|
|
18
22
|
bool is_first_feature = true;
|
|
23
|
+
std::function<void(const Point&)> point_writer;
|
|
24
|
+
|
|
25
|
+
void configure_point_writer(const Options& options) {
|
|
26
|
+
const Options* geo_localization = options.get_options("geo_localization");
|
|
27
|
+
if (!geo_localization) {
|
|
28
|
+
point_writer = [this](const Point& point) { *stream << "[" << point.y << "," << point.x << "]"; };
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const Options* transform = geo_localization->get_options("transform");
|
|
33
|
+
const Options* crs = geo_localization->get_options("crs");
|
|
34
|
+
if (!transform || !crs) throw std::invalid_argument("Invalid geo_localization.");
|
|
35
|
+
|
|
36
|
+
const std::string authority = crs->get<std::string>("authority", "");
|
|
37
|
+
const auto code = crs->get("code", 0L);
|
|
38
|
+
if (authority != "EPSG" || code != 4326) throw std::invalid_argument("Unsupported CRS: " + authority + ":" + std::to_string(code));
|
|
39
|
+
const double x_origin = transform->get<double>("x_origin", 0.0);
|
|
40
|
+
const double y_origin = transform->get<double>("y_origin", 0.0);
|
|
41
|
+
const double x_pixel_size = transform->get<double>("x_pixel_size", 1.0);
|
|
42
|
+
const double y_pixel_size = transform->get<double>("y_pixel_size", 1.0);
|
|
43
|
+
const double x_row_offset = transform->get<double>("x_row_offset", 0.0);
|
|
44
|
+
const double y_column_offset = transform->get<double>("y_column_offset", 0.0);
|
|
45
|
+
|
|
46
|
+
*stream << std::fixed << std::setprecision(7);
|
|
47
|
+
|
|
48
|
+
point_writer = [this, x_origin, y_origin, x_pixel_size, y_pixel_size, x_row_offset, y_column_offset](const Point& point) {
|
|
49
|
+
const double x = point.y;
|
|
50
|
+
const double y = point.x;
|
|
51
|
+
*stream << "[" << x_origin + x * x_pixel_size + y * x_row_offset << "," << y_origin + x * y_column_offset + y * y_pixel_size << "]";
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void write_point(const Point& point) {
|
|
56
|
+
point_writer(point);
|
|
57
|
+
}
|
|
19
58
|
|
|
20
59
|
protected:
|
|
21
60
|
void write_header() override {
|
|
@@ -40,7 +79,9 @@ class GeoJsonStreamingMerger : public StreamingMerger {
|
|
|
40
79
|
std::ofstream* stream_to,
|
|
41
80
|
unsigned int pixel_value)
|
|
42
81
|
: StreamingMerger(number_of_threads, options, stream_to),
|
|
43
|
-
target_value(pixel_value) {
|
|
82
|
+
target_value(pixel_value) {
|
|
83
|
+
configure_point_writer(options);
|
|
84
|
+
}
|
|
44
85
|
|
|
45
86
|
void stream_raw_polygon(const Polygon& polygon) override {
|
|
46
87
|
if (!stream) return;
|
|
@@ -55,12 +96,14 @@ class GeoJsonStreamingMerger : public StreamingMerger {
|
|
|
55
96
|
const size_t points_size = points.size();
|
|
56
97
|
if (points_size > 0) {
|
|
57
98
|
for (size_t i = 0; i < points_size; ++i) {
|
|
58
|
-
|
|
99
|
+
write_point(points[i]);
|
|
59
100
|
if (i < points_size - 1) *stream << ",";
|
|
60
101
|
}
|
|
61
|
-
*stream << ",
|
|
102
|
+
*stream << ",";
|
|
103
|
+
write_point(points[0]);
|
|
104
|
+
*stream << "]";
|
|
62
105
|
} else {
|
|
63
|
-
*stream << "]
|
|
106
|
+
*stream << "]";
|
|
64
107
|
}
|
|
65
108
|
|
|
66
109
|
for (const std::vector<Point>& inner_points : polygon.inner) {
|
|
@@ -68,10 +111,12 @@ class GeoJsonStreamingMerger : public StreamingMerger {
|
|
|
68
111
|
if (inner_size > 0) {
|
|
69
112
|
*stream << ",[";
|
|
70
113
|
for (size_t i = 0; i < inner_size; ++i) {
|
|
71
|
-
|
|
114
|
+
write_point(inner_points[i]);
|
|
72
115
|
if (i < inner_size - 1) *stream << ",";
|
|
73
116
|
}
|
|
74
|
-
*stream << ",
|
|
117
|
+
*stream << ",";
|
|
118
|
+
write_point(inner_points[0]);
|
|
119
|
+
*stream << "]";
|
|
75
120
|
}
|
|
76
121
|
}
|
|
77
122
|
*stream << "]}}";
|
|
@@ -37,6 +37,16 @@
|
|
|
37
37
|
#include "PolygonFinder/src/polygon/bitmaps/FastPngBitmap.cpp"
|
|
38
38
|
#include "PolygonFinder/src/polygon/bitmaps/RemoteFastPngBitmap.h"
|
|
39
39
|
#include "PolygonFinder/src/polygon/bitmaps/RemoteFastPngBitmap.cpp"
|
|
40
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/RasterSource.h"
|
|
41
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.h"
|
|
42
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/AsciiSource.cpp"
|
|
43
|
+
#ifdef CONTREK_HAS_TIFF
|
|
44
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/TiffSource.h"
|
|
45
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/TiffSource.cpp"
|
|
46
|
+
#endif
|
|
47
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/PngSource.h"
|
|
48
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/PngSource.cpp"
|
|
49
|
+
#include "PolygonFinder/src/polygon/bitmaps/streaming/RasterStreamer.h"
|
|
40
50
|
#include "PolygonFinder/src/polygon/matchers/Matcher.h"
|
|
41
51
|
#include "PolygonFinder/src/polygon/matchers/Matcher.cpp"
|
|
42
52
|
#include "PolygonFinder/src/polygon/matchers/ValueNotMatcher.h"
|
|
@@ -395,6 +405,7 @@ void Init_cpp_polygon_finder() {
|
|
|
395
405
|
.define_method("value_at", &Bitmap::value_at)
|
|
396
406
|
.define_method("w", &Bitmap::w)
|
|
397
407
|
.define_method("h", &Bitmap::h)
|
|
408
|
+
.define_method("get_bytes_per_pixel", &Bitmap::get_bytes_per_pixel)
|
|
398
409
|
.define_method("error", &Bitmap::error)
|
|
399
410
|
.define_method("clear", &Bitmap::clear)
|
|
400
411
|
.define_method("print", &Bitmap::print);
|
|
@@ -402,6 +413,7 @@ void Init_cpp_polygon_finder() {
|
|
|
402
413
|
Data_Type<RawBitmap> rb_cRawBitmap =
|
|
403
414
|
define_class<RawBitmap, Bitmap>("CPPRawBitMap")
|
|
404
415
|
.define_constructor(Constructor<RawBitmap>())
|
|
416
|
+
.define_constructor(Constructor<RawBitmap, uint, uint>())
|
|
405
417
|
.define_method("rgb_value_at", &RawBitmap::rgb_value_at)
|
|
406
418
|
.define_method("w", &RawBitmap::w)
|
|
407
419
|
.define_method("h", &RawBitmap::h)
|
|
@@ -430,6 +442,77 @@ void Init_cpp_polygon_finder() {
|
|
|
430
442
|
.define_method("error", &FastPngBitmap::error)
|
|
431
443
|
.define_method("print", &FastPngBitmap::print);
|
|
432
444
|
|
|
445
|
+
Data_Type<RasterSource> rb_cRasterSource =
|
|
446
|
+
define_class<RasterSource>("CPPRasterSource");
|
|
447
|
+
|
|
448
|
+
Data_Type<AsciiSource> rb_cAsciiSource =
|
|
449
|
+
define_class<AsciiSource, RasterSource>("CPPAsciiSource")
|
|
450
|
+
.define_constructor(Constructor<AsciiSource, Bitmap&>(),Arg("bitmap"))
|
|
451
|
+
.define_method("width",&AsciiSource::width)
|
|
452
|
+
.define_method("height",&AsciiSource::height)
|
|
453
|
+
.define_method("get_bytes_per_pixel",&AsciiSource::get_bytes_per_pixel);
|
|
454
|
+
|
|
455
|
+
Data_Type<PngSource> rb_cPngSource =
|
|
456
|
+
define_class<PngSource, RasterSource>("CPPPngSource")
|
|
457
|
+
.define_constructor(Constructor<PngSource, std::string>(),Arg("filepath"))
|
|
458
|
+
.define_method("width",&PngSource::width)
|
|
459
|
+
.define_method("height",&PngSource::height)
|
|
460
|
+
.define_method("get_bytes_per_pixel",&PngSource::get_bytes_per_pixel);
|
|
461
|
+
|
|
462
|
+
#ifdef CONTREK_HAS_TIFF
|
|
463
|
+
Data_Type<TiffSource> rb_cTiffSource =
|
|
464
|
+
define_class<TiffSource, RasterSource>("CPPTiffSource")
|
|
465
|
+
.define_constructor(Constructor<TiffSource, std::string, bool>(),Arg("filepath"),Arg("suppress_warnings") = false)
|
|
466
|
+
.define_method("width",&TiffSource::width)
|
|
467
|
+
.define_method("height",&TiffSource::height)
|
|
468
|
+
.define_method("get_bytes_per_pixel",&TiffSource::get_bytes_per_pixel)
|
|
469
|
+
.define_method("geo_localization",[](TiffSource& self) {
|
|
470
|
+
const GeoLocalization& geo = self.geo_localization();
|
|
471
|
+
|
|
472
|
+
if (!geo.valid) return Object(Qnil);
|
|
473
|
+
|
|
474
|
+
Hash transform;
|
|
475
|
+
transform[Symbol("x_origin")] = geo.transform.x_origin;
|
|
476
|
+
transform[Symbol("y_origin")] = geo.transform.y_origin;
|
|
477
|
+
transform[Symbol("x_pixel_size")] = geo.transform.x_pixel_size;
|
|
478
|
+
transform[Symbol("y_pixel_size")] = geo.transform.y_pixel_size;
|
|
479
|
+
transform[Symbol("x_row_offset")] = geo.transform.x_row_offset;
|
|
480
|
+
transform[Symbol("y_column_offset")] = geo.transform.y_column_offset;
|
|
481
|
+
|
|
482
|
+
Hash crs;
|
|
483
|
+
crs[Symbol("authority")] = geo.crs.authority;
|
|
484
|
+
crs[Symbol("code")] = geo.crs.code;
|
|
485
|
+
|
|
486
|
+
Hash result;
|
|
487
|
+
result[Symbol("transform")] = transform;
|
|
488
|
+
result[Symbol("crs")] = crs;
|
|
489
|
+
|
|
490
|
+
return Object(result);
|
|
491
|
+
});
|
|
492
|
+
#endif
|
|
493
|
+
|
|
494
|
+
Data_Type<RasterStreamer> rb_cRasterStreamer =
|
|
495
|
+
define_class<RasterStreamer>("CPPRasterStreamer")
|
|
496
|
+
.define_constructor(Constructor<RasterStreamer, RasterSource&, uint32_t>(),Arg("source"),Arg("stripe_height"))
|
|
497
|
+
.define_method("stripe_height",&RasterStreamer::stripe_height)
|
|
498
|
+
.define_method("each",[](RasterStreamer& self, Bitmap& buffer) {
|
|
499
|
+
VALUE block = rb_block_proc();
|
|
500
|
+
self.each(
|
|
501
|
+
buffer,
|
|
502
|
+
[block](Bitmap& bitmap,uint32_t buffer_rows,std::size_t buffer_size, std::size_t buffer_read) {
|
|
503
|
+
Rice::Data_Object<Bitmap> rb_bitmap(&bitmap);
|
|
504
|
+
Rice::Object(block).call(
|
|
505
|
+
"call",
|
|
506
|
+
rb_bitmap,
|
|
507
|
+
buffer_rows,
|
|
508
|
+
buffer_size,
|
|
509
|
+
buffer_read
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
);
|
|
515
|
+
|
|
433
516
|
Data_Type<Matcher> rb_cMatcher =
|
|
434
517
|
define_class<Matcher>("CPPMatcher")
|
|
435
518
|
.define_constructor(Constructor<Matcher, char>())
|