contrek 1.3.2 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8fcfc790a324e42855e8b8e816f946da8ea764943a44f4d9999b4cc7a360209d
4
- data.tar.gz: 8f0d0f42350fc8236a9c2e5ca974ab11d91a5bcfbdd4a7fdef9cf310bf764306
3
+ metadata.gz: 14fceba0577a0e8bf3ce358d23099eb7d359dfdaafdbfac3489f853001be47b3
4
+ data.tar.gz: 4097a012082df7a8c59dd873c2bac9556b0ac5f40699183cb3de1410a9f34a5c
5
5
  SHA512:
6
- metadata.gz: b7a5c008b6811b36ce143642d6300e89d7975dd0861cfc429880bbd76bf8a4b2eadf6c24351ca6d3de746ddbecaec849b4aed2052e973e34a386a9ae20e4a4a6
7
- data.tar.gz: 8ff2d2465df0afcd376255bb575a46581d27888ec5e7293d9971e27f624550dc83e1fb47ca11814cbeb220c0b38c51b7179087361fbab9140ca181a728de706e
6
+ metadata.gz: ed0bf7cbf8f90b6327b8ad2ca990e0f05a59e664a9ea04a20a164f51ad0640a85275c80ff34b21e66c2611226448c0c9c7fbc7effeb1c4f51c02c3550267fbdd
7
+ data.tar.gz: 9e9677f9fddbd0746a594a26315eb93773f078ed78035cb2b3b36030036079382e7bea31fc4cbc040d7e7d21f1e8fd158a7609f11d1a71aa257592a963af4eb6
data/CHANGELOG.md CHANGED
@@ -132,3 +132,6 @@ All notable changes to this project will be documented in this file.
132
132
 
133
133
  ## [1.3.2] - 2026-06-27
134
134
  - **Streaming merger:** A series of optimizations have been implemented at the C++ memory pool level; using a different strategy, these are now freed as soon as possible depending on the source data content and the morphology of the containing polygons. This has allowed for a further reduction in the peak memory indicated in the previous changelog, specifically from 13GB to 4.3GB, on the same image with 20,000,000 polygons.
135
+
136
+ ## [1.3.3] - 2026-06-28
137
+ - **GeoJsonStreamingMerger:** Fixes both Ruby and CPP side.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- contrek (1.3.2)
4
+ contrek (1.3.3)
5
5
  chunky_png (~> 1.4)
6
6
  concurrent-ruby (~> 1.3.5)
7
7
  rice (= 4.5.0)
@@ -10,6 +10,7 @@
10
10
  #include "Tests.h"
11
11
  #include <sys/resource.h>
12
12
  #include <string.h>
13
+ #include <unistd.h>
13
14
  #include <iostream>
14
15
  #include <list>
15
16
  #include <map>
@@ -19,7 +20,6 @@
19
20
  #include <algorithm>
20
21
  #include <cstdio>
21
22
  #include <fstream>
22
- #include <unistd.h>
23
23
 
24
24
  #include "polygon/finder/PolygonFinder.h"
25
25
  #include "polygon/finder/concurrent/ClippedPolygonFinder.h"
