@@ -16,21 +16,26 @@ use crate::v0_6_1::ser::{
1616/// Collects features in two phases:
1717/// 1. `add_feature()` flattens each feature's properties and stores them with geometry.
1818/// 2. After sorting `keys()`, call `write_features()` with an `FgbWriter` to emit all features.
19+ ///
20+ /// プロパティのキーは layer 全体で一度だけ heap に持ち、各 feature は
21+ /// `(column_idx, value)` の疎なリストとして保持する。これにより同一キーの
22+ /// 文字列が feature ごとに複製されず、メモリ消費を抑えられる。
1923pub struct LayerSerializer {
20- /// Union of all keys seen across all features, in insertion order.
21- all_keys : Vec < String > ,
22- key_set : std :: collections :: BTreeSet < String > ,
23- /// Per-feature geometry.
24+ column_idx : std :: collections :: HashMap < String , usize > ,
25+ columns : Vec < String > ,
26+ /// 各列の型。各列で最初に登場した値の型を採用する。
27+ column_types : Vec < flatgeobuf :: ColumnType > ,
2428 geometries : Vec < geo_types:: Geometry < f64 > > ,
25- /// Per-feature flattened properties.
26- features : Vec < FlatProperties > ,
29+ /// Per-feature sparse properties: `(column_idx, value)` のリスト。
30+ features : Vec < Vec < ( usize , FieldValue < ' static > ) > > ,
2731}
2832
2933impl LayerSerializer {
3034 pub fn new ( ) -> Self {
3135 LayerSerializer {
32- all_keys : Vec :: new ( ) ,
33- key_set : std:: collections:: BTreeSet :: new ( ) ,
36+ column_idx : std:: collections:: HashMap :: new ( ) ,
37+ columns : Vec :: new ( ) ,
38+ column_types : Vec :: new ( ) ,
3439 geometries : Vec :: new ( ) ,
3540 features : Vec :: new ( ) ,
3641 }
@@ -48,55 +53,72 @@ impl LayerSerializer {
4853 geometry : impl Into < geo_types:: Geometry < f64 > > ,
4954 properties : impl serde:: Serialize ,
5055 ) {
51- let flat = FlatProperties :: flatten ( properties) . unwrap ( ) ;
52- for key in flat. keys ( ) {
53- if self . key_set . insert ( key. to_owned ( ) ) {
54- self . all_keys . push ( key. to_owned ( ) ) ;
55- }
56- }
56+ let entries = FlatProperties :: flatten ( properties) . unwrap ( ) . into_entries ( ) ;
57+ let feat_vals = entries
58+ . into_iter ( )
59+ . map ( |( key, value) | {
60+ let idx = self
61+ . column_idx
62+ . get ( key. as_ref ( ) )
63+ . copied ( )
64+ . unwrap_or_else ( || {
65+ let i = self . columns . len ( ) ;
66+ let owned = key. into_owned ( ) ;
67+ self . columns . push ( owned. clone ( ) ) ;
68+ self . column_types
69+ . push ( crate :: v0_6_1:: fgb:: ser:: prop:: to_column_type ( & value) ) ;
70+ self . column_idx . insert ( owned, i) ;
71+ i
72+ } ) ;
73+ ( idx, value)
74+ } )
75+ . collect ( ) ;
5776 self . geometries . push ( geometry. into ( ) ) ;
58- self . features . push ( flat ) ;
77+ self . features . push ( feat_vals ) ;
5978 }
6079
6180 /// Returns an iterator over the union of all flattened keys.
6281 pub fn keys ( & self ) -> impl Iterator < Item = & str > {
63- self . all_keys . iter ( ) . map ( |s| s. as_str ( ) )
82+ self . columns . iter ( ) . map ( |s| s. as_str ( ) )
6483 }
6584
6685 /// Set the column order to use when writing features.
86+ ///
87+ /// `columns` には既存の全列を含めること(順序入れ替えのみ可、列の追加・削除は不可)。
6788 pub fn set_columns ( & mut self , columns : Vec < String > ) {
68- self . all_keys = columns;
89+ let new_idx: std:: collections:: HashMap < String , usize > =
90+ columns. iter ( ) . cloned ( ) . zip ( 0 ..) . collect ( ) ;
91+ let remap: Vec < usize > = self . columns . iter ( ) . map ( |k| new_idx[ k] ) . collect ( ) ;
92+ let mut new_types = vec ! [ flatgeobuf:: ColumnType :: String ; columns. len( ) ] ;
93+ for ( old, & ty) in self . column_types . iter ( ) . enumerate ( ) {
94+ new_types[ remap[ old] ] = ty;
95+ }
96+ for feat in & mut self . features {
97+ for ( idx, _) in feat {
98+ * idx = remap[ * idx] ;
99+ }
100+ }
101+ self . columns = columns;
102+ self . column_idx = new_idx;
103+ self . column_types = new_types;
69104 }
70105
71106 /// Write all collected features to the FgbWriter using the current key order.
72107 ///
73- /// `FgbWriter::property` は連番idxでしか列を auto-declare しない仕様のため、
74- /// 全 feature を書く前に `add_column` で列を宣言してしまう。型は各 key の
75- /// 全 feature 中で最初に現れた値の型を採用し、以降の衝突は無視する。
76- /// 全 feature で値が無い列は String として宣言(実際には誰も書かないので無害)。
108+ /// `FgbWriter::property` は連番 idx でしか列を auto-declare しない仕様のため、
109+ /// 全 feature を書く前に `add_column` で列を宣言してしまう。
77110 pub fn write_features ( self , writer : & mut FgbWriter < ' _ > ) -> Result < ( ) , Error > {
78- for key in & self . all_keys {
79- let col_type = self
80- . features
81- . iter ( )
82- . find_map ( |f| f. get ( key) . map ( crate :: v0_6_1:: fgb:: ser:: prop:: to_column_type) )
83- . unwrap_or ( flatgeobuf:: ColumnType :: String ) ;
84- writer. add_column ( key, col_type, |_, _| { } ) ;
111+ for ( key, & ty) in self . columns . iter ( ) . zip ( self . column_types . iter ( ) ) {
112+ writer. add_column ( key, ty, |_, _| { } ) ;
85113 }
86-
87- for i in 0 ..self . features . len ( ) {
88- process_geometry ( & self . geometries [ i] , writer) ?;
89- let feat = & self . features [ i] ;
90- for ( idx, key) in self . all_keys . iter ( ) . enumerate ( ) {
91- let column_value = match feat. get ( key) {
92- Some ( value) => crate :: v0_6_1:: fgb:: ser:: prop:: to_column_value ( value) ,
93- None => continue ,
94- } ;
114+ for ( feat, geom) in self . features . iter ( ) . zip ( self . geometries . iter ( ) ) {
115+ process_geometry ( geom, writer) ?;
116+ for ( idx, val) in feat {
95117 flatgeobuf:: geozero:: PropertyProcessor :: property (
96118 writer,
97- idx,
98- key . as_ref ( ) ,
99- & column_value ,
119+ * idx,
120+ self . columns [ * idx ] . as_ref ( ) ,
121+ & crate :: v0_6_1 :: fgb :: ser :: prop :: to_column_value ( val ) ,
100122 ) ?;
101123 }
102124 flatgeobuf:: geozero:: FeatureProcessor :: feature_end ( writer, 0 ) ?;
0 commit comments