aboutsummaryrefslogtreecommitdiff
path: root/pkg/images/tv/wcslab/wlsetup.x
blob: c37e24ca1418e1d59429ed39713f1cb02fb41c3f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
include <gset.h>
include <mach.h>
include <math.h>
include <math/curfit.h>
include "wcslab.h"
include "wcs_desc.h"

# WL_SETUP -- Determine all the basic characteristics of the plot.
#
# Description
#  Determine basic characteristics of the plot at hand.  This involved
#  "discovering" what part of the world system covers the screen, the
#  orientation of the world to logical systems, what type of graph will
#  be produced, etc.  Many of the parameters determined here can be 
#  over-ridden by user-specified values.

procedure wl_setup (wd)

pointer wd                       # I: the WCSLAB descriptor

bool	north
double	array[N_EDGES,N_DIM], max_value[N_DIM], min_value[N_DIM]
double	range[N_DIM], pole_position[N_DIM], view_edge[N_EDGES,N_DIM]
double	wl_coord_rotation()
pointer mw_sctran()
string	logtran "logical"
string	wrldtran "world"

begin
	# Calculate the transformations from the Logical (pixel space) system
	# to the World (possibly anything) system and back.
	WL_LWCT(wd) = mw_sctran (WL_MW(wd), logtran, wrldtran, AXIS)
	WL_WLCT(wd) = mw_sctran (WL_MW(wd), wrldtran, logtran, AXIS)

	# Indicate whether the center of the transformation is north.
	if (WL_SYSTEM_TYPE(wd) == RA_DEC)
	    north = (WL_WORLD_CENTER(wd,LATITUDE) > 0.0D0)

	# Determine the poles position.
	if (WL_SYSTEM_TYPE(wd) == RA_DEC)
	    call wl_pole_position (WL_WLCT(wd), WL_AXIS_FLIP(wd), 
	        WL_WORLD_CENTER(wd,LONGITUDE), north, WL_SYSTEM_TYPE(wd),
		pole_position)

	# Determine graph type based on the system type.
	call wl_determine_graph_type (WL_SYSTEM_TYPE(wd), pole_position,
	    WL_SCREEN_BOUNDARY(wd,1), WL_GRAPH_TYPE(wd))

	# Now find the extent of the WCS the window views, by constructing
	# x,y vectors containing evenly spaced points around the edges of
	# the viewing window.

	call wl_construct_edge_vectors (WL_SCREEN_BOUNDARY(wd,1),
	    view_edge[1,X_DIM], view_edge[1,Y_DIM], N_EDGES)
	
	# Find the range of the axes over the graphics viewport.
	call wl_l2wd (WL_LWCT(wd), WL_AXIS_FLIP(wd), view_edge[1,X_DIM], 
	     view_edge[1,Y_DIM], array[1,AXIS1], array[1,AXIS2], N_EDGES)
	call alimd (array[1,AXIS1], N_EDGES, min_value[AXIS1], max_value[AXIS1])
	call alimd (array[1,AXIS2], N_EDGES, min_value[AXIS2], max_value[AXIS2])
	range[AXIS1] = abs (max_value[AXIS1] - min_value[AXIS1])
	range[AXIS2] = abs (max_value[AXIS2] - min_value[AXIS2])

	# The above isn't good enough for the sky projections.  Deal with those.
	if (WL_SYSTEM_TYPE(wd) == RA_DEC)
	    call wl_sky_extrema (wd, array[1,AXIS1], N_EDGES, pole_position,
	        north, min_value[AXIS1], max_value[AXIS1], range[AXIS1],
		min_value[AXIS2], max_value[AXIS2], range[AXIS2])

	# Determine the rotation between the systems.
	WL_ROTA(wd) = wl_coord_rotation (WL_WLCT(wd), WL_AXIS_FLIP(wd),
	    WL_WORLD_CENTER(wd,AXIS1), max_value[AXIS2],
	    WL_WORLD_CENTER(wd,AXIS1), min_value[AXIS2])

	# Round the intervals.  This is done to make the labelling "nice" and
	# to smooth edge effects.
	if (IS_INDEFD (WL_MAJOR_INTERVAL(wd,AXIS1)) ||
	    IS_INDEFD (WL_BEGIN(wd,AXIS1)) || IS_INDEFD (WL_END(wd,AXIS1)))
	    call wl_round_axis (wd, AXIS1, min_value[AXIS1], max_value[AXIS1],
	        range[AXIS1])

	if (IS_INDEFD (WL_MAJOR_INTERVAL(wd,AXIS2)) ||
	    IS_INDEFD (WL_BEGIN(wd,AXIS2)) || IS_INDEFD (WL_END(wd,AXIS2)))
	    call wl_round_axis (wd, AXIS2, min_value[AXIS2], max_value[AXIS2],
	        range[AXIS2])
