contrek 1.3.2 → 1.3.4

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +3 -1
  4. data/README.md +28 -28
  5. data/contrek.gemspec +1 -0
  6. data/ext/cpp_polygon_finder/PolygonFinder/src/ContrekApi.h +2 -0
  7. data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +2 -4
  8. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/FinderUtils.cpp +11 -5
  9. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.cpp +8 -10
  10. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +4 -1
  11. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.cpp +114 -42
  12. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/NodeCluster.h +1 -1
  13. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +2 -1
  14. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cluster.cpp +1 -1
  15. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.cpp +66 -116
  16. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Cursor.h +1 -1
  17. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Finder.cpp +1 -0
  18. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/GeoJsonStreamingMerger.h +6 -7
  19. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.cpp +2 -87
  20. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Part.h +3 -13
  21. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/PartPool.cpp +0 -1
  22. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.cpp +1 -57
  23. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Partitionable.h +1 -4
  24. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Queueable.h +31 -17
  25. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.cpp +0 -16
  26. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Sequence.h +0 -9
  27. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.cpp +2 -6
  28. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.h +1 -4
  29. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/SvgStreamingMerger.h +14 -1
  30. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/concurrent/Tile.cpp +1 -1
  31. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/DouglasPeuckerReducer.cpp +177 -0
  32. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/DouglasPeuckerReducer.h +52 -0
  33. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/RasterReducer.cpp +109 -0
  34. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/RasterReducer.h +32 -0
  35. data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/reducers/VisvalingamReducer.h +6 -1
  36. data/ext/cpp_polygon_finder/cpp_polygon_finder.cpp +20 -2
  37. data/lib/contrek/bitmaps/painting.rb +27 -10
  38. data/lib/contrek/bitmaps/png_bitmap.rb +3 -0
  39. data/lib/contrek/bitmaps/raw_bitmap.rb +1 -0
  40. data/lib/contrek/bitmaps/rendering.rb +74 -0
  41. data/lib/contrek/cpp/cpp_geo_json_concurrent_streaming_merger.rb +11 -0
  42. data/lib/contrek/cpp/{cpp_concurrent_streaming_merger.rb → cpp_svg_concurrent_streaming_merger.rb} +1 -1
  43. data/lib/contrek/finder/concurrent/cursor.rb +62 -84
  44. data/lib/contrek/finder/concurrent/finder.rb +2 -1
  45. data/lib/contrek/finder/concurrent/geo_json_streaming_merger.rb +46 -0
  46. data/lib/contrek/finder/concurrent/part.rb +4 -77
  47. data/lib/contrek/finder/concurrent/partitionable.rb +1 -58
  48. data/lib/contrek/finder/concurrent/queueable.rb +23 -5
  49. data/lib/contrek/finder/concurrent/sequence.rb +0 -15
  50. data/lib/contrek/finder/concurrent/streaming_merger.rb +5 -27
  51. data/lib/contrek/finder/concurrent/svg_streaming_merger.rb +35 -0
  52. data/lib/contrek/finder/concurrent/tile.rb +1 -1
  53. data/lib/contrek/finder/node.rb +26 -8
  54. data/lib/contrek/finder/node_cluster.rb +91 -37
  55. data/lib/contrek/finder/polygon_finder.rb +1 -1
  56. data/lib/contrek/reducers/douglas_peucker_reducer.rb +191 -0
  57. data/lib/contrek/reducers/raster_reducer.rb +72 -0
  58. data/lib/contrek/version.rb +1 -1
  59. data/lib/contrek.rb +7 -1
  60. metadata +27 -3
@@ -20,14 +20,5 @@ class Sequence : public Queueable<Point>{
20
20
  public:
21
21
  Sequence() {}
22
22
  std::string toString();
23
- bool is_not_vertical();
24
23
  Shape* shape = nullptr;
25
- const std::vector<Point>& get_vector_cache() {
26
- if (vector_cache.empty() && this->size > 0) {
27
- vector_cache = this->to_vector();
28
- }
29
- return vector_cache;
30
- }
31
- private:
32
- std::vector<Point> vector_cache;
33
24
  };
@@ -15,15 +15,11 @@
15
15
 
