@@ -299,11 +299,13 @@ def draw_agents(
299
299
"x:Q" ,
300
300
title = xlabel ,
301
301
scale = alt .Scale (type = "linear" , domain = [xmin , xmax ]),
302
+ axis = None ,
302
303
),
303
304
y = alt .Y (
304
305
"y:Q" ,
305
306
title = ylabel ,
306
307
scale = alt .Scale (type = "linear" , domain = [ymin , ymax ]),
308
+ axis = None ,
307
309
),
308
310
size = alt .Size ("size:Q" , legend = None , scale = alt .Scale (domain = [0 , 50 ])),
309
311
shape = alt .Shape (
@@ -352,8 +354,7 @@ def draw_propertylayer(
352
354
Returns:
353
355
alt.Chart: A tuple containing the base chart and the color bar chart.
354
356
"""
355
- base = None
356
- bar_chart_viz = None
357
+ main_charts = []
357
358
358
359
for layer_name in property_layers :
359
360
if layer_name == "empty" :
@@ -384,7 +385,6 @@ def draw_propertylayer(
384
385
vmin = portrayal .vmin if portrayal .vmin is not None else np .min (data )
385
386
vmax = portrayal .vmax if portrayal .vmax is not None else np .max (data )
386
387
387
- # Prepare data for Altair
388
388
df = pd .DataFrame (
389
389
{
390
390
"x" : np .repeat (np .arange (data .shape [0 ]), data .shape [1 ]),
@@ -393,183 +393,48 @@ def draw_propertylayer(
393
393
}
394
394
)
395
395
396
- current_chart = None
397
396
if color :
398
- # Create a function to map values to RGBA colors with proper opacity scaling
399
- def apply_rgba (
400
- val , v_min = vmin , v_max = vmax , a = alpha , p_color = portrayal .color
401
- ):
402
- # Normalize value to range [0,1] and clamp
403
- normalized = max (
404
- 0 ,
405
- min (
406
- ((val - v_min ) / (v_max - v_min ))
407
- if (v_max - v_min ) != 0
408
- else 0.5 ,
409
- 1 ,
410
- ),
411
- )
412
-
413
- # Scale opacity by alpha parameter
414
- opacity = normalized * a
415
-
416
- # Convert color to RGB components
417
- rgb_color_val = to_rgb (p_color )
418
- r = int (rgb_color_val [0 ] * 255 )
419
- g = int (rgb_color_val [1 ] * 255 )
420
- b = int (rgb_color_val [2 ] * 255 )
421
- return f"rgba({ r } , { g } , { b } , { opacity :.2f} )"
422
-
423
- # Apply color mapping to each value in the dataset
424
- df ["color_str" ] = df ["value" ].apply (apply_rgba )
425
-
426
- # Create chart for the property layer
427
- current_chart = (
428
- alt .Chart (df )
429
- .mark_rect ()
430
- .encode (
431
- x = alt .X ("x:O" , axis = None ),
432
- y = alt .Y ("y:O" , axis = None ),
433
- fill = alt .Fill ("color_str:N" , scale = None ),
434
- )
435
- .properties (
436
- width = chart_width , height = chart_height , title = layer_name
437
- )
397
+ # For a single color gradient, we define the range from transparent to solid.
398
+ rgb = to_rgb (color )
399
+ r , g , b = (int (c * 255 ) for c in rgb )
400
+
401
+ min_color = f"rgba({ r } ,{ g } ,{ b } ,0)"
402
+ max_color = f"rgba({ r } ,{ g } ,{ b } ,{ alpha } )"
403
+ opacity = 1
404
+ color_scale = alt .Scale (
405
+ range = [min_color , max_color ], domain = [vmin , vmax ]
438
406
)
439
- base = (
440
- alt .layer (current_chart , base )
441
- if base is not None
442
- else current_chart
443
- )
444
-
445
- # Add colorbar if specified in portrayal
446
- if portrayal .colorbar :
447
- # Extract RGB components from base color
448
- rgb_color_val = to_rgb (portrayal .color )
449
- r_int = int (rgb_color_val [0 ] * 255 )
450
- g_int = int (rgb_color_val [1 ] * 255 )
451
- b_int = int (rgb_color_val [2 ] * 255 )
452
-
453
- # Define gradient endpoints
454
- min_color_str = f"rgba({ r_int } ,{ g_int } ,{ b_int } ,0)"
455
- max_color_str = f"rgba({ r_int } ,{ g_int } ,{ b_int } ,{ alpha :.2f} )"
456
-
457
- # Define colorbar dimensions
458
- colorbar_height = 20
459
- colorbar_width = chart_width
460
-
461
- # Create dataframe for gradient visualization
462
- df_gradient = pd .DataFrame ({"x_grad" : [0 , 1 ], "y_grad" : [0 , 1 ]})
463
-
464
- # Create evenly distributed tick values
465
- axis_values = np .linspace (vmin , vmax , 11 )
466
- tick_positions = np .linspace (10 , colorbar_width - 10 , 11 )
467
-
468
- # Prepare data for axis and labels
469
- axis_data = pd .DataFrame (
470
- {"value_axis" : axis_values , "x_axis" : tick_positions }
471
- )
472
-
473
- # Create colorbar with linear gradient
474
- colorbar_chart_obj = (
475
- alt .Chart (df_gradient )
476
- .mark_rect (
477
- x = 20 ,
478
- y = 0 ,
479
- width = colorbar_width - 20 ,
480
- height = colorbar_height ,
481
- color = alt .Gradient (
482
- gradient = "linear" ,
483
- stops = [
484
- alt .GradientStop (color = min_color_str , offset = 0 ),
485
- alt .GradientStop (color = max_color_str , offset = 1 ),
486
- ],
487
- x1 = 0 ,
488
- x2 = 1 , # Horizontal gradient
489
- y1 = 0 ,
490
- y2 = 0 , # Keep y constant
491
- ),
492
- )
493
- .encode (
494
- x = alt .value (chart_width / 2 ), y = alt .value (8 )
495
- ) # Center colorbar
496
- .properties (width = colorbar_width , height = colorbar_height )
497
- )
498
- # Add tick marks to colorbar
499
- axis_chart = (
500
- alt .Chart (axis_data )
501
- .mark_tick (thickness = 2 , size = 10 )
502
- .encode (
503
- x = alt .X ("x_axis:Q" , axis = None ),
504
- y = alt .value (colorbar_height - 2 ),
505
- )
506
- )
507
- # Add value labels below tick marks
508
- text_labels = (
509
- alt .Chart (axis_data )
510
- .mark_text (baseline = "top" , fontSize = 10 , dy = 0 )
511
- .encode (
512
- x = alt .X ("x_axis:Q" ),
513
- text = alt .Text ("value_axis:Q" , format = ".1f" ),
514
- y = alt .value (colorbar_height + 10 ),
515
- )
516
- )
517
- # Add title to colorbar
518
- title_chart = (
519
- alt .Chart (pd .DataFrame ([{"text_title" : layer_name }]))
520
- .mark_text (
521
- fontSize = 12 ,
522
- fontWeight = "bold" ,
523
- baseline = "bottom" ,
524
- align = "center" ,
525
- )
526
- .encode (
527
- text = "text_title:N" ,
528
- x = alt .value (colorbar_width / 2 ),
529
- y = alt .value (colorbar_height + 40 ),
530
- )
531
- )
532
- # Combine all colorbar components
533
- combined_colorbar = alt .layer (
534
- colorbar_chart_obj , axis_chart , text_labels , title_chart
535
- ).properties (width = colorbar_width , height = colorbar_height + 50 )
536
-
537
- bar_chart_viz = (
538
- alt .vconcat (bar_chart_viz , combined_colorbar ).resolve_scale (
539
- color = "independent"
540
- )
541
- if bar_chart_viz is not None
542
- else combined_colorbar
543
- )
544
407
545
408
elif colormap :
546
409
cmap = colormap
547
- cmap_scale = alt .Scale (scheme = cmap , domain = [vmin , vmax ])
548
-
549
- current_chart = (
550
- alt .Chart (df )
551
- .mark_rect (opacity = alpha )
552
- .encode (
553
- x = alt .X ("x:O" , axis = None ),
554
- y = alt .Y ("y:O" , axis = None ),
555
- color = alt .Color (
556
- "value:Q" ,
557
- scale = cmap_scale ,
558
- title = layer_name ,
559
- legend = alt .Legend (title = layer_name )
560
- if portrayal .colorbar
561
- else None ,
562
- ),
563
- )
564
- .properties (width = chart_width , height = chart_height )
565
- )
566
- base = (
567
- alt .layer (current_chart , base )
568
- if base is not None
569
- else current_chart
570
- )
410
+ color_scale = alt .Scale (scheme = cmap , domain = [vmin , vmax ])
411
+ opacity = alpha
412
+
571
413
else :
572
414
raise ValueError (
573
415
f"PropertyLayer { layer_name } portrayal must include 'color' or 'colormap'."
574
416
)
575
- return (base , bar_chart_viz )
417
+
418
+ current_chart = (
419
+ alt .Chart (df )
420
+ .mark_rect (opacity = opacity )
421
+ .encode (
422
+ x = alt .X ("x:O" , axis = None ),
423
+ y = alt .Y ("y:O" , axis = None ),
424
+ color = alt .Color (
425
+ "value:Q" ,
426
+ scale = color_scale ,
427
+ title = layer_name ,
428
+ legend = alt .Legend (title = layer_name , orient = "bottom" )
429
+ if portrayal .colorbar
430
+ else None ,
431
+ ),
432
+ )
433
+ .properties (width = chart_width , height = chart_height )
434
+ )
435
+
436
+ if current_chart is not None :
437
+ main_charts .append (current_chart )
438
+
439
+ base = alt .layer (* main_charts ).resolve_scale (color = "independent" )
440
+ return base
0 commit comments