end


# WL_POLE_POSITION -- Determine logical coordinates of a pole.
#
# Description
#  Calculate the pole's position in the Logical system.
#
# Bugs
#  Can only deal with Right Ascension/Declination.

procedure wl_pole_position (wlct, flip, longitude, north, system_type, 
	pole_position)

pointer wlct                  # I: the world-to-logical transformation
int	flip                  # I: true if the axes are transposed
double  longitude             # I: the longitude to determine latitude
bool    north                 # I: true if the pole is in the north
int     system_type           # I: type of system being examined
double  pole_position[N_DIM]  # O: the pole's logical coordinates

double	sgn

begin
	switch (system_type) {

	# For Right Ascension/Declination, the pole is at any longitude but
	# at only 90 degrees (north) or -90 degrees (south) latitude.
	case RA_DEC:
	    if  (north)
		sgn = NORTH_POLE_LATITUDE
	    else
		sgn = SOUTH_POLE_LATITUDE
	    call wl_w2ld (wlct, flip, longitude, sgn, pole_position[X_DIM],
                pole_position[Y_DIM], 1)
	}

	# Sanity check on the pole position.  It is very likely that there is
	# no valid position in pixel space for the pole.  This is checked for
	# by looking for extremely large numbers.
	if  (abs (pole_position[X_DIM]) > abs (double (MAX_INT)))
	    pole_position[X_DIM] = real (MAX_INT)
	if  (abs (pole_position[Y_DIM]) > abs (double (MAX_INT)))
	    pole_position[Y_DIM] = real (MAX_INT)
end


# How close can the pole be to the center of the screen to be near-polar.
define	HOW_CLOSE	3.

# WL_DETERMINE_GRAPH_TYPE -- Determine the actual graph type.

procedure wl_determine_graph_type (system_type, pole_position,
	screen_boundary, graph_type)

int	system_type               # I: the type of WCS being dealt with
double	pole_position[N_DIM]      # I: the location of the pole
double	screen_boundary[N_SIDES]  # I: the edges of the display
int	graph_type                # O: the graph type

double	max_dist, pole_dist, xcen, ycen

begin
	# Determine graph type based on axis type.
	switch (system_type) {

	# If the pole is on the graph then force a graph_type of polar.
	case RA_DEC:

	    xcen = (screen_boundary[LEFT] + screen_boundary[RIGHT]) / 2.
	    ycen =  (screen_boundary[BOTTOM] + screen_boundary[TOP]) / 2.
	    max_dist = min ((screen_boundary[LEFT] - xcen) ** 2,
	        (screen_boundary[TOP] - ycen)**2)
	    pole_dist = (pole_position[X_DIM] - xcen) ** 2 +
	        (pole_position[Y_DIM] - ycen) ** 2
	
	    # Check to see whether the graph is "polar", "near_polar"
	    # or "normal".  If the pole lies within middle part of the
	    # viewport, then the graph is "polar". If the pole is within
	    # a certain maximum distance then it is "near_polar".
	    # Otherwise it is normal.

	    switch (graph_type) {
	    case NORMAL:
		# do nothing
	    case POLAR:
		# do nothing
	    case NEAR_POLAR:
		# do nothing
	    default:
	        if (pole_dist < max_dist)
	            graph_type = POLAR
	        else if  (pole_dist < HOW_CLOSE * max_dist)
	            graph_type = NEAR_POLAR
	        else
	            graph_type = NORMAL
	    }

	# For all other cases, explicitely set this to normal.
	default:
	    graph_type = NORMAL
	}
end


# WL_CONSTRUCT_EDGE_VECTORS -- Construct vectors of values along window's edge.
#
# Description
#  This routines filles two arrays, with the x-values and y-values of
#  evenly spaced points along the edges of the screen.  This is used to
#  make transformation of the logical edges into the world system
#  more convenient.

procedure wl_construct_edge_vectors (screen_boundary, x, y, vector_size)

double	screen_boundary[N_SIDES]       # I: the side values
double	x[vector_size], y[vector_size] # O: the edge vector points
int	vector_size                    # I: the number of edge vector points

double	current, interval
int	i, left_over, offset1, offset2, side_length