16
16
  StreamingMerger::StreamingMerger(int number_of_threads,
17
17
  std::vector<std::string>* options,
18
- std::ofstream* stream_to,
19
- int total_width, int total_height)
20
- : VerticalMerger(number_of_threads, options), stream(stream_to), total_width(total_width), total_height(total_height) {
18
+ std::ofstream* stream_to)
19
+ : VerticalMerger(number_of_threads, options), stream(stream_to) {
21
20
  if (!stream) {
22
21
  throw std::invalid_argument("Streaming requires a valid destination output. stream_to cannot be null.");
23
22
  }
24
- if (total_width <= 0 || total_height <= 0) {
25
- throw std::invalid_argument("Streaming requires valid canvas dimensions (width and height must be > 0).");
26
- }
27
23
  }
28
24
 
29
25
  void StreamingMerger::add_tile(ProcessResult& result, bool flush)
@@ -18,8 +18,6 @@
18
18
  class StreamingMerger : public VerticalMerger {
19
19
  protected:
20
20
  std::ofstream* stream;
21
- int total_width;
22
- int total_height;
23
21
  int moved = 0;
24
22
 
25
23
  void stream_polygons(Tile* tile, bool flush = false);
@@ -36,8 +34,7 @@ class StreamingMerger : public VerticalMerger {
36
34
  public:
37
35
  StreamingMerger(int number_of_threads,
38
36
  std::vector<std::string>* options,
39
- std::ofstream* stream_to,
40
- int total_width, int total_height);
37
+ std::ofstream* stream_to);
41
38
  void add_tile(ProcessResult& result, bool flush = false);
42
39
  ProcessResult* process_info() override;
43
40
  };
@@ -9,10 +9,13 @@
9
9
 
10
10
  #pragma once
11
11
  #include <string>
12
+ #include <vector>
12
13
  #include "StreamingMerger.h"
13
14
 
14
15
  class SvgStreamingMerger : public StreamingMerger {
15
16
  private:
17
+ int total_width;
18
+ int total_height;
16
19
  std::string svg_css() {
17
20
  return ".out{fill:none;stroke:red;stroke-width:1;}.in{fill:none;stroke:green;stroke-width:1;}.out:hover{stroke:yellow;}";
18
21
  }
@@ -39,5 +42,15 @@ class SvgStreamingMerger : public StreamingMerger {
39
42
  }
40
43
 
41
44
  public:
42
- using StreamingMerger::StreamingMerger;
45
+ SvgStreamingMerger(int number_of_threads,
46
+ std::vector<std::string>* options,
47
+ std::ofstream* stream_to,
48
+ int total_width, int total_height)
49
+ : StreamingMerger(number_of_threads, options, stream_to),
50
+ total_width(total_width),
51
+ total_height(total_height) {
52
+ if (total_width <= 0 || total_height <= 0) {
53
+ throw std::invalid_argument("SVG Streaming requires valid canvas dimensions (width and height must be > 0).");
54
+ }
55
+ }
43
56
  };
@@ -94,7 +94,7 @@ void Tile::assign_shapes(std::vector<Shape*>& shapes) {
94
94
  }
95
95
 
96
96
  bool Tile::tg_border(const Point& coord) {
97
- return( coord.x == (this->next == nullptr ? start_x_ : (end_x_ - 1)));
97
+ return( coord.x == (this->next == nullptr ? start_x_ : (end_x_)));
98
98
  }
99
99
 
