@@ -3,6 +3,7 @@ use std::path::Path;
33use std:: str:: FromStr ;
44
55use clap:: Parser as CliParser ;
6+ use egui:: Color32 ;
67use log:: error;
78use mapvas:: map:: coordinates:: PixelCoordinate ;
89use mapvas:: map:: geometry_collection:: { Geometry , Metadata } ;
@@ -16,6 +17,29 @@ use std::io::{BufRead, BufReader, Read};
1617
1718mod sender;
1819
20+ #[ derive( Debug , Clone ) ]
21+ struct FileArg {
22+ path : std:: path:: PathBuf ,
23+ heatmap : bool ,
24+ }
25+
26+ impl std:: str:: FromStr for FileArg {
27+ type Err = std:: convert:: Infallible ;
28+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
29+ if let Some ( path) = s. strip_suffix ( ":heatmap" ) {
30+ Ok ( Self {
31+ path : path. into ( ) ,
32+ heatmap : true ,
33+ } )
34+ } else {
35+ Ok ( Self {
36+ path : s. into ( ) ,
37+ heatmap : false ,
38+ } )
39+ }
40+ }
41+ }
42+
1943/// Walk a geometry tree and append every coordinate it contains to `out`.
2044fn collect_coords ( geometry : & Geometry < PixelCoordinate > , out : & mut Vec < PixelCoordinate > ) {
2145 match geometry {
@@ -107,6 +131,10 @@ struct Args {
107131 #[ arg( short, long) ]
108132 output : Option < std:: path:: PathBuf > ,
109133
134+ /// Render without map background (black background, only geometries). Only with --output.
135+ #[ arg( long) ]
136+ no_map : bool ,
137+
110138 /// Image width in pixels (only with --output)
111139 #[ arg( long, default_value_t = 1980 ) ]
112140 width : u32 ,
@@ -116,7 +144,8 @@ struct Args {
116144 height : u32 ,
117145
118146 /// Files to parse. stdin is used if not provided.
119- files : Vec < std:: path:: PathBuf > ,
147+ /// Append `:heatmap` to a file to render it as a heatmap (e.g. `points.geojson:heatmap`).
148+ files : Vec < FileArg > ,
120149}
121150
122151fn render_output ( args : & Args ) {
@@ -128,7 +157,13 @@ fn render_output(args: &Args) {
128157 let config = Config :: new ( ) ;
129158 init_style_config ( config. vector_style_file . as_deref ( ) ) ;
130159
131- let renderer = HeadlessRenderer :: with_config ( config) ;
160+ let renderer = if args. no_map {
161+ HeadlessRenderer :: with_config ( config)
162+ . without_map ( )
163+ . with_background_color ( Color32 :: BLACK )
164+ } else {
165+ HeadlessRenderer :: with_config ( config)
166+ } ;
132167
133168 let mut events: Vec < MapEvent > = Vec :: new ( ) ;
134169 if args. files . is_empty ( ) {
@@ -143,15 +178,20 @@ fn render_output(args: &Args) {
143178 . with_invert_coordinates ( args. invert_coordinates ) ;
144179 events. extend ( content_parser. parse ( ) ) ;
145180 } else {
146- for path in & args. files {
147- let mut parser = AutoFileParser :: new ( path)
181+ for file_arg in & args. files {
182+ let mut parser = AutoFileParser :: new ( & file_arg . path )
148183 . with_label_pattern ( & args. label_pattern )
149184 . with_invert_coordinates ( args. invert_coordinates ) ;
150- events. extend ( parser. parse ( ) ) ;
185+ let file_events: Vec < MapEvent > = parser. parse ( ) . collect ( ) ;
186+ if args. heatmap || file_arg. heatmap {
187+ events. extend ( convert_to_heatmap ( file_events) ) ;
188+ } else {
189+ events. extend ( file_events) ;
190+ }
151191 }
152192 }
153193
154- if args. heatmap {
194+ if args. files . is_empty ( ) && args . heatmap {
155195 events = convert_to_heatmap ( events) ;
156196 }
157197
@@ -173,16 +213,16 @@ fn render_output(args: &Args) {
173213 ) ;
174214}
175215
176- fn readers ( paths : & [ std :: path :: PathBuf ] ) -> Vec < Box < dyn BufRead > > {
177- let mut res: Vec < Box < dyn BufRead > > = Vec :: new ( ) ;
178- if paths . is_empty ( ) {
179- res. push ( Box :: new ( std:: io:: stdin ( ) . lock ( ) ) ) ;
216+ fn readers ( files : & [ FileArg ] ) -> Vec < ( Box < dyn BufRead > , bool ) > {
217+ let mut res: Vec < ( Box < dyn BufRead > , bool ) > = Vec :: new ( ) ;
218+ if files . is_empty ( ) {
219+ res. push ( ( Box :: new ( std:: io:: stdin ( ) . lock ( ) ) , false ) ) ;
180220 } else {
181- for f in paths {
182- match File :: open ( f ) {
183- Ok ( file) => res. push ( Box :: new ( BufReader :: new ( file) ) ) ,
221+ for f in files {
222+ match File :: open ( & f . path ) {
223+ Ok ( file) => res. push ( ( Box :: new ( BufReader :: new ( file) ) , f . heatmap ) ) ,
184224 Err ( e) => {
185- eprintln ! ( "Error: Failed to open file '{}': {}" , f. display( ) , e) ;
225+ eprintln ! ( "Error: Failed to open file '{}': {}" , f. path . display( ) , e) ;
186226 std:: process:: exit ( 1 ) ;
187227 }
188228 }
@@ -215,15 +255,8 @@ async fn main() {
215255 let ( sender, _) = sender:: MapSender :: new ( ) . await ;
216256
217257 let send_events = |events : Box < dyn Iterator < Item = MapEvent > > | {
218- if args. heatmap {
219- let collected: Vec < MapEvent > = events. collect ( ) ;
220- for event in convert_to_heatmap ( collected) {
221- sender. send_event ( event) ;
222- }
223- } else {
224- for event in events {
225- sender. send_event ( event) ;
226- }
258+ for event in events {
259+ sender. send_event ( event) ;
227260 }
228261 } ;
229262
@@ -240,15 +273,24 @@ async fn main() {
240273 let content_parser = mapvas:: parser:: ContentAutoParser :: new ( content)
241274 . with_label_pattern ( & args. label_pattern )
242275 . with_invert_coordinates ( args. invert_coordinates ) ;
243- send_events ( Box :: new ( content_parser. parse ( ) . into_iter ( ) ) ) ;
276+ let events: Vec < MapEvent > = content_parser. parse ( ) ;
277+ if args. heatmap {
278+ send_events ( Box :: new ( convert_to_heatmap ( events) . into_iter ( ) ) ) ;
279+ } else {
280+ send_events ( Box :: new ( events. into_iter ( ) ) ) ;
281+ }
244282 } else {
245283 // Use file-based auto-parser for each file
246- for file_path in & args. files {
247- let mut auto_parser = AutoFileParser :: new ( file_path )
284+ for file_arg in & args. files {
285+ let mut auto_parser = AutoFileParser :: new ( & file_arg . path )
248286 . with_label_pattern ( & args. label_pattern )
249287 . with_invert_coordinates ( args. invert_coordinates ) ;
250288 let events: Vec < MapEvent > = auto_parser. parse ( ) . collect ( ) ;
251- send_events ( Box :: new ( events. into_iter ( ) ) ) ;
289+ if args. heatmap || file_arg. heatmap {
290+ send_events ( Box :: new ( convert_to_heatmap ( events) . into_iter ( ) ) ) ;
291+ } else {
292+ send_events ( Box :: new ( events. into_iter ( ) ) ) ;
293+ }
252294 }
253295 }
254296 } else {
@@ -273,10 +315,14 @@ async fn main() {
273315 } ;
274316
275317 let readers = readers ( & args. files ) ;
276- for reader in readers {
318+ for ( reader, file_heatmap ) in readers {
277319 let mut parser = parser ( ) ;
278320 let events: Vec < MapEvent > = parser. parse ( reader) . collect ( ) ;
279- send_events ( Box :: new ( events. into_iter ( ) ) ) ;
321+ if args. heatmap || file_heatmap {
322+ send_events ( Box :: new ( convert_to_heatmap ( events) . into_iter ( ) ) ) ;
323+ } else {
324+ send_events ( Box :: new ( events. into_iter ( ) ) ) ;
325+ }
280326 }
281327 }
282328
0 commit comments