begin
	# Divide the vectors into equal amounts for each side.
	side_length = vector_size / N_SIDES
	left_over = mod (vector_size, N_SIDES)

	# Calculate the horizontal components.
	interval =  (screen_boundary[RIGHT] - screen_boundary[LEFT]) /
	    side_length
	current = screen_boundary[LEFT]
	offset1 = side_length
	for  (i = 1; i <= side_length; i = i + 1) {
	    x[i] = current + interval
	    y[i] = screen_boundary[BOTTOM]
	    x[i+offset1] = current
	    y[i+offset1] = screen_boundary[TOP]
	    current = current + interval 
	}

	# Calculate the verticle components.
	interval = (screen_boundary[TOP] - screen_boundary[BOTTOM]) /
	    side_length
	current = screen_boundary[BOTTOM]
	offset1 = 2 * side_length
	offset2 = 3 * side_length
	for  (i = 1; i <= side_length; i = i + 1) {
	    x[i+offset1] = screen_boundary[LEFT]
	    y[i+offset1] = current
	    x[i+offset2] = screen_boundary[RIGHT]
	    y[i+offset2] = current + interval
	    current = current + interval 
	}

	# Fill in the left over with a single point.
	offset1 = 4 * side_length
	for  (i = 1; i <= left_over; i = i + 1) {
	    x[i+offset1] = screen_boundary[LEFT]
	    y[i+offset1] = screen_boundary[BOTTOM]
	}

end


# WL_SKY_EXTREMA -- Determine what range the view window covers in the sky.
# This routine is only called if the WCS RA,DEC.
#
# Description
#   Because of the different graph types and the fact that axis 1 usually
#   wraps, more work needs to be done to determine what part of the sky
#   is covered by the viewing window.

procedure wl_sky_extrema (wd, ax1_array, n_points, pole_position, north,
	ax1min, ax1max, ax1ran, ax2min, ax2max, ax2ran)

pointer	wd                         # I: the WCSLAB descriptor
double  ax1_array[n_points]        # I: the axis 1 edge vector
int     n_points                   # I: the length of the edge vector
double  pole_position[N_DIM]       # I: the pole position
bool    north                      # I: is the pole in the north ?
double  ax1min, ax1max, ax1ran     # I/O: the minimum, maximum, range in axis 1
double  ax2min, ax2max, ax2ran     # I/O: the minimum, maximum, range in axis 2

bool	is_pole
double	nx, ny, xcen, ycen
int	wl_direction_from_axis1(), wl_find_side(), wl_opposite_side()

begin
	# Is the pole on the graph ?
	if ((pole_position[X_DIM] < WL_SCREEN_BOUNDARY(wd,LEFT))   ||
	    (pole_position[X_DIM] > WL_SCREEN_BOUNDARY(wd,RIGHT))  ||
	    (pole_position[Y_DIM] < WL_SCREEN_BOUNDARY(wd,BOTTOM)) ||
	    (pole_position[Y_DIM] > WL_SCREEN_BOUNDARY(wd,TOP)))
	    is_pole = false
	else
	    is_pole = true

	# If so adjust the RA and DEC ranges appropriately.
	if (is_pole) {

	    # Set the RA range.
	    ax1min = 0.0D0
	    ax1max = 359.9D0
	    ax1ran = 360.0D0

	    # Set the dec range.
	    if (north)
	        ax2max = NORTH_POLE_LATITUDE - ((NORTH_POLE_LATITUDE -
		    ax2min) * DISTANCE_TO_POLE )
	    else
	        ax2min = SOUTH_POLE_LATITUDE + ((NORTH_POLE_LATITUDE +
		    ax2max) * DISTANCE_TO_POLE)
	    ax2ran = abs (ax2max - ax2min)

	    # Mark the pole.
	    call gmark (WL_GP(wd), real (pole_position[X_DIM]),
		real (pole_position[Y_DIM]), POLE_MARK_SHAPE, POLE_MARK_SIZE,
		POLE_MARK_SIZE)

	} else {
	     # Only the RA range needs adjusting.
	     call wl_ra_range (ax1_array, n_points, ax1min, ax1max, ax1ran)
	}

	# Adjust the labelling characteristics appropritatley for various
	# types of graphs.

	if  (WL_GRAPH_TYPE(wd) == POLAR) {

	    # Determine which direction the axis 2's will be labeled on polar
	    # graphs.
	    if (IS_INDEFD (WL_POLAR_LABEL_POSITION(wd))) {
	        call wl_get_axis2_label_direction (WL_LWCT(wd),
		    WL_AXIS_FLIP(wd), pole_position, WL_SCREEN_BOUNDARY(wd,1),
		    WL_POLAR_LABEL_POSITION(wd), WL_BAD_LABEL_SIDE(wd))
	    } else {
	        WL_BAD_LABEL_SIDE(wd) = wl_direction_from_axis1 (WL_WLCT(wd),
	            WL_AXIS_FLIP(wd), pole_position, north,
		    WL_POLAR_LABEL_POSITION(wd), WL_BEGIN(wd,AXIS2),
	            WL_END(wd,AXIS2), WL_SCREEN_BOUNDARY(wd,1))
		if (IS_INDEFI (WL_BAD_LABEL_SIDE(wd)))
		    WL_BAD_LABEL_SIDE(wd) = BOTTOM
	    }

	    # If the graph type is polar, then determine how to justify
	    # the labels.

	    if (IS_INDEFI (WL_POLAR_LABEL_DIRECTION(wd)))
	        WL_POLAR_LABEL_DIRECTION(wd) =
	            wl_opposite_side (WL_BAD_LABEL_SIDE(wd))

	# If the graph_type is near-polar, then handle the directions a bit
	# differently.
	} else if (WL_GRAPH_TYPE(wd) == NEAR_POLAR) {

	    # Find the side that the pole is on.  
	    xcen = (WL_SCREEN_BOUNDARY(wd,LEFT) +
	        WL_SCREEN_BOUNDARY(wd,RIGHT)) / 2.
	    ycen = (WL_SCREEN_BOUNDARY(wd,BOTTOM) +
	        WL_SCREEN_BOUNDARY(wd,TOP)) / 2.
	    call wl_axis_on_line (xcen, ycen, pole_position[X_DIM],
	        pole_position[Y_DIM], WL_SCREEN_BOUNDARY(wd,1), nx, ny)

	    if (IS_INDEFD(nx) || IS_INDEFD(ny)) {
		WL_BAD_LABEL_SIDE(wd) = BOTTOM
		WL_POLAR_LABEL_DIRECTION(wd) = LEFT
	    } else {
	        WL_BAD_LABEL_SIDE(wd) = wl_find_side (nx, ny,
	            WL_SCREEN_BOUNDARY(wd,1))
	        if (WL_BAD_LABEL_SIDE(wd) == LEFT || WL_BAD_LABEL_SIDE(wd) ==
	            RIGHT)
		    if (abs (ny - WL_SCREEN_BOUNDARY(wd,BOTTOM)) <
	                abs (ny - WL_SCREEN_BOUNDARY(wd,TOP)))
		        WL_POLAR_LABEL_DIRECTION(wd) = BOTTOM
		    else
		        WL_POLAR_LABEL_DIRECTION(wd) = TOP
	        else
		    if (abs (nx - WL_SCREEN_BOUNDARY(wd,LEFT)) <
	                abs (nx - WL_SCREEN_BOUNDARY(wd,RIGHT)))
		        WL_POLAR_LABEL_DIRECTION(wd) = LEFT
		    else
		        WL_POLAR_LABEL_DIRECTION(wd) = RIGHT
	    }

	}
