@@ -235,6 +235,20 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,
235
235
# override graticule labels provided by sf::st_graticule() if necessary
236
236
graticule <- self $ fixup_graticule_labels(graticule , scale_x , scale_y , params )
237
237
238
+ # Convert graticule to viewscales for axis guides
239
+ viewscales <- Map(
240
+ view_scales_from_graticule ,
241
+ scale = list (x = scale_x , y = scale_y , x.sec = scale_x , y.sec = scale_y ),
242
+ aesthetic = c(" x" , " y" , " x.sec" , " y.sec" ),
243
+ label = self $ label_axes [c(" bottom" , " left" , " top" , " right" )],
244
+ MoreArgs = list (
245
+ graticule = graticule ,
246
+ bbox = bbox ,
247
+ label_graticule = self $ label_graticule
248
+ )
249
+ )
250
+
251
+ # Rescale graticule for panel grid
238
252
sf :: st_geometry(graticule ) <- sf_rescale01(sf :: st_geometry(graticule ), x_range , y_range )
239
253
graticule $ x_start <- sf_rescale01_x(graticule $ x_start , x_range )
240
254
graticule $ x_end <- sf_rescale01_x(graticule $ x_end , x_range )
@@ -247,11 +261,15 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,
247
261
graticule = graticule ,
248
262
crs = params $ crs ,
249
263
default_crs = params $ default_crs ,
250
- label_axes = self $ label_axes ,
251
- label_graticule = self $ label_graticule
264
+ viewscales = viewscales
252
265
)
253
266
},
254
267
268
+ setup_panel_guides = function (self , panel_params , guides , params = list ()) {
269
+ params <- Coord $ setup_panel_guides(panel_params $ viewscales , guides , params )
270
+ c(params , panel_params )
271
+ },
272
+
255
273
backtransform_range = function (self , panel_params ) {
256
274
target_crs <- panel_params $ default_crs
257
275
source_crs <- panel_params $ crs
@@ -314,162 +332,6 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,
314
332
)
315
333
}
316
334
ggname(" grill" , inject(grobTree(!!! grobs )))
317
- },
318
-
319
- render_axis_h = function (self , panel_params , theme ) {
320
- graticule <- panel_params $ graticule
321
-
322
- # top axis
323
- id1 <- id2 <- integer(0 )
324
- # labels based on panel side
325
- id1 <- c(id1 , which(graticule $ type == panel_params $ label_axes $ top & graticule $ y_start > 0.999 ))
326
- id2 <- c(id2 , which(graticule $ type == panel_params $ label_axes $ top & graticule $ y_end > 0.999 ))
327
-
328
- # labels based on graticule direction
329
- if (" S" %in% panel_params $ label_graticule ) {
330
- id1 <- c(id1 , which(graticule $ type == " E" & graticule $ y_start > 0.999 ))
331
- }
332
- if (" N" %in% panel_params $ label_graticule ) {
333
- id2 <- c(id2 , which(graticule $ type == " E" & graticule $ y_end > 0.999 ))
334
- }
335
- if (" W" %in% panel_params $ label_graticule ) {
336
- id1 <- c(id1 , which(graticule $ type == " N" & graticule $ y_start > 0.999 ))
337
- }
338
- if (" E" %in% panel_params $ label_graticule ) {
339
- id2 <- c(id2 , which(graticule $ type == " N" & graticule $ y_end > 0.999 ))
340
- }
341
-
342
- ticks1 <- graticule [unique0(id1 ), ]
343
- ticks2 <- graticule [unique0(id2 ), ]
344
- tick_positions <- c(ticks1 $ x_start , ticks2 $ x_end )
345
- tick_labels <- c(ticks1 $ degree_label , ticks2 $ degree_label )
346
-
347
- if (length(tick_positions ) > 0 ) {
348
- top <- draw_axis(
349
- tick_positions ,
350
- tick_labels ,
351
- axis_position = " top" ,
352
- theme = theme
353
- )
354
- } else {
355
- top <- zeroGrob()
356
- }
357
-
358
- # bottom axis
359
- id1 <- id2 <- integer(0 )
360
- # labels based on panel side
361
- id1 <- c(id1 , which(graticule $ type == panel_params $ label_axes $ bottom & graticule $ y_start < 0.001 ))
362
- id2 <- c(id2 , which(graticule $ type == panel_params $ label_axes $ bottom & graticule $ y_end < 0.001 ))
363
-
364
- # labels based on graticule direction
365
- if (" S" %in% panel_params $ label_graticule ) {
366
- id1 <- c(id1 , which(graticule $ type == " E" & graticule $ y_start < 0.001 ))
367
- }
368
- if (" N" %in% panel_params $ label_graticule ) {
369
- id2 <- c(id2 , which(graticule $ type == " E" & graticule $ y_end < 0.001 ))
370
- }
371
- if (" W" %in% panel_params $ label_graticule ) {
372
- id1 <- c(id1 , which(graticule $ type == " N" & graticule $ y_start < 0.001 ))
373
- }
374
- if (" E" %in% panel_params $ label_graticule ) {
375
- id2 <- c(id2 , which(graticule $ type == " N" & graticule $ y_end < 0.001 ))
376
- }
377
-
378
- ticks1 <- graticule [unique0(id1 ), ]
379
- ticks2 <- graticule [unique0(id2 ), ]
380
- tick_positions <- c(ticks1 $ x_start , ticks2 $ x_end )
381
- tick_labels <- c(ticks1 $ degree_label , ticks2 $ degree_label )
382
-
383
- if (length(tick_positions ) > 0 ) {
384
- bottom <- draw_axis(
385
- tick_positions ,
386
- tick_labels ,
387
- axis_position = " bottom" ,
388
- theme = theme
389
- )
390
- } else {
391
- bottom <- zeroGrob()
392
- }
393
-
394
- list (top = top , bottom = bottom )
395
- },
396
-
397
- render_axis_v = function (self , panel_params , theme ) {
398
- graticule <- panel_params $ graticule
399
-
400
- # right axis
401
- id1 <- id2 <- integer(0 )
402
- # labels based on panel side
403
- id1 <- c(id1 , which(graticule $ type == panel_params $ label_axes $ right & graticule $ x_end > 0.999 ))
404
- id2 <- c(id2 , which(graticule $ type == panel_params $ label_axes $ right & graticule $ x_start > 0.999 ))
405
-
406
- # labels based on graticule direction
407
- if (" N" %in% panel_params $ label_graticule ) {
408
- id1 <- c(id1 , which(graticule $ type == " E" & graticule $ x_end > 0.999 ))
409
- }
410
- if (" S" %in% panel_params $ label_graticule ) {
411
- id2 <- c(id2 , which(graticule $ type == " E" & graticule $ x_start > 0.999 ))
412
- }
413
- if (" E" %in% panel_params $ label_graticule ) {
414
- id1 <- c(id1 , which(graticule $ type == " N" & graticule $ x_end > 0.999 ))
415
- }
416
- if (" W" %in% panel_params $ label_graticule ) {
417
- id2 <- c(id2 , which(graticule $ type == " N" & graticule $ x_start > 0.999 ))
418
- }
419
-
420
- ticks1 <- graticule [unique0(id1 ), ]
421
- ticks2 <- graticule [unique0(id2 ), ]
422
- tick_positions <- c(ticks1 $ y_end , ticks2 $ y_start )
423
- tick_labels <- c(ticks1 $ degree_label , ticks2 $ degree_label )
424
-
425
- if (length(tick_positions ) > 0 ) {
426
- right <- draw_axis(
427
- tick_positions ,
428
- tick_labels ,
429
- axis_position = " right" ,
430
- theme = theme
431
- )
432
- } else {
433
- right <- zeroGrob()
434
- }
435
-
436
- # left axis
437
- id1 <- id2 <- integer(0 )
438
- # labels based on panel side
439
- id1 <- c(id1 , which(graticule $ type == panel_params $ label_axes $ left & graticule $ x_end < 0.001 ))
440
- id2 <- c(id2 , which(graticule $ type == panel_params $ label_axes $ left & graticule $ x_start < 0.001 ))
441
-
442
- # labels based on graticule direction
443
- if (" N" %in% panel_params $ label_graticule ) {
444
- id1 <- c(id1 , which(graticule $ type == " E" & graticule $ x_end < 0.001 ))
445
- }
446
- if (" S" %in% panel_params $ label_graticule ) {
447
- id2 <- c(id2 , which(graticule $ type == " E" & graticule $ x_start < 0.001 ))
448
- }
449
- if (" E" %in% panel_params $ label_graticule ) {
450
- id1 <- c(id1 , which(graticule $ type == " N" & graticule $ x_end < 0.001 ))
451
- }
452
- if (" W" %in% panel_params $ label_graticule ) {
453
- id2 <- c(id2 , which(graticule $ type == " N" & graticule $ x_start < 0.001 ))
454
- }
455
-
456
- ticks1 <- graticule [unique0(id1 ), ]
457
- ticks2 <- graticule [unique0(id2 ), ]
458
- tick_positions <- c(ticks1 $ y_end , ticks2 $ y_start )
459
- tick_labels <- c(ticks1 $ degree_label , ticks2 $ degree_label )
460
-
461
- if (length(tick_positions ) > 0 ) {
462
- left <- draw_axis(
463
- tick_positions ,
464
- tick_labels ,
465
- axis_position = " left" ,
466
- theme = theme
467
- )
468
- } else {
469
- left <- zeroGrob()
470
- }
471
-
472
- list (left = left , right = right )
473
335
}
474
336
)
475
337
@@ -716,3 +578,141 @@ parse_axes_labeling <- function(x) {
716
578
labs = unlist(strsplit(x , " " ))
717
579
list (top = labs [1 ], right = labs [2 ], bottom = labs [3 ], left = labs [4 ])
718
580
}
581
+
582
+
583
+ # ' ViewScale from graticule
584
+ # '
585
+ # ' This function converts a graticule and other CoordSf's settings to a
586
+ # ' ViewScale with the appropriate `breaks` and `labels` to be rendered by a
587
+ # ' guide.
588
+ # '
589
+ # ' @param graticule A graticule as produced by `sf::st_graticule()`.
590
+ # ' @param scale An x or y position scale for a panel.
591
+ # ' @param aesthetic One of `"x"`, `"y"`, `"x.sec"` or `"y.sec'` specifying the
592
+ # ' plot position of the guide.
593
+ # ' @param label One of `"E"` for meridians or `"N"` for parallels. If neither,
594
+ # ' no tick information will be produced.
595
+ # ' @param label_graticule See `?coord_sf`.
596
+ # ' @param bbox A `numeric(4)` bounding box with 'xmin', 'ymin', 'xmax' and
597
+ # ' 'ymax' positions.
598
+ # '
599
+ # ' @return A `ViewScale` object.
600
+ # ' @noRd
601
+ # ' @keywords internal
602
+ view_scales_from_graticule <- function (graticule , scale , aesthetic ,
603
+ label , label_graticule , bbox ) {
604
+
605
+ # Setup position specific parameters
606
+ # Note that top/bottom doesn't necessarily mean to label the meridians and
607
+ # left/right doesn't necessarily mean to label the parallels.
608
+ position <- switch (
609
+ arg_match0(aesthetic , c(" x" , " x.sec" , " y" , " y.sec" )),
610
+ " x" = " bottom" ,
611
+ " x.sec" = " top" ,
612
+ " y" = " left" ,
613
+ " y.sec" = " right"
614
+ )
615
+ axis <- gsub(" \\ .sec$" , " " , aesthetic )
616
+ if (axis == " x" ) {
617
+ orth <- " y"
618
+ thres <- bbox [c(2 , 4 )] # To determine graticule is close to axis
619
+ limits <- bbox [c(1 , 3 )] # To use as scale limits
620
+ } else {
621
+ orth <- " x"
622
+ thres <- bbox [c(1 , 3 )]
623
+ limits <- bbox [c(2 , 4 )]
624
+ }
625
+
626
+ # Determine what columns in the graticule contain the starts and ends of the
627
+ # axis direction and the orthogonal direction.
628
+ axis_start <- paste0(axis , " _start" )
629
+ axis_end <- paste0(axis , " _end" )
630
+ orth_start <- paste0(orth , " _start" )
631
+ orth_end <- paste0(orth , " _end" )
632
+
633
+ # Find the start and endpoints in the graticule that are in close proximity
634
+ # to the axis position to generate 'accepted' starts and ends. Close proximity
635
+ # here is defined as within 0.1% of the scale range of the *orthogonal* scale.
636
+ if (position %in% c(" top" , " right" )) {
637
+ thres <- thres [1 ] + 0.999 * diff(thres )
638
+ accept_start <- graticule [[orth_start ]] > thres
639
+ accept_end <- graticule [[orth_end ]] > thres
640
+ } else {
641
+ thres <- thres [1 ] + 0.001 * diff(thres )
642
+ accept_start <- graticule [[orth_start ]] < thres
643
+ accept_end <- graticule [[orth_end ]] < thres
644
+ }
645
+
646
+ # Parsing the information of the `label_axes` argument:
647
+ # should we label the meridians ("E") or parallels ("N")?
648
+ type <- graticule $ type
649
+ idx_start <- idx_end <- integer(0 )
650
+ idx_start <- c(idx_start , which(type == label & accept_start ))
651
+ idx_end <- c(idx_end , which(type == label & accept_end ))
652
+
653
+ # Parsing the information of the `label_graticule` argument. Because
654
+ # geometry can be rotated, not all meridians are guaranteed to intersect the
655
+ # top/bottom axes and likewise not all parallels are guaranteed to intersect
656
+ # the left/right axes.
657
+ if (" S" %in% label_graticule ) {
658
+ idx_start <- c(idx_start , which(type == " E" & accept_start ))
659
+ }
660
+ if (" N" %in% label_graticule ) {
661
+ idx_end <- c(idx_end , which(type == " E" & accept_end ))
662
+ }
663
+ if (" W" %in% label_graticule ) {
664
+ idx_start <- c(idx_start , which(type == " N" & accept_start ))
665
+ }
666
+ if (" E" %in% label_graticule ) {
667
+ idx_end <- c(idx_end , which(type == " N" & accept_end ))
668
+ }
669
+
670
+ # Combine start and end positions for tick marks and labels
671
+ tick_start <- vec_slice(graticule , unique0(idx_start ))
672
+ tick_end <- vec_slice(graticule , unique0(idx_end ))
673
+ positions <- c(field(tick_start , axis_start ), field(tick_end , axis_end ))
674
+ labels <- c(tick_start $ degree_label , tick_end $ degree_label )
675
+
676
+ # The positions/labels need to be ordered for axis dodging
677
+ ord <- order(positions )
678
+ positions <- positions [ord ]
679
+ labels <- labels [ord ]
680
+
681
+ # Find out if the scale has defined guides
682
+ if (scale $ position != position ) {
683
+ # Try to use secondary axis' guide
684
+ guide <- scale $ secondary.axis $ guide %|| % waiver()
685
+ if (is.derived(guide )) {
686
+ guide <- scale $ guide
687
+ }
688
+ } else {
689
+ guide <- scale $ guide
690
+ }
691
+ # Instruct default guides: no ticks or labels should default to no guide
692
+ if (length(positions ) > 0 ) {
693
+ guide <- guide %| W | % " axis"
694
+ } else {
695
+ guide <- guide %| W | % " none"
696
+ }
697
+
698
+ ggproto(
699
+ NULL , ViewScale ,
700
+ scale = scale ,
701
+ guide = guide ,
702
+ position = position ,
703
+ aesthetics = scale $ aesthetics ,
704
+ name = scale $ name ,
705
+ scale_is_discrete = scale $ is_discrete(),
706
+ limits = limits ,
707
+ continuous_range = limits ,
708
+ breaks = positions ,
709
+ minor_breaks = NULL ,
710
+
711
+ # This viewscale has fixed labels, not dynamic ones as other viewscales
712
+ # might have.
713
+ labels = labels ,
714
+ get_labels = function (self , breaks = self $ get_breaks()) {
715
+ self $ labels
716
+ }
717
+ )
718
+ }
0 commit comments