@@ -441,7 +441,7 @@ void stream_progressive_png_image(const std::string& filepath, uint32_t stripe_h
441
441
  shared_stream.rdbuf()->pubsetbuf(buffer.data(), buffer.size());
442
442
 
443
443
  SvgStreamingMerger vmerger(0, &varguments, &shared_stream, total_width, total_height);
444
- // GeoJsonStreamingMerger vmerger(0, &varguments, &shared_stream, total_width, total_height, 4294901760);
444
+ // GeoJsonStreamingMerger vmerger(0, &varguments, &shared_stream, 4294901760);
445
445
  try {
446
446
  size_t row_size = static_cast<size_t>(total_width) * 4;
447
447
  int stripe_count = 0;
@@ -20,13 +20,13 @@ class GeoJsonStreamingMerger : public StreamingMerger {
20
20
  protected:
21
21
  void write_header() override {
22
22
  if (stream) {
23
- *stream << "{\"type\":\"FeatureCollection\",\"features\":[\n";
23
+ *stream << "{\"type\":\"FeatureCollection\",\"features\":[";
24
24
  }
25
25
  }
26
26
 
27
27
  void write_footer() override {
28
28
  if (stream) {
29
- *stream << "\n]}";
29
+ *stream << "]}";
30
30
  }
31
31
  }
32
32
  void write_outer_polygon_start() override {}
@@ -38,20 +38,19 @@ class GeoJsonStreamingMerger : public StreamingMerger {
38
38
  GeoJsonStreamingMerger(int number_of_threads,
39
39
  std::vector<std::string>* options,
40
40
  std::ofstream* stream_to,
41
- int total_width, int total_height,
42
41
  unsigned int pixel_value)
43
- : StreamingMerger(number_of_threads, options, stream_to, total_width, total_height),
42
+ : StreamingMerger(number_of_threads, options, stream_to),
44
43
  target_value(pixel_value) {}
45
44
 
46
45
  void stream_raw_polygon(const Shape* shape) override {
47
46
  if (!stream) return;
48
47
  if (!is_first_feature) {
49
- *stream << ",\n";
48
+ *stream << ",";
50
49
  }
51
50
  is_first_feature = false;
52
51
 
53
- *stream << "{\"type\":\"Feature\",\"properties\":{\"value\":" << target_value
54
- << "},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[ [";
52
+ *stream << "{\"type\":\"Feature\",\"properties\":{\"PixelVal\":" << target_value
53
+ << "},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[";
55
54
  const std::vector<Point>& points = shape->outer_polyline->raw();
56
55
  const size_t points_size = points.size();
57
56
  if (points_size > 0) {
@@ -35,8 +35,10 @@ class Part : public Queueable<Point> {
35
35
  bool transmutation_skip = false;
36
36
  bool dead_end = false;
37
37
  bool mirror = false;
38
+
38
39
  private:
39
40
  bool touched_ = false;
41
+
40
42
  public:
41
43
  Part* next = nullptr;
42
44
  Part* next_seam = nullptr;
@@ -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
  };
@@ -97,6 +97,7 @@
97
97
  #include "PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.h"
98
98
  #include "PolygonFinder/src/polygon/finder/concurrent/StreamingMerger.cpp"
99
99
  #include "PolygonFinder/src/polygon/finder/concurrent/SvgStreamingMerger.h"
100
+ #include "PolygonFinder/src/polygon/finder/concurrent/GeoJsonStreamingMerger.h"
100
101
  #include "PolygonFinder/src/polygon/finder/concurrent/ShapePool.h"
101
102
  #include "PolygonFinder/src/polygon/finder/concurrent/ShapePool.cpp"
102
103
  extern "C" {
@@ -309,7 +310,7 @@ struct OfstreamWrapper {
309
310
  std::ofstream& get_stream() { return stream; }
310
311
  };
311
312
 
312
- StreamingMerger* create_streaming_merger(Object self,
313
+ SvgStreamingMerger* create_svg_streaming_merger(Object self,
313
314
  int number_of_threads,
314
315
  std::vector<std::string>* options,
315
316
  Object stream_obj,
@@ -319,6 +320,15 @@ StreamingMerger* create_streaming_merger(Object self,
319
320
  return new SvgStreamingMerger(number_of_threads, options, &wrapper->get_stream(), total_width, total_height);
320
321
  }
321
322
 
323
+ GeoJsonStreamingMerger* create_geo_json_streaming_merger(Object self,
324
+ int number_of_threads,
325
+ std::vector<std::string>* options,
326
+ Object stream_obj,
327
+ int pixel_val) {
328
+ OfstreamWrapper* wrapper = Rice::detail::From_Ruby<OfstreamWrapper*>().convert(stream_obj.value());
329
+ return new GeoJsonStreamingMerger(number_of_threads, options, &wrapper->get_stream(),pixel_val);
330
+ }
331
+
322
332
  OfstreamWrapper* create_ofstream(Object self, std::string path) {
323
333
  return new OfstreamWrapper(path);
324
334
  }
@@ -437,7 +447,11 @@ void Init_cpp_polygon_finder() {
437
447
  ProcessResult pr = Rice::detail::ruby_result_to_process_result(rb_result);
438
448
  self.add_tile(pr, flush);
439
449
  });
440
- rb_cstreaming_merger.define_singleton_method("new", &create_streaming_merger);
450
+
451
+ Data_Type<SvgStreamingMerger> rb_csvgstreaming_merger = define_class<SvgStreamingMerger, StreamingMerger>("CPPSvgStreamingMerger");
452
+ rb_csvgstreaming_merger.define_singleton_method("new", &create_svg_streaming_merger);
453
+ Data_Type<GeoJsonStreamingMerger> rb_cgeojsonstreaming_merger = define_class<GeoJsonStreamingMerger, StreamingMerger>("CPPGeoJsonStreamingMerger");
454
+ rb_cgeojsonstreaming_merger.define_singleton_method("new", &create_geo_json_streaming_merger);
441
455
 
442
456
  Module mContrek = define_module("Contrek");
443
457
  Module mCpp = define_module_under(mContrek, "Cpp");
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Contrek
4
+ module Cpp
5
+ class CPPGeoJsonConcurrentStreamingMerger < CPPGeoJsonStreamingMerger
6
+ def self.new(stream_to:, number_of_threads: 0, options: nil, pixel_val: 0)
7
+ super(number_of_threads, options, stream_to, pixel_val)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Contrek
4
4
  module Cpp
5
- class CPPConcurrentStreamingMerger < CPPStreamingMerger
5
+ class CPPSvgConcurrentStreamingMerger < CPPSvgStreamingMerger
6
6
  def self.new(stream_to:, total_width:, total_height:, number_of_threads: 0, options: nil)
7
7
  super(number_of_threads, options, stream_to, total_width, total_height)
8
8
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Contrek
4
+ module Concurrent
5
+ class GeoJsonStreamingMerger < StreamingMerger
6
+ def initialize(stream_to:, pixel_val:, options: {})
7
+ @pixel_val = pixel_val
8
+ super(stream_to:, options:)
9
+ end
10
+
11
+ def write_header
12
+ @first_feature = true
13
+ @stream.write("{\"type\":\"FeatureCollection\",\"features\":[")
14
+ end
15
+
16
+ def write_footer
17
+ @stream.write("]}")
18
+ end
19
+
20
+ def stream_raw_polygon(shape)
21
+ if @first_feature
22
+ @first_feature = false
23
+ else
24
+ @stream.write(",")
25
+ end
26
+ outer_ring = shape.outer_polyline.raw.map { |p| [p[:y], p[:x]] }
27
+ outer_ring << outer_ring.first if outer_ring.first != outer_ring.last
28
+ polygon_coordinates = [outer_ring]
29
+ shape.inner_polylines.map(&:raw).each do |sequence|
30
+ inner_ring = sequence.map { |p| [p[:y], p[:x]] }
31
+ inner_ring << inner_ring.first if inner_ring.first != inner_ring.last
32
+ polygon_coordinates << inner_ring
33
+ end
34
+ feature_hash = {
35
+ type: "Feature",
36
+ properties: {PixelVal: @pixel_val},
37
+ geometry: {
38
+ type: "Polygon",
39
+ coordinates: polygon_coordinates
40
+ }
41
+ }
42
+ @stream.write(JSON.generate(feature_hash))
43
+ end
44
+ end
45
+ end
46
+ end
@@ -3,10 +3,8 @@
3
3
  module Contrek
4
4
  module Concurrent
5
5
  class StreamingMerger < VerticalMerger
6
- def initialize(stream_to:, total_width:, total_height:, options: {})
6
+ def initialize(stream_to:, options: {})
7
7
  @stream = stream_to
8
- @total_width = total_width
9
- @total_height = total_height
10
8
  @moved = 0
11
9
  super(options: options)
12
10
  end
@@ -48,41 +46,21 @@ module Contrek
48
46
 
49
47
  def stream_raw_polygon(shape)
50
48
  outer_pts = shape.outer_polyline.raw.map { |p| "#{p[:y]},#{p[:x]}" }.join(" ")
51
- @stream.write(svg_outer_polygon(outer_pts))
49
+ write_outer_polygon(outer_pts)
52
50
  shape.inner_polylines.map(&:raw).each do |sequence|
53
51
  inner_pts = sequence.map { |p| "#{p[:y]},#{p[:x]}" }.join(" ")
54
- @stream.write(svg_inner_polygon(inner_pts))
52
+ write_inner_polygon(inner_pts)
55
53
  end
56
54
  end
57
55
 
58
56
  def ensure_header
59
57
  if @stream.pos == 0
60
- @stream.write(svg_header)
58
+ write_header
61
59
  end
62
60
  end
63
61
 
64
62
  def ensure_footer
65
- @stream.write(svg_footer)
66
- end
67
-
68
- def svg_footer
69
- "</svg>"
70
- end
71
-
72
- def svg_header
73
- "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"#{@total_width}\" height=\"#{@total_height}\"><style>#{svg_css}</style>"
74
- end
75
-
76
- def svg_outer_polygon(outer_pts)
77
- "<polygon points=\"#{outer_pts}\" class=\"out\"/>"
78
- end
79
-
80
- def svg_inner_polygon(inner_pts)
81
- "<polygon points=\"#{inner_pts}\" class=\"in\"/>"
82
- end
83
-
84
- def svg_css
85
- ".out{fill:none;stroke:red;stroke-width:1;}.in{fill:none;stroke:green;stroke-width:1;}.out:hover{stroke:yellow;}"
63
+ write_footer
86
64
  end
87
65
  end
88
66
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Contrek
4
+ module Concurrent
5
+ class SvgStreamingMerger < StreamingMerger
6
+ def initialize(stream_to:, total_width:, total_height:, options: {})
7
+ @total_width = total_width
8
+ @total_height = total_height
9
+ super(stream_to:, options:)
10
+ end
11
+
12
+ def write_header
13
+ @stream.write("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"#{@total_width}\" height=\"#{@total_height}\"><style>#{svg_css}</style>")
14
+ end
15
+
16
+ def write_footer
17
+ @stream.write("</svg>")
18
+ end
19
+
20
+ def write_outer_polygon(outer_pts)
21
+ @stream.write("<polygon points=\"#{outer_pts}\" class=\"out\"/>")
22
+ end
23
+
24
+ def write_inner_polygon(inner_pts)
25
+ @stream.write("<polygon points=\"#{inner_pts}\" class=\"in\"/>")
26
+ end
27
+
28
+ private
29
+
30
+ def svg_css
31
+ ".out{fill:none;stroke:red;stroke-width:1;}.in{fill:none;stroke:green;stroke-width:1;}.out:hover{stroke:yellow;}"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Contrek
4
- VERSION = "1.3.2"
4
+ VERSION = "1.3.3"
5
5
  end
data/lib/contrek.rb CHANGED
@@ -40,6 +40,8 @@ require "contrek/finder/concurrent/merger"
40
40
  require "contrek/finder/concurrent/horizontal_merger"
41
41
  require "contrek/finder/concurrent/vertical_merger"
42
42
  require "contrek/finder/concurrent/streaming_merger"
43
+ require "contrek/finder/concurrent/svg_streaming_merger"
44
+ require "contrek/finder/concurrent/geo_json_streaming_merger"
43
45
  require "contrek/finder/concurrent/cursor"
44
46
  require "contrek/map/mercator_projection"
45
47
  require "contrek/matchers/matcher"
@@ -54,7 +56,8 @@ require "contrek/cpp/cpp_concurrent_finder"
54
56
  require "contrek/cpp/cpp_concurrent_merger"
55
57
  require "contrek/cpp/cpp_concurrent_horizontal_merger"
56
58
  require "contrek/cpp/cpp_concurrent_vertical_merger"
57
- require "contrek/cpp/cpp_concurrent_streaming_merger"
59
+ require "contrek/cpp/cpp_svg_concurrent_streaming_merger"
60
+ require "contrek/cpp/cpp_geo_json_concurrent_streaming_merger"
58
61
  require "contrek/cpp/cpp_tempfile"
59
62
  require "contrek/cpp/cpp_result"
60
63
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contrek
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.2
4
+ version: 1.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emanuele Cesaroni
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-27 00:00:00.000000000 Z
11
+ date: 2026-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -257,9 +257,10 @@ files:
257
257
  - lib/contrek/cpp/cpp_concurrent_finder.rb
258
258
  - lib/contrek/cpp/cpp_concurrent_horizontal_merger.rb
259
259
  - lib/contrek/cpp/cpp_concurrent_merger.rb
260
- - lib/contrek/cpp/cpp_concurrent_streaming_merger.rb
261
260
  - lib/contrek/cpp/cpp_concurrent_vertical_merger.rb
261
+ - lib/contrek/cpp/cpp_geo_json_concurrent_streaming_merger.rb
262
262
  - lib/contrek/cpp/cpp_result.rb
263
+ - lib/contrek/cpp/cpp_svg_concurrent_streaming_merger.rb
263
264
  - lib/contrek/cpp/cpp_tempfile.rb
264
265
  - lib/contrek/finder/bounds.rb
265
266
  - lib/contrek/finder/concurrent/clipped_polygon_finder.rb
@@ -268,6 +269,7 @@ files:
268
269
  - lib/contrek/finder/concurrent/end_point.rb
269
270
  - lib/contrek/finder/concurrent/fake_cluster.rb
270
271
  - lib/contrek/finder/concurrent/finder.rb
272
+ - lib/contrek/finder/concurrent/geo_json_streaming_merger.rb
271
273
  - lib/contrek/finder/concurrent/horizontal_merger.rb
272
274
  - lib/contrek/finder/concurrent/hub.rb
273
275
  - lib/contrek/finder/concurrent/inner_polyline.rb
@@ -282,6 +284,7 @@ files:
282
284
  - lib/contrek/finder/concurrent/sequence.rb
283
285
  - lib/contrek/finder/concurrent/shape.rb
284
286
  - lib/contrek/finder/concurrent/streaming_merger.rb
287
+ - lib/contrek/finder/concurrent/svg_streaming_merger.rb
285
288
  - lib/contrek/finder/concurrent/tile.rb
286
289
  - lib/contrek/finder/concurrent/vertical_merger.rb
287
290
  - lib/contrek/finder/list.rb