end


# WL_COORD_ROTATION -- Determine "rotation" between the coordinate systems.
#
# Description
#  This routine takes the world-to-logical coordinate transformation and
#  two points in the world system which should define the positive verticle
#  axis in the world system.  These points are translated into the logical
#  system and the angle between the logical vector and its positive verticle
#  vector is calculated and returned.  The rotation angle is returned
#  in degrees and is always positive.

double	procedure wl_coord_rotation (wlct, flip, wx1, wy1, wx2, wy2)

pointer	wlct               # I: the world-to-logical transformation
int	flip               # I: true if the coordinates are transposed
double	wx1, wy1, wx2, wy2 # I: points in world space to figure rotation from

double	delx, dely, rota, x1, y1, x2, y2
bool	fp_equald()

begin
	# Transform the points to the logical system.
	call wl_w2ld (wlct, flip, wx1, wy1, x1, y1, 1)
	call wl_w2ld (wlct, flip, wx2, wy2, x2, y2, 1)

	# Determine the rotation.
	delx = x2 - x1
	dely = y2 - y1
	if (fp_equald (delx, 0.0D0) && fp_equald (dely, 0.0D0))
	    rota = 0.
	else
	    rota = RADTODEG (atan2 (dely, delx))

	if  (rota < 0.0D0)
	    rota = rota + FULL_CIRCLE

	return (rota)
end


# Define how many axis one should go for.

define RA_NUM_TRY         6
define DEC_NUM_TRY        6
define DEC_POLAR_NUM_TRY  4

# WL_ROUND_AXIS - Round values for the axis.

procedure wl_round_axis (wd, axis, minimum, maximum, range)

pointer wd                       # I: the WCSLAB descriptor
int     axis                     # I: the axis being worked on
double  minimum, maximum, range  # I: raw values to be rounded

int	num_try

begin
	# Depending on axis type, round the values.
	switch (WL_SYSTEM_TYPE(wd)) {
	case RA_DEC:
	    if (axis == LONGITUDE)
	        call wl_round_ra (minimum, maximum, range, RA_NUM_TRY,
	            WL_BEGIN(wd,LONGITUDE), WL_END(wd,LONGITUDE),
		    WL_MAJOR_INTERVAL(wd,LONGITUDE))
	    else {
	        if (WL_GRAPH_TYPE(wd) == POLAR)
	            num_try = DEC_POLAR_NUM_TRY
	        else
	            num_try = DEC_NUM_TRY
	        call wl_round_dec (minimum, maximum, range, num_try,
	            WL_BEGIN(wd,LATITUDE), WL_END(wd,LATITUDE), 
	            WL_MAJOR_INTERVAL(wd,LATITUDE))
	    }

	default:
	    call wl_generic_round (minimum, maximum, range, WL_BEGIN(wd,axis),
	        WL_END(wd,axis), WL_MAJOR_INTERVAL(wd,axis))
	}