100
100
  void Tile::info() {
@@ -0,0 +1,177 @@
1
+ /*
2
+ * DouglasPeuckerReducer.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 "DouglasPeuckerReducer.h"
11
+ #include <cstdint>
12
+ #include <limits>
13
+ #include <vector>
14
+
15
+ DouglasPeuckerReducer::DouglasPeuckerReducer(std::vector<Point>& list_of_points)
16
+ : Reducer(list_of_points) {
17
+ }
18
+
19
+ void DouglasPeuckerReducer::reduce() {
20
+ const std::size_t count = points.size();
21
+ if (count < 4) {
22
+ return;
23
+ }
24
+ const std::size_t first_index = extremeIndexByX();
25
+ const std::size_t second_index = farthestIndexFrom(first_index);
26
+ if (second_index == first_index) {
27
+ return;
28
+ }
29
+ std::vector<bool> keep(count, false);
30
+ keep[first_index] = true;
31
+ keep[second_index] = true;
32
+ simplifyArc(first_index, second_index, keep);
33
+ simplifyArc(second_index, first_index, keep);
34
+ compactRing(keep);
35
+ }
36
+
37
+ std::size_t DouglasPeuckerReducer::extremeIndexByX() const {
38
+ std::size_t best = 0;
39
+ int best_x = points[0].x;
40
+ for (std::size_t index = 1; index < points.size(); ++index) {
41
+ const int x = points[index].x;
42
+ if (x < best_x) {
43
+ best_x = x;
44
+ best = index;
45
+ }
46
+ }
47
+ return best;
48
+ }
49
+
50
+ std::size_t DouglasPeuckerReducer::farthestIndexFrom(std::size_t origin_index) const {
51
+ const Point& origin = points[origin_index];
52
+ std::size_t best_index = origin_index;
53
+ std::int64_t best_distance = -1;
54
+ for (std::size_t index = 0; index < points.size(); ++index) {
55
+ const std::int64_t dx = static_cast<std::int64_t>(points[index].x) - origin.x;
56
+ const std::int64_t dy = static_cast<std::int64_t>(points[index].y) - origin.y;
57
+ const std::int64_t distance = dx * dx + dy * dy;
58
+ if (distance > best_distance) {
59
+ best_distance = distance;
60
+ best_index = index;
61
+ }
62
+ }
63
+ return best_index;
64
+ }
65
+
66
+ void DouglasPeuckerReducer::simplifyArc(std::size_t first_index, std::size_t last_index, std::vector<bool>& keep) const {
67
+ const std::size_t count = points.size();
68
+ std::vector<Arc> stack;
69
+ stack.push_back({first_index, last_index});
70
+ while (!stack.empty()) {
71
+ const Arc arc = stack.back();
72
+ stack.pop_back();
73
+ std::size_t split_index = 0;
74
+ if (!farthestOnArc(
75
+ arc.first,
76
+ arc.last,
77
+ count,
78
+ split_index)) {
79
+ continue;
80
+ }
81
+ keep[split_index] = true;
82
+ if (nextIndex(arc.first, count) != split_index) {
83
+ stack.push_back({arc.first, split_index});
84
+ }
85
+ if (nextIndex(split_index, count) != arc.last) {
86
+ stack.push_back({split_index, arc.last});
87
+ }
88
+ }
89
+ }
90
+
91
+ bool DouglasPeuckerReducer::farthestOnArc(std::size_t first_index, std::size_t last_index,
92
+ std::size_t count, std::size_t& split_index) const {
93
+ const Point& first = points[first_index];
94
+ const Point& last = points[last_index];
95
+ const double ax = static_cast<double>(first.x);
96
+ const double ay = static_cast<double>(first.y);
97
+ const double bx = static_cast<double>(last.x);
98
+ const double by = static_cast<double>(last.y);
99
+ const double abx = bx - ax;
100
+ const double aby = by - ay;
101
+ const double length_squared = abx * abx + aby * aby;
102
+ double max_distance = TOLERANCE_SQUARED;
103
+ bool found = false;
104
+ std::size_t index = nextIndex(first_index, count);
105
+
106
+ while (index != last_index) {
107
+ const Point& point = points[index];
108
+ double distance;
109
+ if (length_squared == 0.0) {
110
+ const double dx = static_cast<double>(point.x) - ax;
111
+ const double dy = static_cast<double>(point.y) - ay;
112
+ distance = dx * dx + dy * dy;
113
+ } else {
114
+ distance = pointSegmentDistanceSquared(
115
+ static_cast<double>(point.x),
116
+ static_cast<double>(point.y),
117
+ ax,
118
+ ay,
119
+ bx,
120
+ by,
121
+ abx,
122
+ aby,
123
+ length_squared);
124
+ }
125
+ if (distance > max_distance) {
126
+ max_distance = distance;
127
+ split_index = index;
128
+ found = true;
129
+ }
130
+ index = nextIndex(index, count);
131
+ }
132
+ return found;
133
+ }
134
+
135
+ double DouglasPeuckerReducer::pointSegmentDistanceSquared(
136
+ double px,
137
+ double py,
138
+ double ax,
139
+ double ay,
140
+ double bx,
141
+ double by,
142
+ double abx,
143
+ double aby,
144
+ double length_squared) {
145
+ const double apx = px - ax;
146
+ const double apy = py - ay;
147
+ const double dot = apx * abx + apy * aby;
148
+ if (dot <= 0.0) {
149
+ return apx * apx + apy * apy;
150
+ }
151
+ if (dot >= length_squared) {
152
+ const double bpx = px - bx;
153
+ const double bpy = py - by;
154
+ return bpx * bpx + bpy * bpy;
155
+ }
156
+ const double cross = apx * aby - apy * abx;
157
+ return (cross * cross) / length_squared;
158
+ }
159
+
160
+ std::size_t DouglasPeuckerReducer::nextIndex(std::size_t index, std::size_t count) {
161
+ return index + 1 == count ? 0 : index + 1;
162
+ }
163
+
164
+ void DouglasPeuckerReducer::compactRing(const std::vector<bool>& keep) {
165
+ std::size_t write_index = 0;
166
+ for (
167
+ std::size_t read_index = 0;
168
+ read_index < points.size();
169
+ ++read_index
170
+ ) {
171
+ if (keep[read_index]) {
172
+ points[write_index] = points[read_index];
173
+ ++write_index;
174
+ }
175
+ }
176
+ points.resize(write_index);
177
+ }
@@ -0,0 +1,52 @@
1
+ /*
2
+ * DouglasPeuckerReducer.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
+ #include <cstddef>
12
+ #include <vector>
13
+ #include "Reducer.h"
14
+
15
+ struct Point;
16
+
17
+ class DouglasPeuckerReducer : public Reducer {
18
+ public:
19
+ explicit DouglasPeuckerReducer(std::vector<Point>& list_of_points);
20
+ void reduce() override;
21
+
22
+ private:
23
+ static constexpr double TOLERANCE_SQUARED = 1.0;
24
+ struct Arc {
25
+ std::size_t first;
26
+ std::size_t last;
27
+ };
28
+ std::size_t extremeIndexByX() const;
29
+ std::size_t farthestIndexFrom(
30
+ std::size_t origin_index) const;
31
+ void simplifyArc(
32
+ std::size_t first_index,
33
+ std::size_t last_index,
34
+ std::vector<bool>& keep) const;
35
+ bool farthestOnArc(
36
+ std::size_t first_index,
37
+ std::size_t last_index,
38
+ std::size_t count,
39
+ std::size_t& split_index) const;
40
+ static double pointSegmentDistanceSquared(
41
+ double px,
42
+ double py,
43
+ double ax,
44
+ double ay,
45
+ double bx,
46
+ double by,
47
+ double abx,
48
+ double aby,
49
+ double length_squared);
50
+ static std::size_t nextIndex(std::size_t index, std::size_t count);
51
+ void compactRing(const std::vector<bool>& keep);
52
+ };
@@ -0,0 +1,109 @@
1
+ /*
2
+ * RasterReducer.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 "RasterReducer.h"
11
+ #include <stdexcept>
12
+ #include <string>
13
+
14
+ RasterReducer::RasterReducer(std::vector<Point>& points, int versus)
15
+ : Reducer(points),
16
+ versus_(versus) {
17
+ }
18
+
19
+ void RasterReducer::reduce() {
20
+ toRasterPolygon(points, versus_);
21
+ }
22
+
23
+ void RasterReducer::toRasterPolygon(std::vector<Point>& points, int versus) {
24
+ const std::size_t n = points.size();
25
+
26
+ if (n == 0) {
27
+ return;
28
+ }
29
+
30
+ const bool outer = versus == Node::O;
31
+ const int firstX = points.front().x;
32
+ const int firstY = points.front().y;
33
+ int previousX = points.back().x;
34
+ int previousY = points.back().y;
35
+
36
+ for (std::size_t i = 0; i < n; ++i) {
37
+ const int currentX = points[i].x;
38
+ const int currentY = points[i].y;
39
+ int nextX;
40
+ int nextY;
41
+ if (i + 1 < n) {
42
+ nextX = points[i + 1].x;
43
+ nextY = points[i + 1].y;
44
+ } else {
45
+ nextX = firstX;
46
+ nextY = firstY;
47
+ }
48
+ const Pole incomingSide = segmentPole(
49
+ currentX - previousX,
50
+ currentY - previousY,
51
+ outer);
52
+ const Pole outgoingSide = segmentPole(
53
+ nextX - currentX,
54
+ nextY - currentY,
55
+ outer);
56
+ int x = currentX;
57
+ int y = currentY;
58
+ const auto applySide = [&x, &y](Pole side) {
59
+ switch (side) {
60
+ case Pole::South:
61
+ --y;
62
+ break;
63
+
64
+ case Pole::East:
65
+ --x;
66
+ break;
67
+
68
+ case Pole::North:
69
+ case Pole::West:
70
+ break;
71
+ }
72
+ };
73
+ applySide(incomingSide);
74
+ if (outgoingSide != incomingSide) {
75
+ applySide(outgoingSide);
76
+ }
77
+ points[i].x = x;
78
+ points[i].y = y;
79
+ previousX = currentX;
80
+ previousY = currentY;
81
+ }
82
+ }
83
+
84
+ RasterReducer::Pole RasterReducer::segmentPole(int dx, int dy, bool clockwise) {
85
+ const int sx = sign(dx);
86
+ const int sy = sign(dy);
87
+ if (sx == 0 && sy == 1) {
88
+ return clockwise ? Pole::East : Pole::West;
89
+ }
90
+ if (sx == 1 && sy == 0) {
91
+ return clockwise ? Pole::North : Pole::South;
92
+ }
93
+ if (sx == 0 && sy == -1) {
94
+ return clockwise ? Pole::West : Pole::East;
95
+ }
96
+ if (sx == -1 && sy == 0) {
97
+ return clockwise ? Pole::South : Pole::North;
98
+ }
99
+ throw std::invalid_argument(
100
+ "Non-axial segment dx=" +
101
+ std::to_string(dx) +
102
+ " dy=" +
103
+ std::to_string(dy) +
104
+ "!");
105
+ }
106
+
107
+ int RasterReducer::sign(int value) {
108
+ return (value > 0) - (value < 0);
109
+ }
@@ -0,0 +1,32 @@
1
+ /*
2
+ * RasterReducer.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
+ #include <vector>
12
+ #include "Reducer.h"
13
+
14
+ struct Point;
15
+
16
+ class RasterReducer : public Reducer {
17
+ public:
18
+ RasterReducer(std::vector<Point>& points, int versus);
19
+ void reduce() override;
20
+
21
+ private:
22
+ enum class Pole {
23
+ North,
24
+ South,
25
+ East,
26
+ West
27
+ };
28
+ int versus_;
29
+ void toRasterPolygon(std::vector<Point>& points, int versus);
30
+ static Pole segmentPole(int dx, int dy, bool clockwise);
31
+ static int sign(int value);
32
+ };
@@ -25,7 +25,12 @@ class VisvalingamReducer : public Reducer {
25
25
  class Triangle {
26
26
  public:
27
27
  static int area(const Point& a, const Point& b, const Point& c) {
28
- return std::abs(((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2);
28
+ const int cross = (c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y);
29
+ int half = cross / 2;
30
+ if (cross < 0 && cross % 2 != 0) {
31
+ --half;
32
+ }
33
+ return std::abs(half);
29
34
  }
30
35
  };
31
36
 
@@ -52,6 +52,10 @@
52
52
  #include "PolygonFinder/src/polygon/reducers/LinearReducer.h"
53
53
  #include "PolygonFinder/src/polygon/reducers/VisvalingamReducer.cpp"
54
54
  #include "PolygonFinder/src/polygon/reducers/VisvalingamReducer.h"
55
+ #include "PolygonFinder/src/polygon/reducers/RasterReducer.cpp"
56
+ #include "PolygonFinder/src/polygon/reducers/RasterReducer.h"
57
+ #include "PolygonFinder/src/polygon/reducers/DouglasPeuckerReducer.cpp"
58
+ #include "PolygonFinder/src/polygon/reducers/DouglasPeuckerReducer.h"
55
59
  #include "PolygonFinder/src/polygon/finder/concurrent/Finder.h"
56
60
  #include "PolygonFinder/src/polygon/finder/concurrent/Finder.cpp"
57
61
  #include "PolygonFinder/src/polygon/finder/concurrent/Poolable.h"
@@ -97,6 +101,7 @@
97
101
  #include "PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.h"
98
102
  #include "PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.cpp"
99
103
  #include "PolygonFinder/src/polygon/finder/concurrent/SvgStreamingMerger.h"
104
+ #include "PolygonFinder/src/polygon/finder/concurrent/GeoJsonStreamingMerger.h"
100
105
  #include "PolygonFinder/src/polygon/finder/concurrent/ShapePool.h"
101
106
  #include "PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp"
102
107
  extern "C" {
@@ -309,7 +314,7 @@ struct OfstreamWrapper {
309
314
  std::ofstream& get_stream() { return stream; }
310
315
  };
311
316
 
312
- StreamingMerger* create_streaming_merger(Object self,
317
+ SvgStreamingMerger* create_svg_streaming_merger(Object self,
313
318
  int number_of_threads,
314
319
  std::vector<std::string>* options,
315
320
  Object stream_obj,
@@ -319,6 +324,15 @@ StreamingMerger* create_streaming_merger(Object self,
319
324
  return new SvgStreamingMerger(number_of_threads, options, &wrapper->get_stream(), total_width, total_height);
320
325
  }
321
326
 
327
+ GeoJsonStreamingMerger* create_geo_json_streaming_merger(Object self,
328
+ int number_of_threads,
329
+ std::vector<std::string>* options,
330
+ Object stream_obj,
331
+ int pixel_val) {
332
+ OfstreamWrapper* wrapper = Rice::detail::From_Ruby<OfstreamWrapper*>().convert(stream_obj.value());
333
+ return new GeoJsonStreamingMerger(number_of_threads, options, &wrapper->get_stream(),pixel_val);
334
+ }
335
+
322
336
  OfstreamWrapper* create_ofstream(Object self, std::string path) {
323
337
  return new OfstreamWrapper(path);
324
338
  }
@@ -437,7 +451,11 @@ void Init_cpp_polygon_finder() {
437
451
  ProcessResult pr = Rice::detail::ruby_result_to_process_result(rb_result);
438
452
  self.add_tile(pr, flush);
439
453
  });
440
- rb_cstreaming_merger.define_singleton_method("new", &create_streaming_merger);
454
+
455
+ Data_Type<SvgStreamingMerger> rb_csvgstreaming_merger = define_class<SvgStreamingMerger, StreamingMerger>("CPPSvgStreamingMerger");
456
+ rb_csvgstreaming_merger.define_singleton_method("new", &create_svg_streaming_merger);
457
+ Data_Type<GeoJsonStreamingMerger> rb_cgeojsonstreaming_merger = define_class<GeoJsonStreamingMerger, StreamingMerger>("CPPGeoJsonStreamingMerger");
458
+ rb_cgeojsonstreaming_merger.define_singleton_method("new", &create_geo_json_streaming_merger);
441
459
 
442
460
  Module mContrek = define_module("Contrek");
443
461
  Module mCpp = define_module_under(mContrek, "Cpp");
@@ -21,23 +21,40 @@ module Contrek
21
21
  colors.sort_by { |color, count| -color }
22
22
  end
23
23
 
24
- def self.direct_draw_polygons(polygons, png_image)
24
+ def self.direct_draw_polygons(polygons, png_image, zoom: 1)
25
25
  polygons.compact.each do |poly|
26
- color = ChunkyPNG::Color("red @ 1.0")
27
- poly[:outer].each_cons(2) do |coords|
28
- png_image.draw_line(coords[0][:x], coords[0][:y], coords[1][:x], coords[1][:y], color)
26
+ if (bounds = poly[:bounds])
27
+ b_factor = 3.to_f / zoom
28
+ bounds_polygon = [
29
+ {x: bounds[:min_x] - b_factor, y: bounds[:min_y] - b_factor},
30
+ {x: bounds[:max_x] + b_factor, y: bounds[:min_y] - b_factor},
31
+ {x: (bounds[:max_x] + b_factor), y: bounds[:max_y] + b_factor},
32
+ {x: (bounds[:min_x] - b_factor), y: bounds[:max_y] + b_factor}
33
+ ]
34
+ draw_polygon(bounds_polygon, png_image, ChunkyPNG::Color("blue @ 1.0"), zoom)
29
35
  end
30
- png_image.draw_line(poly[:outer][0][:x], poly[:outer][0][:y], poly[:outer][-1][:x], poly[:outer][-1][:y], color)
36
+ color = ChunkyPNG::Color("red @ 1.0")
37
+ draw_polygon(poly[:outer], png_image, color, zoom)
31
38
  color = ChunkyPNG::Color("green @ 1.0")
32
39
  poly[:inner].each do |sequence|
33
- next if sequence.empty?
34
- sequence.each_cons(2) do |coords|
35
- png_image.draw_line(coords[0][:x], coords[0][:y], coords[1][:x], coords[1][:y], color)
36
- end
37
- png_image.draw_line(sequence[0][:x], sequence[0][:y], sequence[-1][:x], sequence[-1][:y], color)
40
+ draw_polygon(sequence, png_image, color, zoom)
38
41
  end
39
42
  end
40
43
  end
44
+
45
+ def self.draw_polygon(sequence, png_image, color, zoom, margin = 0)
46
+ return if sequence.empty?
47
+ sequence.each_cons(2) do |coords|
48
+ png_image.draw_line(coords[0][:x] * zoom,
49
+ coords[0][:y] * zoom,
50
+ coords[1][:x] * zoom,
51
+ coords[1][:y] * zoom, color)
52
+ end
53
+ png_image.draw_line(sequence[0][:x] * zoom,
54
+ sequence[0][:y] * zoom,
55
+ sequence[-1][:x] * zoom,
56
+ sequence[-1][:y] * zoom, color)
57
+ end
41
58
  end
42
59
  end
43
60
  end
@@ -5,6 +5,9 @@ require "chunky_png"
5
5
  module Contrek
6
6
  module Bitmaps
7
7
  class PngBitmap < Bitmap
8
+ include Rendering
9
+
10
+ attr_reader :image
8
11
  def initialize(file_path)
9
12
  @image = ChunkyPNG::Image.from_file(file_path)
10
13
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "ruby2d"
3
4
  module Contrek
4
5
  module Bitmaps
5
6
  class RawBitmap < PngBitmap
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Contrek
4
+ module Bitmaps
5
+ module Rendering
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def to_window(result, zoom: 100, coords: true, window_w: 2000, window_h: 1200, font_h: 20)
12
+ ubuntu_purple = ChunkyPNG::Color.from_hex("#77216F")
13
+ bm = RawBitmap.new(w: window_w, h: window_h, color: ubuntu_purple)
14
+ # grid
15
+ light_purple = ChunkyPNG::Color.from_hex("#95338B")
16
+ (window_w / zoom).times { |x| bm.draw_line(x * zoom, 0, x * zoom, window_h, light_purple) }
17
+ (window_h / zoom).times { |y| bm.draw_line(0, y * zoom, window_w, y * zoom, light_purple) }
18
+ # polygons
19
+ Painting.direct_draw_polygons(result.points, bm, zoom: zoom)
20
+ # windowing
21
+ Tempfile.create do |temp_file|
22
+ bm.image.save(temp_file.path, color_mode: ChunkyPNG::COLOR_TRUECOLOR_ALPHA)
23
+ Window.clear
24
+ Window.set(
25
+ title: "Rendering #{result[:metadata][:groups]} polygons",
26
+ width: window_w,
27
+ height: window_h,
28
+ resizable: true
29
+ )
30
+ Image.new(
31
+ temp_file.path,
32
+ x: 0, y: 0,
33
+ width: window_w,
34
+ height: window_h
35
+ )
36
+ draw_labels(result.points, zoom: zoom, draw_coords: coords, font_h:)
37
+ Window.on :key_down do |event|
38
+ if event.key == "escape"
39
+ Window.close
40
+ end
41
+ if event.key == "s"
42
+ Window.screenshot("window_content.png")
43
+ end
44
+ end
45
+ Window.show
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def draw_labels(polygons, font_h:, zoom: 1.0, draw_coords: true)
52
+ positions = []
53
+ polygons.compact.each do |poly|
54
+ ([poly[:outer]] + poly[:inner]).each do |seq|
55
+ seq.each_with_index do |coords, n|
56
+ x = coords[:x] * zoom
57
+ y = coords[:y] * zoom
58
+ y += font_h + 2 if positions.index([x, y])
59
+ t = (n + 1).to_s
60
+ t += " - [#{coords[:x]},#{coords[:y]}]" if draw_coords
61
+ draw_text(x, y, t, font_h) unless draw_coords.nil?
62
+ positions << [x, y]
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def draw_text(x, y, text, font_h)
69
+ Text.new(text, x:, y:, size: font_h, color: "white", z: 10)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end