end


# WL_GET_AXIS2_LABEL_DIRECTION -- Dertermine label direction for latitides.
#
# Description
#  Determine from which edge of the graph the axis 2 labels are to
#  appear.  This (in general) is the opposite edge from which the pole
#  is nearest to.  Move the pole to the closest edges, determine which
#  side it is, then chose the direction as the opposite.  Also determines
#  the Axis 1 at which the Axis 2 labels will appear.

procedure wl_get_axis2_label_direction (lwct, flip, pole_position, 
	screen_boundary, pole_label_position, bad_label_side)

pointer lwct                      # I: logical-to-world transformation
int	flip                      # I: true if the axis are transposed
double  pole_position[N_DIM]      # I: the position of the pole
double  screen_boundary[N_SIDES]  # I: the edges of the screen
double  pole_label_position       # O: the axis 1 that axis 2 labels should
	                          #    appear for polar|near-polar graphs
int     bad_label_side            # O: side not to place axis 1 labels

double	dif, tdif, dummy

begin
	# Determine which direction, up or down, the axis 2's will be labelled.
	dif = abs (screen_boundary[TOP] - pole_position[AXIS2])
	bad_label_side= TOP
	tdif = abs (screen_boundary[BOTTOM] - pole_position[AXIS2])
	if (tdif < dif) {
	  dif = tdif
	  bad_label_side = BOTTOM
	}

	# Determine at what value of Axis 1 the Axis 2 labels should appear.
	switch (bad_label_side) {
	case TOP: 
	    call wl_l2wd (lwct, flip, pole_position[AXIS1],
	        screen_boundary[BOTTOM], pole_label_position, dummy, 1)
	case BOTTOM: 
	    call wl_l2wd (lwct, flip, pole_position[AXIS1],
	        screen_boundary[TOP], pole_label_position, dummy, 1)
	case LEFT: 
	    call wl_l2wd (lwct, flip, screen_boundary[RIGHT],
	        pole_position[AXIS2], pole_label_position, dummy, 1)
	case RIGHT:  
	    call wl_l2wd (lwct, flip, screen_boundary[LEFT],
	        pole_position[AXIS2], pole_label_position, dummy, 1)
	}

end


# WL_DIRECTION_FROM_AXIS1 -- Determine axis 2 label direction from axis 1.
#
# Function Returns
#   This returns the side where Axis 1 should not be labelled.

int procedure wl_direction_from_axis1 (wlct, flip, pole_position, north,
	polar_label_position, lbegin, lend, screen_boundary)

pointer wlct                      # I: world-to-logical transformation
int	flip                      # I: true if the axes are transposed
double  pole_position[N_DIM]      # I: the pole position
bool    north                     # I: true if the pole is the north pole
double  polar_label_position      # I: the axis 1 where axis 2 will be 
	                          #    marked
double  lbegin                    # I: low end of axis 2
double  lend                      # I: high end of axis 2
double  screen_boundary[N_SIDES]  # I: the window boundary

double	nx, ny, cx, cy
int	wl_find_side()

begin
	# Determine the point in logical space where the axis 1 and the
	# minimum axis 2 meet.

	if (north)
	    call wl_w2ld (wlct, flip, polar_label_position, lbegin, nx, ny, 1)
	else
	    call wl_w2ld (wlct, flip, polar_label_position, lend, nx, ny, 1)

	# This line should cross a window boundary.  Find that point.

	call wl_axis_on_line (pole_position[X_DIM], pole_position[Y_DIM],
	    screen_boundary, nx, ny, cx, cy)

	# Get the side that the crossing point is. This is the axis 2 labelling
	# direction.

	if (IS_INDEFD(cx) || IS_INDEFD(cy))
	    return (INDEFI)
	else
	    return (wl_find_side (cx, cy, screen_boundary))
end


# WL_OPPOSITE_SIDE - Return the opposite of the given side.
#
# Returns
#   The opposite side of the specified side as follows:
#           RIGHT  -> LEFT
#           LEFT   -> RIGHT
#           TOP    -> BOTTOM
#           BOTTOM -> TOP

int procedure wl_opposite_side (side)

int	side		# I: the side to find the opposite of

int	new_side

begin
	switch (side) {
	case LEFT:
	    new_side = RIGHT
	case RIGHT:
	    new_side = LEFT
	case TOP:
	    new_side = BOTTOM
	case BOTTOM:
	    new_side = TOP
	}

	return (new_side)
end


# Define whether things are on the screen boundary or on them.

define IN (($1>=screen_boundary[LEFT])&&($1<=screen_boundary[RIGHT])&&($2>=screen_boundary[BOTTOM])&&($2<=screen_boundary[TOP]))


# WL_AXIS_ON_LINE - Determine intersection of line and a screen boundary.
#
# Description
#  Return the point where the line defined by the two input points 
#  crosses a screen boundary.  The boundary is choosen by determining
#  which one is between the two points.

procedure wl_axis_on_line (x0, y0, x1, y1, screen_boundary, nx, ny)

double	x0, y0, x1, y1            # I: random points in space
double	screen_boundary[N_SIDES]  # I: sides of the window
double	nx, ny                    # O: the closest point on a window boundary

double	x_val[N_SIDES], y_val[N_SIDES], tx0, ty0, tx1, ty1, w[2]
int	i
pointer	cvx, cvy
double	dcveval()

begin
	# Get the line parameters.
	x_val[1] = x0
	x_val[2] = x1
	y_val[1] = y0
	y_val[2] = y1

	iferr (call dcvinit (cvx, CHEBYSHEV, 2, min (x0, x1), max (x0, x1)))
	    cvx = NULL
	else {
	    call dcvfit (cvx, x_val, y_val, w, 2, WTS_UNIFORM, i)
	    if (i != OK)
	        call error (i, "wlaxie: Error solving on X")
	}

	iferr (call dcvinit (cvy, CHEBYSHEV, 2, min (y0, y1), max (y0, y1)))
	    cvy = NULL
	else {
	    call dcvfit (cvy, y_val, x_val, w, 2, WTS_UNIFORM, i)
	    if (i != OK)
	        call error (i, "wlaxie: Error solving on Y")
	}

	# Solve for each side.
	x_val[LEFT] = screen_boundary[LEFT]
	if (cvx == NULL)
	    y_val[LEFT] = screen_boundary[LEFT]
	else
	    y_val[LEFT] = dcveval (cvx, x_val[LEFT])

	x_val[RIGHT] = screen_boundary[RIGHT]
	if (cvx == NULL )
	    y_val[RIGHT] = screen_boundary[RIGHT]
	else
	    y_val[RIGHT] = dcveval (cvx, x_val[RIGHT])

	y_val[TOP] = screen_boundary[TOP]
	if (cvy == NULL)
	    x_val[TOP] = screen_boundary[TOP]
	else
	    x_val[TOP] = dcveval (cvy, y_val[TOP])

	y_val[BOTTOM] = screen_boundary[BOTTOM]
	if (cvy == NULL)
	    x_val[BOTTOM] = screen_boundary[BOTTOM]
	else
	    x_val[BOTTOM] = dcveval (cvy, y_val[BOTTOM])

	# Rearrange the input points to be in ascending order.
	if  (x0 < x1) {
	    tx0 = x0
	    tx1 = x1
	} else {
	    tx0 = x1
	    tx1 = x0
	}

	if  (y0 < y1) {
	    ty0 = y0
	    ty1 = y1
	} else {
	    ty0 = y1
	    ty1 = y0
	}

	# Now find which point is between the two given points and is within
	# the viewing area.
	# NOTE: Conversion to real for the check- if two points are so close
	# for double, any of them would serve as the correct answer.

	nx = INDEFD
	ny = INDEFD
	for  (i = 1; i <= N_SIDES; i = i + 1)
	    if  (real (tx0) <= real (x_val[i]) && 
	        real (x_val[i]) <= real (tx1)  &&
	        real (ty0) <= real (y_val[i])  && 
	        real (y_val[i]) <= real (ty1)  &&
	        IN (x_val[i], y_val[i]) ) {
	        nx = x_val[i]
	        ny = y_val[i]
	    }

	# Release the curve fit descriptors.
	if (cvx != NULL)
	    call dcvfree (cvx)
	if (cvy != NULL)
	    call dcvfree (cvy)
end


# WL_FIND_SIDE -- Return the side that the given point is lying on.
#
# Function Returns
#  Return the side, TOP, BOTTOM, LEFT, or RIGHT, that the specified
#  point is lying on.  One of the coordinates must be VERY CLOSE to one of 
#  the sides or INDEFI will be returned.

int procedure wl_find_side (x, y, screen_boundary)

double x, y                      # I: the point to inquire about
double screen_boundary[N_SIDES]  # I: the edges of the screen

double	dif, ndif
int	side

begin
	dif = abs (x - screen_boundary[LEFT])
	side = LEFT

	ndif = abs (x - screen_boundary[RIGHT])
	if  (ndif < dif) {
	  side = RIGHT
	  dif = ndif
	}
	
	ndif = abs (y - screen_boundary[BOTTOM])
	if  (ndif < dif) {
	  side = BOTTOM
	  dif = ndif
	}
	
	ndif = abs (y - screen_boundary[TOP])
	if  (ndif < dif)
	  side = TOP

	return (side)
end


# WL_RA_RANGE -- Determine the range in RA given a list of possible values.
#
# Description
#  Determine the largest range in RA from the provided list of values.
#  The problem here is that it is unknown which way the graph is oriented.
#  To simplify the problem, it is assume that the graph range does not extend
#  beyond a hemisphere and that all distances in RA is less than a hemisphere.
#  This assumption is needed to decide when the 0 hour is on the graph.

procedure wl_ra_range (ra, n_values, min, max, diff)

double	ra[ARB]		# I:   the possible RA values
int	n_values	# I:   the number of possible RA values
double	min		# I/O: the minimum RA
double	max		# I/O: the maximum RA
double	diff		# I/O: the difference between minimum and maximum

bool	wrap
int	i, j, n_diffs
pointer	sp, max_array, min_array, ran_array
int	wl_max_element_array()

begin
	call smark (sp)
	call salloc (max_array, n_values * n_values, TY_DOUBLE)
	call salloc (min_array, n_values * n_values, TY_DOUBLE)
	call salloc (ran_array, n_values * n_values, TY_DOUBLE)

	# Check whether the RA is wrapped or not.
	n_diffs = 0
	do i = 1, n_values {
	    if (ra[i] >= min && ra[i] <= max)
		next
	    n_diffs = n_diffs + 1
	}
	if (n_diffs > 0)
	    wrap = true
	else
	    wrap = false

	n_diffs = 0
	for  (i = 1; i <= n_values; i = i + 1) {
	    for  (j = i + 1; j <= n_values; j = j + 1) {
	        n_diffs = n_diffs + 1
	        call wl_getradif (ra[i], ra[j], Memd[min_array+n_diffs-1],
		    Memd[max_array+n_diffs-1], Memd[ran_array+n_diffs-1],
		    wrap)
	    }
	}

	i = wl_max_element_array (Memd[ran_array], n_diffs)
	min = Memd[min_array+i-1]
	max = Memd[max_array+i-1]
	diff = Memd[ran_array+i-1]

	call sfree (sp)
end


# WL_GETRADIFF -- Get differences in RA based on degrees.
#
# Description
#  This procedure determines, given two values in degrees, the minimum,
#  maximum, and difference of those values.  The assumption is that no
#  difference should be greater than half a circle.  Based on this assumption,
#  a difference is found and the minimum and maximum are determined.  The
#  maximum can be greater than 360 degrees.

procedure wl_getradif (val1, val2, min, max, diff, wrap)

double	val1, val2  # I: the RA values
double	min, max    # O: the min RA and max RA (possibly > 360.0)
double	diff        # O: the min, max difference
bool	wrap	    # I: is the ra wrapped ?

begin
	if (! wrap && (abs (val1 - val2) > HALF_CIRCLE))
	    if (val1 < val2) {
	        min = val2
	        max = val1 + FULL_CIRCLE
	    } else {
	        min = val1
	        max = val2 + FULL_CIRCLE
	    }
	else
	    if (val1 < val2) {
		min = val1
		max = val2
	    } else {
		min = val2
		max = val1
	    }
	diff = max - min
end


define	NRAGAP    26

# WL_ROUND_RA -- Modify the RA limits and calculate an interval to label.
#
# Description
#  The RA limits determine by just the extremes of the window ususally do
#  not fall on "reasonable" boundaries; i.e. essentially they are random
#  numbers.  However, for labelling purposes, it is nice to have grids and
#  tick marks for "rounded" numbers-  For RA, this means values close to
#  whole hours, minutes, or seconds.  For example, if the span across the
#  plot is a few hours, the marks and labels should represent simply whole
#  hours.  This routine determines new RA limits based on this and some 
#  interval to produce marks between the newly revised limits.

procedure wl_round_ra (longmin, longmax, longran, num_try, minimum, maximum, 
	major_interval)

double	longmin          # I: longitude minimum
double	longmax          # I: longitude maximum
double	longran          # I: longitude range
int	num_try          # I: the number of intervals to try for
double	minimum          # O: the minimum RA value (in degrees)
double	maximum          # O: the maximum RA value (in degrees)
double	major_interval   # O: the appropriate interval (in degrees) for the
	                 #    major line marks.

double  ragap[NRAGAP]
double	wl_check_arrayd(), wl_round_upd()
data	ragap / 1.0D-4, 2.0D-4, 5.0D-4,  1.0D-3,  2.0D-3,  5.0D-3,
	        0.01D0, 0.02D0, 0.05D0, 0.1D0,  0.2D0,  0.5D0,   1.0D0,
		2.0D0,   5.0D0, 10.0D0, 20.0D0, 30.0D0, 60.0D0, 120.0D0,
		300.0D0, 600.0D0,  1.2D3,  1.8D3,  3.6D3,  7.2D3 /


begin
	major_interval = wl_check_arrayd (DEGTOST (longran) / num_try, 
	    ragap, NRAGAP)
	minimum = STTODEG (wl_round_upd (DEGTOST (longmin), major_interval) - 
	    major_interval)
	maximum = STTODEG (wl_round_upd (DEGTOST (longmax), major_interval))
	major_interval = STTODEG (major_interval)
end


define	NDECGAP    28

# WL_ROUND_DEC -- Modify the DEC limits and calculate an interval to label.
#
# Description
#  The DEC limits determine by just the extremes of the window ususally do
#  not fall on "reasonable" boundaries; i.e. essentially they are random
#  numbers.  However, for labelling purposes, it is nice to have grids and
#  tick marks for "rounded" numbers-  For DEC, this means values close to
#  whole degrees, minutes, or seconds.  For example, if the span across the
#  plot is a few degrees, the marks and labels should represent simply whole
#  degrees.  This routine determines new DEC limits based on this and some 
#  interval to produce marks between the newly revised limits.

procedure wl_round_dec (latmin, latmax, latran, num_try, minimum, maximum, 
	major_interval)

double latmin              # I: the latitude minimum
double latmax              # I: the latitude maximum
double latran              # I: the latitude range
int    num_try             # I: number of intervals to try for
double minimum             # O: the DEC minimum
double maximum             # O: the DEC maximum
double major_interval      # O: the labelling interval to use for major lines

double  decgap[NDECGAP]
double	wl_check_arrayd(), wl_round_upd()
data	decgap / 1.0D-4, 2.0D-4,  5.0D-4, 1.0D-3, 2.0D-3,  5.0D-3,
	         0.01D0, 0.02D0,  0.05D0, 0.1D0,  0.2D0,   0.5D0,   1.0D0,
		 2.0D0, 5.0D0, 10.0D0,20.0D0, 30.0D0, 60.0D0, 120.0d0,
		 300.0D0, 600.0D0, 1.2D3, 1.8D3, 3.6D3, 7.2D3, 1.8D4, 3.6D4 /

begin
	major_interval = wl_check_arrayd (DEGTOSA (latran) / num_try, 
	    decgap, NDECGAP)
	minimum = SATODEG (wl_round_upd (DEGTOSA (latmin), major_interval) -
	    major_interval)
	maximum = SATODEG (wl_round_upd (DEGTOSA (latmax), major_interval))
	major_interval = SATODEG (major_interval)

	# Make sure that the grid marking does not include the pole.
	maximum = min (maximum, NORTH_POLE_LATITUDE - major_interval)
	minimum = max (minimum, SOUTH_POLE_LATITUDE + major_interval)
end


# WL_GENERIC_ROUND -- Round the values (if possible).
#
# History
#   7Feb91 - Created by Jonathan D. Eisenhamer, STScI.

procedure wl_generic_round (minimum, maximum, range, lbegin, lend, interval)

double	minimum, maximum, range  # I: the raw input values
double	lbegin, lend             # O: the begin and end label points
double	interval                 # O: the major label interval

double	amant, diff
int	iexp, num
double	wl_round_upd()

begin
	diff = log10 (abs (range) / 4.D0)
	iexp = int (diff)
	if (diff < 0)
	    iexp = iexp - 1

	amant = diff - double (iexp)
	if (amant < 0.15D0)
	  num = 1
	else if (amant < 0.50D0)
	  num = 2
	else if (amant < 0.85D0)
	  num = 5
	else
	  num = 10

	interval = double (num) * 10.0D0 ** iexp
	lbegin = wl_round_upd (minimum, interval) - interval
	lend = wl_round_upd (maximum, interval)
end


#  WL_ROUND_UPD -- Round X up to nearest whole multiple of Y.

double procedure wl_round_upd (x, y)

double	x		# I: value to be rounded
double	y		# I: multiple of X is to be rounded up in

double	z, r

begin
	if (x < 0.0D0)
	    z = 0.0D0
	else
	    z = y
	r = y * double (int ((x + z) / y))

	return (r)
end



#  WL_CHECK_ARRAYD -- Check proximity of array elements to each other.
#
# Description
#  Returns the element of the array arr(n) which is closest to an exact
#  value EX.

double procedure wl_check_arrayd (ex, arr, n)

double	ex		# I: the exact value
double	arr[ARB]	# I: the array of rounded values
int	n		# I: dimension of array  of rounded values

int	j

begin
	for (j = 1;  j < n && (ex - arr[j]) > 0.0D0;  j = j + 1)
	    ;
	if (j > 1 && j < n)
	    if (abs (ex - arr[j-1]) < abs (ex - arr[j])) 
		j = j - 1

	return (arr[j])
end