1 /*
2  * tkgeomapPlace.c --
3  *
4  * 	This file defines functions that display geoplaces as canvas items.
5  *
6  * Copyright (c) 2004 Gordon D. Carrie. All rights reserved.
7  *
8  * Licensed under the Open Software License version 2.1
9  *
10  * Please address questions and feedback to user0@tkgeomap.org
11  *
12  * @(#) $Id: tkgeomapPlace.c,v 1.19 2007/06/26 21:58:34 tkgeomap Exp $
13  *
14  **********************************************************************
15  *
16  */
17 
18 #include "tkgeomap.h"
19 #include "tkgeomapInt.h"
20 
21 /*
22  * The structure below declares the record for each geoplace item.
23  */
24 
25 typedef struct {
26     Tk_Item header;		/* Generic stuff that's the same for all
27 				 * types.  MUST BE FIRST IN STRUCTURE. */
28     Tcl_Interp *interp;		/* Interpreter in which item was created */
29     Tk_Canvas canvas;		/* Canvas in which item is displayed */
30     double xRef, yRef;		/* Canvas coordinates of reference point.
31 				 * This is NOT necessarily where the place is */
32     GeoPt refPt;		/* Geographic coordinates of reference point.
33 				 * This is NOT necessarily where the place is */
34     MapPt mRefPt;		/* Map coordinates of reference point. */
35     Tclgeomap_Place tclGeoPlace;/* Geoplace the item displays */
36     MapPt mapPt;		/* Map coordinates of the place. */
37     double xPlc, yPlc;		/* Canvas coordinates of the place */
38     Tclgeomap_Proj proj;	/* Cartographic projection */
39     double scale;		/* Cartographic scale (pure number ratio) */
40     int	updateMap;		/* If true, recompute place map coordinates */
41     int updateCvs;		/* If true, recompute place canvas coord's */
42     XColor *dotColor;		/* Dot color */
43     int dotSize;		/* Dot size */
44     GC dotGC;			/* Graphics context for drawing dot */
45     Pixmap bitmap;		/* Bitmap to draw at place */
46     XColor *bmColor;		/* Bitmap foreground color */
47     GC bmGC;			/* Graphics context for drawing bitmap */
48     char *text;			/* Text string to print at place */
49     XColor *textColor;		/* Text color */
50     Tk_Font font;		/* Text font */
51     Tk_Anchor textAnchor;	/* Relative location of text. */
52     GC txtGC;			/* Graphics context for drawing text */
53     Tk_TextLayout layout;	/* Text layout */
54     int xTxt;			/* Offset from place to left edge of text box */
55     int yTxt;			/* Offset from place to top of text box */
56     int txtWidth;		/* Width of text box */
57     int txtHeight;		/* Height of text box */
58     Angle arrow_az;		/* Arrow azimuth relative to true north */
59     double arrow_len;		/* Arrow length, pixels */
60     double arrow_tip_sz;	/* Length of edges of arrow tip, fraction of
61 				 * arrow length */
62     short arrow_base_x;		/* Offset from place to arrow base, in */
63     short arrow_base_y;		/* drawable coordinates */
64     short arrow_head_x;		/* Offset from place to arrow head, in */
65     short arrow_head_y;		/* drawable coordinates */
66     short arrow_ltip_x;		/* Offset from arrow head to left side of */
67     short arrow_ltip_y;		/* arrow tip, drawable coordinates */
68     short arrow_rtip_x;		/* Offset from arrow head right side of */
69     short arrow_rtip_y;		/* arrow tip, drawable coordinates */
70     XColor *arrowColor;
71     GC arrowGC;			/* Graphics context for drawing arrow */
72 } PlaceItem;
73 
74 /*
75  * Prototypes for procedures defined in this file:
76  */
77 
78 static int		createProc _ANSI_ARGS_((Tcl_Interp *interp,
79 				Tk_Canvas canvas, struct Tk_Item *itemPtr,
80 				int objc, Tcl_Obj *CONST objv[]));
81 static int		configProc _ANSI_ARGS_((Tcl_Interp *interp,
82 				Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
83 				Tcl_Obj *CONST objv[], int flags));
84 static int		coordProc _ANSI_ARGS_((Tcl_Interp *interp,
85 				Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
86 				Tcl_Obj *CONST objv[]));
87 static void		deleteProc _ANSI_ARGS_((Tk_Canvas canvas,
88 				Tk_Item *itemPtr, Display *display));
89 static void		forgetPlace _ANSI_ARGS_((ClientData clientData));
90 static void		forgetProj _ANSI_ARGS_((ClientData clientData));
91 static void		hide _ANSI_ARGS_((PlaceItem *plcItemPtr));
92 static void		displayProc _ANSI_ARGS_((Tk_Canvas canvas,
93 				Tk_Item *itemPtr, Display *display,
94 				Drawable dst, int x, int y, int width,
95 				int height));
96 static double		pointProc _ANSI_ARGS_((Tk_Canvas canvas,
97 				Tk_Item *itemPtr, double *coordPtr));
98 static int		areaProc _ANSI_ARGS_((Tk_Canvas canvas,
99 				Tk_Item *itemPtr, double *rectPtr));
100 static int		postscriptProc _ANSI_ARGS_((Tcl_Interp *interp,
101 				Tk_Canvas canvas, Tk_Item *itemPtr,
102 				int prepass));
103 static void		scaleProc _ANSI_ARGS_((Tk_Canvas canvas,
104 				Tk_Item *itemPtr, double originX,
105 				double originY, double scaleX, double scaleY));
106 static void		translateProc _ANSI_ARGS_((Tk_Canvas canvas,
107 				Tk_Item *itemPtr, double deltaX,
108 				double deltaY));
109 static int		parseGeoPlaceOption _ANSI_ARGS_((ClientData clientData,
110 				Tcl_Interp *interp, Tk_Window tkwin,
111 				char *value, char *widgRec, int offset));
112 static char *		printGeoPlaceOption _ANSI_ARGS_((ClientData clientData,
113 				Tk_Window tkwin, char *widgRec, int offset,
114 				Tcl_FreeProc **freeProcPtr));
115 static void		geoPlaceUpdate _ANSI_ARGS_((ClientData clientData));
116 static int		parseRefPtOption _ANSI_ARGS_((ClientData clientData,
117 				Tcl_Interp *interp, Tk_Window tkwin,
118 				char *value, char *widgRec, int offset));
119 static char *		printRefPtOption _ANSI_ARGS_((ClientData clientData,
120 				Tk_Window tkwin, char *widgRec, int offset,
121 				Tcl_FreeProc **freeProcPtr));
122 static int		parseProjOption _ANSI_ARGS_((ClientData clientData,
123 				Tcl_Interp *interp, Tk_Window tkwin,
124 				char *value, char *widgRec, int offset));
125 static char *		printProjOption _ANSI_ARGS_((ClientData clientData,
126 				Tk_Window tkwin, char *widgRec, int offset,
127 				Tcl_FreeProc **freeProcPtr));
128 static int		parseArrowOption _ANSI_ARGS_((ClientData clientData,
129 				Tcl_Interp *interp, Tk_Window tkwin,
130 				char *value, char *widgRec, int offset));
131 static char *		printArrowOption _ANSI_ARGS_((ClientData clientData,
132 				Tk_Window tkwin, char *widgRec, int offset,
133 				Tcl_FreeProc **freeProcPtr));
134 static void		computeTextLayout _ANSI_ARGS_((
135 				PlaceItem *plcItemPtr));
136 static void		updateMap _ANSI_ARGS_((ClientData clientData));
137 static void		updateCvs _ANSI_ARGS_((PlaceItem *plcItemPtr));
138 
139 /*
140  * Custom configuration options
141  */
142 
143 static Tk_CustomOption geoPlaceOption = {
144     parseGeoPlaceOption,
145     printGeoPlaceOption,
146     NULL
147 };
148 
149 static Tk_CustomOption refPtOption = {
150     parseRefPtOption,
151     printRefPtOption,
152     NULL
153 };
154 
155 static Tk_CustomOption projOption = {
156     parseProjOption,
157     printProjOption,
158     NULL
159 };
160 
161 static Tk_CustomOption arrowOption = {
162     parseArrowOption,
163     printArrowOption,
164     NULL
165 };
166 
167 /*
168  * The members of tagsOption will be set in TkgeomapPlaceInit
169  */
170 
171 static Tk_CustomOption tagsOption = {
172     NULL, NULL, NULL
173 };
174 
175 /*
176  * Configuration options for the geoplace item
177  */
178 
179 static Tk_ConfigSpec configSpecs[] = {
180     {TK_CONFIG_CUSTOM, "-place", NULL, NULL, "", 0,
181 	TK_CONFIG_NULL_OK, &geoPlaceOption},
182     {TK_CONFIG_CUSTOM, "-refpoint", NULL, NULL, "0.0 0.0", 0, 0,
183 	&refPtOption},
184     {TK_CONFIG_CUSTOM, "-projection", NULL, NULL, "", 0, 0,
185 	&projOption},
186     {TK_CONFIG_DOUBLE, "-scale", NULL, NULL, "1.0e-7",
187 	Tk_Offset(PlaceItem, scale), 0, NULL},
188     {TK_CONFIG_COLOR, "-dotcolor", NULL, NULL, "Black",
189 	Tk_Offset(PlaceItem, dotColor), TK_CONFIG_NULL_OK, NULL},
190     {TK_CONFIG_PIXELS, "-dotsize", NULL, NULL, "1",
191 	Tk_Offset(PlaceItem, dotSize), 0, NULL},
192     {TK_CONFIG_BITMAP, "-bitmap", NULL, NULL, "",
193 	Tk_Offset(PlaceItem, bitmap), TK_CONFIG_NULL_OK, NULL},
194     {TK_CONFIG_COLOR, "-bitmapcolor", NULL, NULL, "Black",
195 	Tk_Offset(PlaceItem, bmColor), TK_CONFIG_NULL_OK, NULL},
196     {TK_CONFIG_STRING, "-text", NULL, NULL, "", Tk_Offset(PlaceItem, text),
197 	0, NULL},
198     {TK_CONFIG_COLOR, "-textcolor", NULL, NULL, "Black",
199 	Tk_Offset(PlaceItem, textColor), TK_CONFIG_NULL_OK, NULL},
200     {TK_CONFIG_FONT, "-font", NULL, NULL, "fixed",
201 	Tk_Offset(PlaceItem, font), TK_CONFIG_NULL_OK, NULL},
202     {TK_CONFIG_ANCHOR, "-anchor", NULL, NULL, "center",
203 	Tk_Offset(PlaceItem, textAnchor), 0, NULL},
204     {TK_CONFIG_CUSTOM, "-arrow", NULL, NULL, "0.0 0 0.0", 0, 0,
205 	&arrowOption},
206     {TK_CONFIG_COLOR, "-arrowcolor", NULL, NULL, "Black",
207 	Tk_Offset(PlaceItem, arrowColor), TK_CONFIG_NULL_OK, NULL},
208     {TK_CONFIG_CUSTOM, "-tags", NULL, NULL, NULL, 0, TK_CONFIG_NULL_OK,
209 	&tagsOption},
210     {TK_CONFIG_END,  NULL,  NULL,  NULL, NULL, 0, 0, NULL}
211 };
212 
213 /*
214  * The structures below defines the geoplace item type in terms of
215  * procedures that can be invoked by generic item code.
216  */
217 
218 static Tk_ItemType geoPlaceType = {
219     "geomap_place",
220     sizeof(PlaceItem),
221     createProc,
222     configSpecs,
223     configProc,
224     coordProc,
225     deleteProc,
226     displayProc,
227     TK_CONFIG_OBJS,
228     pointProc,
229     areaProc,
230     postscriptProc,
231     scaleProc,
232     translateProc,
233     (Tk_ItemIndexProc *) NULL,
234     (Tk_ItemCursorProc *) NULL,
235     (Tk_ItemSelectionProc *) NULL,
236     (Tk_ItemInsertProc *) NULL,
237     (Tk_ItemDCharsProc *) NULL,
238     (Tk_ItemType *) NULL,
239 };
240 
241 /*
242  *------------------------------------------------------------------------
243  *
244  * TkgeomapPlaceInit --
245  *
246  *	This procedure adds the geoplace item type to the canvas widget.
247  *	See the user documentation for a description of the its effect
248  *	on Tcl.
249  *
250  * Results:
251  *	A standard Tcl result.
252  *
253  * Side effects:
254  * 	Prerequisite packages are initialized, and the "geoplace" item is
255  * 	added to the Tk canvas interface.
256  *
257  *------------------------------------------------------------------------
258  */
259 
260 int
TkgeomapPlaceInit(interp)261 TkgeomapPlaceInit(interp)
262     Tcl_Interp *interp;			/* Current interpreter */
263 {
264     static int loaded;			/* If true, package is loaded */
265 
266     if (loaded) {
267 	return TCL_OK;
268     }
269 #ifdef USE_TCL_STUBS
270     if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
271 	return TCL_ERROR;
272     }
273 #endif
274 #ifdef USE_TK_STUBS
275     if (Tk_InitStubs(interp, TK_VERSION, 0) == NULL) {
276 	return TCL_ERROR;
277     }
278 #endif
279 
280     /*
281      * Set tagsOption.  This must be done at run time because the stubs
282      * library set the functions to non-static values, so they cannot be
283      * used in an initializer.
284      */
285 
286     tagsOption.parseProc = Tk_CanvasTagsParseProc;
287     tagsOption.printProc = Tk_CanvasTagsPrintProc;
288 
289     Tk_CreateItemType(&geoPlaceType);
290     loaded = 1;
291     return TCL_OK;
292 }
293 
294 /*
295  *--------------------------------------------------------------
296  *
297  * createProc --
298  *
299  *	This procedure is invoked to create a new geoplace
300  *	item in a canvas.
301  *
302  * Results:
303  *	A standard Tcl return value.  If an error occurred in
304  *	creating the item, then an error message is left in
305  *	the interp's result;  in this case itemPtr is left uninitialized,
306  *	so it can be safely freed by the caller.
307  *
308  * Side effects:
309  *	A new geoplace item is created.
310  *
311  *--------------------------------------------------------------
312  */
313 
314 static int
createProc(interp,canvas,itemPtr,objc,objv)315 createProc(interp, canvas, itemPtr, objc, objv)
316     Tcl_Interp *interp;			/* Interpreter for error reporting. */
317     Tk_Canvas canvas;			/* Canvas to hold new item. */
318     Tk_Item *itemPtr;			/* Record to hold new item;  header
319 					 * has been initialized by caller. */
320     int objc;				/* Number of arguments in objv. */
321     Tcl_Obj *CONST objv[];		/* Arguments describing rectangle. */
322 {
323     PlaceItem *plcItemPtr = (PlaceItem *)itemPtr;
324     double x, y;
325 
326 
327     /*
328      * Set location of reference point from coordinates given on command line.
329      */
330 
331     if ((Tk_CanvasGetCoordFromObj(interp, canvas, objv[0], &x) != TCL_OK)
332 	    || (Tk_CanvasGetCoordFromObj(interp, canvas, objv[1], &y)
333 		!= TCL_OK)) {
334 	return TCL_ERROR;
335     }
336     plcItemPtr->xRef = x;
337     plcItemPtr->yRef = y;
338 
339     /*
340      * Initialize values.  Bogus initializers will be set properly
341      * when the item is configured.
342      */
343 
344     plcItemPtr->interp = interp;
345     plcItemPtr->canvas = canvas;
346     plcItemPtr->refPt = GeoPtFmDeg(0.0, 0.0);
347     plcItemPtr->proj = NULL;
348     plcItemPtr->scale = 0.0;
349     plcItemPtr->tclGeoPlace = NULL;
350     plcItemPtr->mapPt = MapPtNowhere();
351     plcItemPtr->updateMap = 1;
352     plcItemPtr->updateCvs = 1;
353     plcItemPtr->dotColor = None;
354     plcItemPtr->dotSize = 0;
355     plcItemPtr->dotGC = None;
356     plcItemPtr->bitmap = None;
357     plcItemPtr->bmColor = None;
358     plcItemPtr->bmGC = None;
359     plcItemPtr->text = CKALLOC(1);
360     strcpy(plcItemPtr->text, "");
361     plcItemPtr->textColor = None;
362     plcItemPtr->font = NULL;
363     plcItemPtr->textAnchor = TK_ANCHOR_CENTER;
364     plcItemPtr->txtGC = None;
365     plcItemPtr->layout = NULL;
366     plcItemPtr->arrowColor = None;
367     plcItemPtr->arrowGC = None;
368 
369     /*
370      * Configure the new geoplace item.
371      */
372 
373     if (configProc(interp, canvas, itemPtr, objc - 2, objv + 2, 0)
374 	    != TCL_OK) {
375 	deleteProc(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
376 	return TCL_ERROR;
377     }
378 
379     return TCL_OK;
380 }
381 
382 /*
383  *--------------------------------------------------------------
384  *
385  * configProc --
386  *
387  *	This procedure is invoked to configure various aspects
388  *	of a geoplace item, such as its anchor position.
389  *
390  * Results:
391  *	A standard Tcl result code.  If an error occurs, then
392  *	an error message is left in the interp's result.
393  *
394  * Side effects:
395  *	Configuration information may be set for itemPtr.
396  *
397  *--------------------------------------------------------------
398  */
399 
400 static int
configProc(interp,canvas,itemPtr,objc,objv,flags)401 configProc(interp, canvas, itemPtr, objc, objv, flags)
402     Tcl_Interp *interp;		/* Used for error reporting. */
403     Tk_Canvas canvas;		/* Canvas containing itemPtr. */
404     Tk_Item *itemPtr;		/* Geoplace item to reconfigure. */
405     int objc;			/* Number of elements in objv.  */
406     Tcl_Obj *CONST objv[];	/* Arguments describing things to configure. */
407     int flags;			/* Flags to pass to Tk_ConfigureWidget. */
408 {
409     PlaceItem *plcItemPtr = (PlaceItem *)itemPtr;
410     Tk_Window tkwin = Tk_CanvasTkwin(canvas);
411     double oldScale;
412     Tk_Anchor oldAnchor;
413     XGCValues gcVals;
414     GC gc;
415 
416     oldScale = plcItemPtr->scale;
417     oldAnchor = plcItemPtr->textAnchor;
418     if (Tk_ConfigureWidget(interp, Tk_CanvasTkwin(canvas),
419 		geoPlaceType.configSpecs, objc, (char **)objv,
420 		(char *)plcItemPtr, flags|TK_CONFIG_OBJS) != TCL_OK) {
421 	return TCL_ERROR;
422     }
423 
424     /*
425      * Update graphics contexts.
426      */
427 
428     if (plcItemPtr->dotSize > 0 && plcItemPtr->dotColor != None) {
429 	gcVals.foreground = plcItemPtr->dotColor->pixel;
430 	gc = Tk_GetGC(tkwin, GCForeground, &gcVals);
431 	if (plcItemPtr->dotGC != None) {
432 	    Tk_FreeGC(Tk_Display(tkwin), plcItemPtr->dotGC);
433 	}
434 	plcItemPtr->dotGC = gc;
435     }
436     if (plcItemPtr->bitmap != None && plcItemPtr->bmColor != None) {
437 	gcVals.foreground = plcItemPtr->bmColor->pixel;
438 	gcVals.clip_mask = plcItemPtr->bitmap;
439 	gc = Tk_GetGC(tkwin, GCForeground|GCClipMask, &gcVals);
440 	if (plcItemPtr->bmGC != None) {
441 	    Tk_FreeGC(Tk_Display(tkwin), plcItemPtr->bmGC);
442 	}
443 	plcItemPtr->bmGC = gc;
444     }
445     if ( (plcItemPtr->textColor != None) && plcItemPtr->font) {
446 	gcVals.foreground = plcItemPtr->textColor->pixel;
447 	gcVals.font = Tk_FontId(plcItemPtr->font);
448 	gc = Tk_GetGC(tkwin, GCForeground|GCFont, &gcVals);
449 	if (plcItemPtr->txtGC != None) {
450 	    Tk_FreeGC(Tk_Display(tkwin), plcItemPtr->txtGC);
451 	}
452 	plcItemPtr->txtGC = gc;
453     }
454     if (plcItemPtr->arrow_len >= 1.0 && plcItemPtr->arrowColor != None) {
455 	gcVals.foreground = plcItemPtr->arrowColor->pixel;
456 	gc = Tk_GetGC(tkwin, GCForeground, &gcVals);
457 	if (plcItemPtr->arrowGC != None) {
458 	    Tk_FreeGC(Tk_Display(tkwin), plcItemPtr->arrowGC);
459 	}
460 	plcItemPtr->arrowGC = gc;
461     }
462 
463     /*
464      * If text has changed, compute new text layout.
465      */
466 
467     if (strlen(plcItemPtr->text) > 0) {
468 	computeTextLayout(plcItemPtr);
469     }
470     updateCvs(plcItemPtr);
471     return TCL_OK;
472 }
473 
474 /*
475  *--------------------------------------------------------------
476  *
477  * coordProc --
478  *
479  *	This procedure is invoked to process the "coords" widget
480  *	command on geoplace items.  See the user documentation for
481  *	details on what it does.
482  *
483  * Results:
484  *	Returns TCL_OK or TCL_ERROR, and sets the interp's result.
485  *
486  * Side effects:
487  *	The coordinates for the given item may be changed.
488  *
489  *--------------------------------------------------------------
490  */
491 
492 static int
coordProc(interp,canvas,itemPtr,objc,objv)493 coordProc(interp, canvas, itemPtr, objc, objv)
494     Tcl_Interp *interp;			/* Used for error reporting. */
495     Tk_Canvas canvas;			/* Canvas containing item. */
496     Tk_Item *itemPtr;			/* Item whose coordinates are to be
497 					 * read or modified. */
498     int objc;				/* Number of coordinates supplied in
499 					 * objv. */
500     Tcl_Obj *CONST objv[];		/* Array of coordinates: x1, y1,
501 					 * x2, y2, ... */
502 {
503     PlaceItem *plcItemPtr = (PlaceItem *)itemPtr;
504     double xRef, yRef;
505 
506     if (objc == 0) {
507 	/*
508 	 * Set result to item coordinates
509 	 */
510 
511 	Tcl_Obj *obj = Tcl_NewObj();
512 	Tcl_Obj *subobj = Tcl_NewDoubleObj(plcItemPtr->xRef);
513 	Tcl_ListObjAppendElement(interp, obj, subobj);
514 	subobj = Tcl_NewDoubleObj(plcItemPtr->yRef);
515 	Tcl_ListObjAppendElement(interp, obj, subobj);
516 	Tcl_SetObjResult(interp, obj);
517     } else if (objc == 1) {
518 	Tcl_Obj *rslt;		/* Result */
519 
520 	if (strcmp(Tcl_GetString(objv[0]), "dump") == 0) {
521 	    /*
522 	     * User wants dump of canvas coordinates for the linearray.
523 	     */
524 
525 	    updateCvs(plcItemPtr);
526 	    rslt = Tcl_NewObj();
527 	    Tcl_ListObjAppendElement(interp, rslt,
528 		    Tcl_NewDoubleObj(plcItemPtr->xPlc));
529 	    Tcl_ListObjAppendElement(interp, rslt,
530 		    Tcl_NewDoubleObj(plcItemPtr->yPlc));
531 	    Tcl_SetObjResult(interp, rslt);
532 	    return TCL_OK;
533 	} else {
534 	    /*
535 	     * New coordinates given on command line in form
536 	     * "pathName coords {x y}"
537 	     */
538 
539 	    Tcl_Obj **coordsPtr;
540 	    int n;
541 
542 	    if (Tcl_ListObjGetElements(interp, objv[0], &n, &coordsPtr)
543 		    != TCL_OK || n != 2) {
544 		Tcl_AppendResult(interp, "Could not split coords list\n", NULL);
545 		return TCL_ERROR;
546 	    }
547 	    if ((Tk_CanvasGetCoordFromObj(interp, canvas, coordsPtr[0],
548 			    &xRef) != TCL_OK)
549 		    || (Tk_CanvasGetCoordFromObj(interp, canvas, coordsPtr[1],
550 			    &yRef) != TCL_OK)) {
551 		return TCL_ERROR;
552 	    }
553 	    plcItemPtr->xRef = xRef;
554 	    plcItemPtr->yRef = yRef;
555 	    plcItemPtr->updateCvs = 1;
556 	    updateCvs(plcItemPtr);
557 	}
558     } else if (objc == 2) {
559 	/*
560 	 * New coordinates given on command line
561 	 */
562 
563 	if ((Tk_CanvasGetCoordFromObj(interp, canvas, objv[0],
564 			&xRef) != TCL_OK)
565 		|| (Tk_CanvasGetCoordFromObj(interp, canvas, objv[1],
566 			&yRef) != TCL_OK)) {
567 	    return TCL_ERROR;
568 	}
569 	plcItemPtr->xRef = xRef;
570 	plcItemPtr->yRef = yRef;
571 	plcItemPtr->updateCvs = 1;
572 	updateCvs(plcItemPtr);
573     } else {
574 	Tcl_AppendResult(interp,
575 		"Coordinates must be specified as \"x y\" or \"{x y}\"", NULL);
576 	return TCL_ERROR;
577     }
578     return TCL_OK;
579 }
580 
581 /*
582  *--------------------------------------------------------------
583  *
584  * deleteProc --
585  *
586  *	This procedure is called to clean up the data structure
587  *	associated with a geoplace item.
588  *
589  * Results:
590  *	None.
591  *
592  * Side effects:
593  *	Resources associated with itemPtr are released.
594  *
595  *--------------------------------------------------------------
596  */
597 
598 static void
deleteProc(canvas,itemPtr,display)599 deleteProc(canvas, itemPtr, display)
600     Tk_Canvas canvas;			/* Info about overall canvas widget. */
601     Tk_Item *itemPtr;			/* Item that is being deleted. */
602     Display *display;			/* Display containing window for
603 					 * canvas. */
604 {
605     PlaceItem *plcItemPtr = (PlaceItem *)itemPtr;
606 
607     Tclgeomap_CnxProjUpdateTask(plcItemPtr->proj, plcItemPtr);
608     Tclgeomap_CnxProjDeleteTask(plcItemPtr->proj, plcItemPtr);
609     Tclgeomap_CnxPlaceUpdateTask(plcItemPtr->tclGeoPlace, plcItemPtr);
610     Tclgeomap_CnxPlaceDeleteTask(plcItemPtr->tclGeoPlace, plcItemPtr);
611     if (plcItemPtr->dotGC != None) {
612 	Tk_FreeGC(display, plcItemPtr->dotGC);
613     }
614     if (plcItemPtr->bmGC != None) {
615 	Tk_FreeGC(display, plcItemPtr->bmGC);
616     }
617     if (plcItemPtr->txtGC != None) {
618 	Tk_FreeGC(display, plcItemPtr->txtGC);
619     }
620     if (plcItemPtr->arrowGC != None) {
621 	Tk_FreeGC(display, plcItemPtr->arrowGC);
622     }
623     Tk_FreeOptions(geoPlaceType.configSpecs, (char *)plcItemPtr,
624 	    display, 0);
625 }
626 
627 /*
628  *--------------------------------------------------------------
629  *
630  * forgetPlace --
631  *
632  * 	This procedure adjusts a place item if the place it is displaying
633  * 	is deleted.
634  *
635  * Results:
636  * 	None.
637  *
638  * Side effects:
639  * 	The item is removed from the display, although it continues to exist.
640  *
641  *--------------------------------------------------------------
642  */
643 
644 static void
forgetPlace(clientData)645 forgetPlace(clientData)
646     ClientData clientData;	/* Geoplace item */
647 {
648     PlaceItem *plcItemPtr = (PlaceItem *)clientData;
649     Tk_Canvas canvas = plcItemPtr->canvas;
650     Tk_Window tkwin = Tk_CanvasTkwin(canvas);
651     Display *display = Tk_Display(tkwin);
652 
653     plcItemPtr->tclGeoPlace = NULL;
654     plcItemPtr->mapPt = MapPtNowhere();
655     plcItemPtr->updateMap = 0;
656     if (plcItemPtr->dotGC != None) {
657 	Tk_FreeGC(display, plcItemPtr->dotGC);
658     }
659     plcItemPtr->dotGC = None;
660     if (plcItemPtr->bmGC != None) {
661 	Tk_FreeGC(display, plcItemPtr->bmGC);
662     }
663     plcItemPtr->bmGC = None;
664     if (plcItemPtr->txtGC != None) {
665 	Tk_FreeGC(display, plcItemPtr->txtGC);
666     }
667     plcItemPtr->txtGC = None;
668     if (plcItemPtr->arrowGC != None) {
669 	Tk_FreeGC(display, plcItemPtr->arrowGC);
670     }
671     plcItemPtr->arrowGC = None;
672     hide(plcItemPtr);
673 }
674 
675 /*
676  *--------------------------------------------------------------
677  *
678  * forgetProj --
679  *
680  * 	This procedure adjusts a place item if the projection it is using
681  * 	is deleted.
682  *
683  * Results:
684  * 	None.
685  *
686  * Side effects:
687  * 	The item is removed from the display, although it continues to exist.
688  *
689  *--------------------------------------------------------------
690  */
691 
692 static void
forgetProj(clientData)693 forgetProj(clientData)
694     ClientData clientData;	/* Geoplace item */
695 {
696     PlaceItem *plcItemPtr = (PlaceItem *)clientData;
697 
698     plcItemPtr->proj = NULL;
699     plcItemPtr->mapPt = MapPtNowhere();
700     plcItemPtr->updateMap = 0;
701     hide(plcItemPtr);
702 }
703 
704 /*
705  *--------------------------------------------------------------
706  *
707  * hide --
708  *
709  * 	This procedure removes a geoplace from a display, although the geoplace
710  * 	item continues to exist.
711  *
712  * Results:
713  * 	None.
714  *
715  * Side effects:
716  * 	None.
717  *
718  *--------------------------------------------------------------
719  */
720 
721 static void
hide(plcItemPtr)722 hide(plcItemPtr)
723     PlaceItem *plcItemPtr;	/* Place to remove from the display */
724 {
725     Tk_CanvasEventuallyRedraw(plcItemPtr->canvas,
726 	    plcItemPtr->header.x1, plcItemPtr->header.y1,
727 	    plcItemPtr->header.x2, plcItemPtr->header.y2);
728     plcItemPtr->header.x1 = plcItemPtr->header.y1 = INT_MIN;
729     plcItemPtr->header.x2 = plcItemPtr->header.y2 = INT_MIN + 1;
730     plcItemPtr->updateCvs = 0;
731 }
732 
733 /*
734  *--------------------------------------------------------------
735  *
736  * displayProc --
737  *
738  *	This procedure is invoked to draw a geoplace item in a given
739  *	drawable.
740  *
741  * Results:
742  *	None.
743  *
744  * Side effects:
745  *	ItemPtr is drawn in drawable using the transformation
746  *	information in canvas.
747  *
748  *--------------------------------------------------------------
749  */
750 
751 static void
displayProc(canvas,itemPtr,display,drawable,x,y,width,height)752 displayProc(canvas, itemPtr, display, drawable, x, y, width, height)
753     Tk_Canvas canvas;			/* Canvas that contains item. */
754     Tk_Item *itemPtr;			/* Item to be displayed. */
755     Display *display;			/* Display on which to draw item. */
756     Drawable drawable;			/* Pixmap or window in which to draw
757 					 * item. */
758     int x, y, width, height;		/* Describes region of canvas that
759 					 * must be redisplayed (not used). */
760 {
761     PlaceItem *plcItemPtr = (PlaceItem *)itemPtr;
762     Tk_Window tkwin = Tk_CanvasTkwin(canvas);
763     int dotSize;
764     short dwX, dwY;			/* Drawable (X) coordinates */
765     int spShift;			/* Amount to shift to center large
766 					 * dot or bitmap */
767 
768     if (!tkwin || !Tk_IsMapped(tkwin) || !plcItemPtr->tclGeoPlace) {
769 	return;
770     }
771     if (plcItemPtr->updateMap) {
772 	updateMap(plcItemPtr);
773     }
774     if (MapPtIsNowhere(plcItemPtr->mapPt)) {
775 	return;
776     }
777     if (plcItemPtr->updateCvs) {
778 	updateCvs(plcItemPtr);
779     }
780     Tk_CanvasDrawableCoords(canvas, plcItemPtr->xPlc, plcItemPtr->yPlc,
781 	    &dwX, &dwY);
782 
783     /*
784      * Draw dot.
785      */
786 
787     if (plcItemPtr->dotSize > 0 && plcItemPtr->dotColor != None) {
788 	dotSize = plcItemPtr->dotSize;
789 	if (dotSize == 1) {
790 	    XDrawPoint(display, drawable, plcItemPtr->dotGC, dwX, dwY);
791 	} else {
792 	    spShift = dotSize / 2;
793 	    XFillArc(display, drawable, plcItemPtr->dotGC,
794 		    dwX - spShift, dwY - spShift,
795 		    (unsigned)dotSize, (unsigned)dotSize, 0, 23040);
796 	}
797     }
798 
799     /*
800      * Draw bitmap.
801      */
802 
803     if (plcItemPtr->bitmap != None && plcItemPtr->bmColor != None) {
804 	Pixmap bitmap = plcItemPtr->bitmap;
805 	int bmWidth, bmHeight;
806 
807 	Tk_SizeOfBitmap(display, bitmap, &bmWidth, &bmHeight);
808 	XSetClipOrigin(display, plcItemPtr->bmGC,
809 		dwX - bmWidth / 2, dwY - bmHeight / 2);
810 	XCopyPlane(display, bitmap, drawable, plcItemPtr->bmGC, 0, 0,
811 		(unsigned)bmWidth, (unsigned)bmHeight,
812 		dwX -bmWidth / 2, dwY -bmHeight / 2, 0x01);
813 	XSetClipOrigin(display, plcItemPtr->bmGC, 0, 0);
814     }
815 
816     /*
817      * Draw text.
818      */
819 
820     if ( (strlen(plcItemPtr->text) > 0)
821 	    && (plcItemPtr->textColor != None)
822 	    && plcItemPtr->font
823 	    && plcItemPtr->layout ) {
824 	Tk_DrawTextLayout(display, drawable, plcItemPtr->txtGC,
825 		plcItemPtr->layout,
826 		dwX + plcItemPtr->xTxt, dwY + plcItemPtr->yTxt, 0, -1);
827     }
828 
829     /*
830      * Draw arrow.
831      */
832 
833     if (plcItemPtr->arrow_len >= 1.0 && plcItemPtr->arrowColor != None) {
834 	XPoint xPtsPtr[2];
835 
836 	/*
837 	 * Coordinates of the arrow head
838 	 */
839 
840 	xPtsPtr[0].x = dwX + plcItemPtr->arrow_head_x;
841 	xPtsPtr[0].y = dwY + plcItemPtr->arrow_head_y;
842 
843 	/*
844 	 * Draw the shaft
845 	 */
846 
847 	xPtsPtr[1].x = dwX + plcItemPtr->arrow_base_x;
848 	xPtsPtr[1].y = dwY + plcItemPtr->arrow_base_y;
849 	XDrawLines(display, drawable, plcItemPtr->arrowGC, xPtsPtr, 2,
850 		CoordModeOrigin);
851 
852 	/*
853 	 * Draw the left side of the arrow head
854 	 */
855 
856 	xPtsPtr[1].x = xPtsPtr[0].x + plcItemPtr->arrow_ltip_x;
857 	xPtsPtr[1].y = xPtsPtr[0].y + plcItemPtr->arrow_ltip_y;
858 	XDrawLines(display, drawable, plcItemPtr->arrowGC, xPtsPtr, 2,
859 		CoordModeOrigin);
860 
861 	/*
862 	 * Draw the right side of the arrow head
863 	 */
864 
865 	xPtsPtr[1].x = xPtsPtr[0].x + plcItemPtr->arrow_rtip_x;
866 	xPtsPtr[1].y = xPtsPtr[0].y + plcItemPtr->arrow_rtip_y;
867 	XDrawLines(display, drawable, plcItemPtr->arrowGC, xPtsPtr, 2,
868 		CoordModeOrigin);
869     }
870 }
871 
872 /*
873  *--------------------------------------------------------------
874  *
875  * pointProc --
876  *
877  *	Computes the distance from a given point to a given
878  *	geoplace item, in canvas units.
879  *
880  * Results:
881  *	The return value is 0.0 if the point whose x and y coordinates
882  *	are coordPtr[0] and coordPtr[1] is inside the geoplace item.  If the
883  *	point isn't inside the geoplace item then the return value is the
884  *	distance from the point to the geoplace.
885  *
886  * Side effects:
887  *	None.
888  *
889  *--------------------------------------------------------------
890  */
891 
892 static double
pointProc(canvas,itemPtr,pointPtr)893 pointProc(canvas, itemPtr, pointPtr)
894     Tk_Canvas canvas;			/* Canvas containing item. */
895     Tk_Item *itemPtr;			/* Item to check against point. */
896     double *pointPtr;			/* Pointer to x and y coordinates. */
897 {
898     PlaceItem *plcItemPtr = (PlaceItem *)itemPtr;
899     double x = pointPtr[0], y = pointPtr[1];
900 
901     if (plcItemPtr->updateCvs) {
902 	updateCvs(plcItemPtr);
903     }
904     return hypot(plcItemPtr->xPlc - x, plcItemPtr->yPlc - y);
905 }
906 
907 /*
908  *--------------------------------------------------------------
909  *
910  * areaProc --
911  *
912  *	This procedure is called to determine whether an item
913  *	lies entirely inside, entirely outside, or overlapping
914  *	a given rectangle.
915  *
916  * Results:
917  *	-1 is returned if the item is entirely outside the area
918  *	given by rectPtr, 0 if it overlaps, and 1 if it is entirely
919  *	inside the given area.
920  *
921  * Side effects:
922  *	None.
923  *
924  *--------------------------------------------------------------
925  */
926 
927 static int
areaProc(canvas,itemPtr,rectPtr)928 areaProc(canvas, itemPtr, rectPtr)
929     Tk_Canvas canvas;		/* Canvas containing item. */
930     Tk_Item *itemPtr;		/* Item to check against rectangle. */
931     double *rectPtr;		/* Pointer to array of four coordinates
932 				 * (x1, y1, x2, y2) describing rectangular
933 				 * area.  */
934 {
935     PlaceItem *plcItemPtr = (PlaceItem *)itemPtr;
936     double x1i, y1i, x2i, y2i;	/* Limits of item */
937     double x1r, y1r, x2r, y2r;	/* Limits of rectPtr rectangle */
938 
939     if (plcItemPtr->updateCvs) {
940 	updateCvs(plcItemPtr);
941     }
942     x1i = itemPtr->x1;
943     y1i = itemPtr->y1;
944     x2i = itemPtr->x2;
945     y2i = itemPtr->y2;
946     x1r = rectPtr[0];
947     y1r = rectPtr[1];
948     x2r = rectPtr[2];
949     y2r = rectPtr[3];
950     if (x1i > x2r || x2i < x1r || y1i > y2r || y2i < y1r) {
951 	return -1;
952     } else if (x1i > x1r && x2i < x2r && y1i > y1r && y2i < y2r) {
953 	return 1;
954     } else {
955 	return 0;
956     }
957 }
958 
959 /*
960  *--------------------------------------------------------------
961  *
962  * postscriptProc --
963  *
964  *	This procedure is called to generate Postscript for
965  *	geoplace items.
966  *
967  * Results:
968  *	The return value is a standard Tcl result.  If an error
969  *	occurs in generating Postscript then an error message is
970  *	left in interp->result, replacing whatever used to be there.
971  *	If no error occurs, then Postscript for the item is appended
972  *	to the result.
973  *
974  * Side effects:
975  *	None.
976  *
977  *--------------------------------------------------------------
978  */
979 
980 static int
postscriptProc(interp,canvas,itemPtr,prepass)981 postscriptProc(interp, canvas, itemPtr, prepass)
982     Tcl_Interp *interp;			/* Leave Postscript or error message
983 					 * here. */
984     Tk_Canvas canvas;			/* Information about overall canvas. */
985     Tk_Item *itemPtr;			/* Item for which Postscript is
986 					 * wanted. */
987     int prepass;			/* 1 means this is a prepass to
988 					 * collect font information;  0 means
989 					 * final Postscript is being created. */
990 {
991     PlaceItem *plcItemPtr;		/* Current geoplace item */
992     Tk_Window tkwin = Tk_CanvasTkwin(canvas);
993     char bytes[200];			/* Printing space */
994     double psX, psY;			/* Postscript coordinates */
995     double cnr[8];			/* 4 corner points in canvas coord's */
996 
997     plcItemPtr = (PlaceItem *)itemPtr;
998     if ( !plcItemPtr->tclGeoPlace ) {
999 	return TCL_OK;
1000     } else {
1001 	Tcl_AppendResult(interp, "% Drawing place ",
1002 		Tclgeomap_PlaceName(plcItemPtr->tclGeoPlace), "\n", NULL);
1003     }
1004     if (plcItemPtr->updateMap) {
1005 	updateMap(plcItemPtr);
1006     }
1007     if (MapPtIsNowhere(plcItemPtr->mapPt)) {
1008 	return TCL_OK;
1009     }
1010     if (plcItemPtr->updateCvs) {
1011 	updateCvs(plcItemPtr);
1012     }
1013     psX = plcItemPtr->xPlc;
1014     psY = Tk_CanvasPsY(canvas, plcItemPtr->yPlc);
1015 
1016     /*
1017      * Create clipping region for item
1018      */
1019 
1020     Tcl_AppendResult(interp, "%% Create clipping region for item\n", NULL);
1021     cnr[0] = 0;
1022     cnr[1] = 0;
1023     cnr[2] = Tk_Width(tkwin);
1024     cnr[3] = 0;
1025     cnr[4] = Tk_Width(tkwin);
1026     cnr[5] = Tk_Height(tkwin);
1027     cnr[6] = 0;
1028     cnr[7] = Tk_Height(tkwin);
1029     Tk_CanvasPsPath(interp, canvas, cnr, 4);
1030     Tcl_AppendResult(interp, "closepath clip\n", NULL);
1031 
1032     /*
1033      * Draw dot.
1034      */
1035 
1036     if (plcItemPtr->dotSize > 0 && plcItemPtr->dotColor != None) {
1037 	Tk_CanvasPsColor(interp, canvas, plcItemPtr->dotColor);
1038 	sprintf(bytes, "newpath %f %f %f", psX, psY, 0.5 * plcItemPtr->dotSize);
1039 	Tcl_AppendResult(interp, bytes, " 0 360 arc fill\n", NULL);
1040     }
1041 
1042     /*
1043      * Draw bitmap.
1044      */
1045 
1046     if (plcItemPtr->bitmap != None && plcItemPtr->bmColor != None) {
1047 	int bmWidth, bmHeight;
1048 
1049 	Tcl_AppendResult(interp, "gsave\n", NULL);
1050 	Tk_CanvasPsColor(interp, canvas, plcItemPtr->bmColor);
1051 	Tk_SizeOfBitmap(Tk_Display(Tk_CanvasTkwin(canvas)),
1052 		plcItemPtr->bitmap, &bmWidth, &bmHeight);
1053 	sprintf(bytes, "%f %f translate\n",
1054 		psX - 0.5 * bmWidth, psY - 0.5 * bmHeight);
1055 	Tcl_AppendResult(interp, bytes, NULL);
1056 	sprintf(bytes, "%d %d scale\n", bmWidth, bmHeight);
1057 	Tcl_AppendResult(interp, bytes, NULL);
1058 	sprintf(bytes, "%d %d true [%d 0 0 %d 0 0]\n",
1059 		bmWidth, bmHeight, bmWidth, bmHeight);
1060 	Tcl_AppendResult(interp, bytes, NULL);
1061 	Tk_CanvasPsBitmap(interp, canvas, plcItemPtr->bitmap,
1062 		0, 0, bmWidth, bmHeight);
1063 	Tcl_AppendResult(interp, " imagemask\ngrestore\n\n", NULL);
1064     }
1065 
1066     /*
1067      * Draw text.
1068      */
1069 
1070     if (plcItemPtr->text && strcmp(plcItemPtr->text, "") != 0
1071 	    && (plcItemPtr->textColor != None) && plcItemPtr->font
1072 	    && plcItemPtr->layout) {
1073 	int xOffset = 0, yOffset = 0;
1074 	Tk_FontMetrics fm;
1075 	double justify = 0.0;
1076 
1077 	Tcl_AppendResult(interp, "gsave\n", NULL);
1078 	if (Tk_CanvasPsColor(interp, canvas, plcItemPtr->textColor) != TCL_OK
1079 		|| Tk_CanvasPsFont(interp, canvas, plcItemPtr->font)
1080 		!= TCL_OK) {
1081 	    return TCL_ERROR;
1082 	}
1083 	switch (plcItemPtr->textAnchor) {
1084 	    case TK_ANCHOR_NW    : xOffset = 0;
1085 				   yOffset = 0;
1086 				   justify = 0.0;
1087 				   break;
1088 	    case TK_ANCHOR_N     : xOffset = 1;
1089 				   yOffset = 0;
1090 				   justify = 0.5;
1091 				   break;
1092 	    case TK_ANCHOR_NE    : xOffset = 2;
1093 				   yOffset = 0;
1094 				   justify = 1.0;
1095 				   break;
1096 	    case TK_ANCHOR_E     : xOffset = 2;
1097 				   yOffset = 1;
1098 				   justify = 1.0;
1099 				   break;
1100 	    case TK_ANCHOR_SE    : xOffset = 2;
1101 				   yOffset = 2;
1102 				   justify = 1.0;
1103 				   break;
1104 	    case TK_ANCHOR_S     : xOffset = 1;
1105 				   yOffset = 2;
1106 				   justify = 0.5;
1107 				   break;
1108 	    case TK_ANCHOR_SW    : xOffset = 0;
1109 				   yOffset = 2;
1110 				   justify = 0.0;
1111 				   break;
1112 	    case TK_ANCHOR_W	 : xOffset = 0;
1113 				   yOffset = 1;
1114 				   justify = 0.0;
1115 				   break;
1116 	    case TK_ANCHOR_CENTER: xOffset = 1;
1117 				   yOffset = 1;
1118 				   justify = 0.5;
1119 				   break;
1120 	}
1121 	sprintf(bytes, "%.15g %.15g [\n", psX, psY);
1122 	Tcl_AppendResult(interp, bytes, NULL);
1123 	Tk_TextLayoutToPostscript(interp, plcItemPtr->layout);
1124 	Tk_GetFontMetrics(plcItemPtr->font, &fm);
1125 	sprintf(bytes, "] %d %g %g %g false DrawText\ngrestore\n",
1126 		fm.linespace, xOffset / -2.0, yOffset / 2.0, justify);
1127 	Tcl_AppendResult(interp, bytes, NULL);
1128     }
1129 
1130     return TCL_OK;
1131 }
1132 
1133 /*
1134  *--------------------------------------------------------------
1135  *
1136  * scaleProc --
1137  *
1138  *	This function rescales a geoplace item.  It should not be confused
1139  *	with the "-scale" configuration option, which sets the cartographic
1140  *	scale of the map containing the item.
1141  *
1142  * Results:
1143  *	None.
1144  *
1145  * Side effects:
1146  * 	The reference point for a geoplace item is moved according to the
1147  * 	transformation:
1148  *		xRef' = originX + scaleX*(xRef-originX)
1149  *		yRef' = originY + scaleY*(yRef-originY)
1150  *	The geoplace may or may not move.
1151  *
1152  *--------------------------------------------------------------
1153  */
1154 
1155 static void
scaleProc(canvas,itemPtr,originX,originY,scaleX,scaleY)1156 scaleProc(canvas, itemPtr, originX, originY, scaleX, scaleY)
1157     Tk_Canvas canvas;			/* Canvas containing rectangle. */
1158     Tk_Item *itemPtr;			/* Rectangle to be scaled. */
1159     double originX, originY;		/* Origin about which to scale rect. */
1160     double scaleX;			/* Amount to scale in X direction. */
1161     double scaleY;			/* Amount to scale in Y direction. */
1162 {
1163     PlaceItem *plcItemPtr = (PlaceItem *) itemPtr;
1164 
1165     plcItemPtr->xRef = originX + scaleX * (plcItemPtr->xRef - originX);
1166     plcItemPtr->yRef = originY + scaleY * (plcItemPtr->yRef - originY);
1167     updateCvs(plcItemPtr);
1168     return;
1169 }
1170 
1171 /*
1172  *--------------------------------------------------------------
1173  *
1174  * translateProc --
1175  *
1176  *	This procedure is called to move an item by a given amount.
1177  *
1178  * Results:
1179  *	None.
1180  *
1181  * Side effects:
1182  *	The position of the item is offset by (xDelta, yDelta), and
1183  *	the bounding box is updated in the generic part of the item
1184  *	structure.
1185  *
1186  *--------------------------------------------------------------
1187  */
1188 
1189 static void
translateProc(canvas,itemPtr,deltaX,deltaY)1190 translateProc(canvas, itemPtr, deltaX, deltaY)
1191     Tk_Canvas canvas;			/* Canvas containing item. */
1192     Tk_Item *itemPtr;			/* Item that is being moved. */
1193     double deltaX, deltaY;		/* Amount by which item is to be
1194 					 * moved. */
1195 {
1196     PlaceItem *plcItemPtr = (PlaceItem *) itemPtr;
1197 
1198     plcItemPtr->xRef += deltaX;
1199     plcItemPtr->yRef += deltaY;
1200     updateCvs(plcItemPtr);
1201 }
1202 
1203 /*
1204  *------------------------------------------------------------------------
1205  *
1206  * parseGeoPlaceOption --
1207  *
1208  *	This procedure is invoked during option processing to handle
1209  *	the "-geoplace" option.
1210  *
1211  * Results:
1212  *	A standard Tcl result.
1213  *
1214  * Side effects:
1215  *	The state for a given item gets replaced by the state
1216  *	indicated in the value argument.
1217  *
1218  *------------------------------------------------------------------------
1219  */
1220 
1221 static int
parseGeoPlaceOption(clientData,interp,tkwin,value,widgRec,offset)1222 parseGeoPlaceOption(clientData, interp, tkwin, value, widgRec, offset)
1223     ClientData clientData;	/* Not used */
1224     Tcl_Interp *interp;		/* Current interpreter */
1225     Tk_Window tkwin;		/* Window containing the geomap item */
1226     char *value;		/* Value of the option */
1227     char *widgRec;		/* Pointer to item record */
1228     int offset;			/* Offset into widgRec */
1229 {
1230     PlaceItem *plcItemPtr = (PlaceItem *)(widgRec + offset);
1231     char *plcNm = value;
1232     Tclgeomap_Place tclGeoPlace;
1233 
1234     if ( strlen(value) > 0 ) {
1235 	/*
1236 	 * User has requested that a geoplace be displayed in the item.
1237 	 */
1238 
1239 	tclGeoPlace = Tclgeomap_GetPlace(interp, plcNm);
1240 	if ( !tclGeoPlace ) {
1241 	    Tcl_AppendResult(interp, "No geoplace named ", value, NULL);
1242 	    return TCL_ERROR;
1243 	}
1244 	if ( tclGeoPlace == plcItemPtr->tclGeoPlace ) {
1245 	    return TCL_OK;
1246 	}
1247 	Tclgeomap_CnxPlaceDeleteTask(plcItemPtr->tclGeoPlace, plcItemPtr);
1248 	plcItemPtr->mapPt = MapPtNowhere();
1249 	plcItemPtr->updateMap = 1;
1250 	Tclgeomap_AddPlaceUpdateTask(tclGeoPlace, geoPlaceUpdate, plcItemPtr);
1251 	Tclgeomap_AddPlaceDeleteTask(tclGeoPlace, forgetPlace, plcItemPtr);
1252 	plcItemPtr->tclGeoPlace = tclGeoPlace;
1253     } else {
1254 	/*
1255 	 * User has requested that NO geoplace be displayed in the item.
1256 	 */
1257 
1258 	if ( plcItemPtr->tclGeoPlace ) {
1259 	    Tclgeomap_CnxPlaceUpdateTask(plcItemPtr->tclGeoPlace, plcItemPtr);
1260 	    Tclgeomap_CnxPlaceDeleteTask(plcItemPtr->tclGeoPlace, plcItemPtr);
1261 	    plcItemPtr->mapPt = MapPtNowhere();
1262 	    plcItemPtr->updateMap = 0;
1263 	}
1264 	plcItemPtr->tclGeoPlace = NULL;
1265     }
1266     return TCL_OK;
1267 }
1268 
1269 /*
1270  *------------------------------------------------------------------------
1271  *
1272  * printGeoPlaceOption --
1273  *
1274  *	This procedure is invoked by the Tk configuration code
1275  *	to produce a printable string for the "-refpoint" configuration
1276  *	option.
1277  *
1278  * Results:
1279  *	The return value is a string describing the state for
1280  *	the item referred to by "widgRec".  In addition, *freeProcPtr
1281  *	is filled in with the address of a procedure to call to free
1282  *	the result string when it's no longer needed.
1283  *
1284  * Side effects:
1285  * 	None.
1286  *
1287  *------------------------------------------------------------------------
1288  */
1289 
1290 static char *
printGeoPlaceOption(clientData,tkwin,widgRec,offset,freeProc)1291 printGeoPlaceOption(clientData, tkwin, widgRec, offset, freeProc)
1292     ClientData clientData;	/* Not used */
1293     Tk_Window tkwin;		/* Window containing the geomap item */
1294     char *widgRec;		/* Pointer to item record */
1295     int offset;			/* Offset into widgRec */
1296     Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
1297 				 * information about how to reclaim
1298 				 * storage for return string. */
1299 {
1300     PlaceItem *plcItemPtr = (PlaceItem *)(widgRec + offset);
1301     CONST char *plcNm;
1302     char *rtn;
1303 
1304     plcNm = plcItemPtr->tclGeoPlace
1305 	?  Tclgeomap_PlaceName(plcItemPtr->tclGeoPlace)
1306 	: "";
1307     rtn = Tcl_Alloc(strlen(plcNm) + 1);
1308     strcpy(rtn, plcNm);
1309     *freeProc = Tcl_Free;
1310     return rtn;
1311 }
1312 
1313 /*
1314  *------------------------------------------------------------------------
1315  *
1316  * geoplaceUpdate --
1317  *
1318  * 	This function redraws a place.  It should be called when a place moves.
1319  *
1320  * Results:
1321  * 	None.
1322  *
1323  * Side effects:
1324  * 	Part of a canvas may be redrawn.
1325  *
1326  *------------------------------------------------------------------------
1327  */
1328 
1329 static void
geoPlaceUpdate(clientData)1330 geoPlaceUpdate(clientData)
1331     ClientData clientData;	/* Item displaying the place */
1332 {
1333     PlaceItem *plcItemPtr = (PlaceItem *)clientData;
1334     updateMap(plcItemPtr);
1335     updateCvs(plcItemPtr);
1336 }
1337 
1338 /*
1339  *------------------------------------------------------------------------
1340  *
1341  * parseRefPtOption --
1342  *
1343  *	This procedure is invoked during option processing to handle
1344  *	the "-refpoint" option.
1345  *
1346  * Results:
1347  *	A standard Tcl result.
1348  *
1349  * Side effects:
1350  *	The state for a given item gets replaced by the state
1351  *	indicated in the value argument.
1352  *
1353  *------------------------------------------------------------------------
1354  */
1355 
1356 static int
parseRefPtOption(clientData,interp,tkwin,value,widgRec,offset)1357 parseRefPtOption(clientData, interp, tkwin, value, widgRec, offset)
1358     ClientData clientData;	/* Not used */
1359     Tcl_Interp *interp;		/* Current interpreter */
1360     Tk_Window tkwin;		/* Window containing the geomap item */
1361     char *value;		/* Value of the option */
1362     char *widgRec;		/* Pointer to item record */
1363     int offset;			/* Offset into widgRec */
1364 {
1365 
1366     PlaceItem *plcItemPtr = (PlaceItem *)(widgRec + offset);
1367     double lat, lon;
1368 
1369     if (sscanf(value, "%lf %lf", &lat, &lon) == 2) {
1370 	plcItemPtr->refPt = GwchLonPt(GeoPtFmDeg(lat, lon));
1371 	plcItemPtr->updateMap = plcItemPtr->updateCvs = 1;
1372 	return TCL_OK;
1373     } else {
1374 	Tcl_AppendResult(interp, "Expected {lat lon}, got ", value, NULL);
1375 	return TCL_ERROR;
1376     }
1377 }
1378 
1379 /*
1380  *------------------------------------------------------------------------
1381  *
1382  * printRefPtOption --
1383  *
1384  *	This procedure is invoked by the Tk configuration code
1385  *	to produce a printable string for the "-refpoint" configuration
1386  *	option.
1387  *
1388  * Results:
1389  *	The return value is a string describing the state for
1390  *	the item referred to by "widgRec".  In addition, *freeProcPtr
1391  *	is filled in with the address of a procedure to call to free
1392  *	the result string when it's no longer needed.
1393  *
1394  * Side effects:
1395  * 	None.
1396  *
1397  *------------------------------------------------------------------------
1398  */
1399 
1400 static char *
printRefPtOption(clientData,tkwin,widgRec,offset,freeProc)1401 printRefPtOption(clientData, tkwin, widgRec, offset, freeProc)
1402     ClientData clientData;	/* Not used */
1403     Tk_Window tkwin;		/* Window containing the geomap item */
1404     char *widgRec;		/* Pointer to item record */
1405     int offset;			/* Offset into widgRec */
1406     Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
1407 				 * information about how to reclaim
1408 				 * storage for return string. */
1409 {
1410     PlaceItem *plcItemPtr = (PlaceItem *)(widgRec + offset);
1411     char lat[TCL_DOUBLE_SPACE], lon[TCL_DOUBLE_SPACE];
1412     char *coordPtr[2];
1413     double dLat, dLon;
1414 
1415     coordPtr[0] = lat;
1416     coordPtr[1] = lon;
1417     *freeProc = Tcl_Free;
1418     GeoPtGetDeg(plcItemPtr->refPt, &dLat, &dLon);
1419     Tcl_PrintDouble(NULL, dLat, lat);
1420     Tcl_PrintDouble(NULL, dLon, lon);
1421     return Tcl_Merge(2, coordPtr);
1422 }
1423 
1424 /*
1425  *------------------------------------------------------------------------
1426  *
1427  * parseProjOption --
1428  *
1429  *	This procedure is invoked during option processing to handle
1430  *	the "-projection" option.
1431  *
1432  * Results:
1433  *	A standard Tcl result.
1434  *
1435  * Side effects:
1436  *	The state for a given item gets replaced by the state
1437  *	indicated in the value argument.
1438  *
1439  *------------------------------------------------------------------------
1440  */
1441 
1442 static int
parseProjOption(clientData,interp,tkwin,value,widgRec,offset)1443 parseProjOption(clientData, interp, tkwin, value, widgRec, offset)
1444     ClientData clientData;	/* Not used */
1445     Tcl_Interp *interp;		/* Current interpreter */
1446     Tk_Window tkwin;		/* Window containing the geomap item */
1447     char *value;		/* Value of the option */
1448     char *widgRec;		/* Pointer to item record */
1449     int offset;			/* Offset into widgRec */
1450 {
1451     PlaceItem *plcItemPtr = (PlaceItem *)(widgRec + offset);
1452     Tclgeomap_Proj proj;
1453 
1454     if ( !value || (strcmp(value, "") == 0) ) {
1455 	if (plcItemPtr->proj) {
1456 	    Tclgeomap_CnxProjUpdateTask(plcItemPtr->proj, plcItemPtr);
1457 	    Tclgeomap_CnxProjDeleteTask(plcItemPtr->proj, plcItemPtr);
1458 	}
1459 	plcItemPtr->proj = NULL;
1460 	return TCL_OK;
1461     }
1462     if ( !(proj = Tclgeomap_GetProj(interp, value)) ) {
1463 	Tcl_AppendResult(interp, "No projection named ", value, NULL);
1464 	return TCL_ERROR;
1465     }
1466     if (plcItemPtr->proj) {
1467 	Tclgeomap_CnxProjUpdateTask(plcItemPtr->proj, plcItemPtr);
1468 	Tclgeomap_CnxProjDeleteTask(plcItemPtr->proj, plcItemPtr);
1469     }
1470     plcItemPtr->proj = proj;
1471     Tclgeomap_AddProjUpdateTask(plcItemPtr->proj, geoPlaceUpdate, plcItemPtr);
1472     Tclgeomap_AddProjDeleteTask(plcItemPtr->proj, forgetProj, plcItemPtr);
1473     plcItemPtr->updateMap = 1;
1474     return TCL_OK;
1475 }
1476 
1477 /*
1478  *------------------------------------------------------------------------
1479  *
1480  * printProjOption --
1481  *
1482  *	This procedure is invoked by the Tk configuration code
1483  *	to produce a printable string for the "-projection" configuration
1484  *	option.
1485  *
1486  * Results:
1487  *	The return value is a string describing the state for
1488  *	the item referred to by "widgRec".  In addition, *freeProcPtr
1489  *	is filled in with the address of a procedure to call to free
1490  *	the result string when it's no longer needed.
1491  *
1492  * Side effects:
1493  * 	None.
1494  *
1495  *------------------------------------------------------------------------
1496  */
1497 
1498 static char *
printProjOption(clientData,tkwin,widgRec,offset,freeProc)1499 printProjOption(clientData, tkwin, widgRec, offset, freeProc)
1500     ClientData clientData;	/* Not used */
1501     Tk_Window tkwin;		/* Window containing the geomap item */
1502     char *widgRec;		/* Pointer to item record */
1503     int offset;			/* Offset into widgRec */
1504     Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
1505 				 * information about how to reclaim
1506 				 * storage for return string. */
1507 {
1508     PlaceItem *plcItemPtr = (PlaceItem *)(widgRec + offset);
1509     CONST char *projNm;
1510     char *rtn;
1511 
1512     projNm = plcItemPtr->proj ? Tclgeomap_ProjName(plcItemPtr->proj) : "";
1513     rtn = Tcl_Alloc(strlen(projNm) + 1);
1514     strcpy(rtn, projNm);
1515     *freeProc = Tcl_Free;
1516     return rtn;
1517 }
1518 
1519 /*
1520  *--------------------------------------------------------------
1521  *
1522  * computeTextLayout --
1523  *
1524  * 	This procedure computes the text layout for a geoplace item.
1525  *
1526  * Results:
1527  * 	The text related fields in a geoplace item pointer are updated.
1528  * Side effects:
1529  * 	None.
1530  *
1531  *--------------------------------------------------------------
1532  */
1533 
1534 static void
computeTextLayout(plcItemPtr)1535 computeTextLayout(plcItemPtr)
1536     PlaceItem *plcItemPtr;		/* Geoplace item */
1537 {
1538     int txtWidth, txtHeight;
1539     Tk_TextLayout layout = NULL;
1540 
1541     if ( !plcItemPtr ) {
1542 	return;
1543     }
1544     if (plcItemPtr->layout) {
1545 	Tk_FreeTextLayout(plcItemPtr->layout);
1546     }
1547     if ( !plcItemPtr->text || !plcItemPtr->font ) {
1548 	plcItemPtr->layout = NULL;
1549 	return;
1550     }
1551     switch (plcItemPtr->textAnchor) {
1552 	case TK_ANCHOR_N:
1553 	    layout = Tk_ComputeTextLayout(plcItemPtr->font, plcItemPtr->text,
1554 		    -1, -1, TK_JUSTIFY_CENTER, 0, &txtWidth, &txtHeight);
1555 	    plcItemPtr->xTxt = -txtWidth / 2;
1556 	    plcItemPtr->yTxt = 0;
1557 	    break;
1558 	case TK_ANCHOR_NE:
1559 	    layout = Tk_ComputeTextLayout(plcItemPtr->font, plcItemPtr->text,
1560 		    -1, -1, TK_JUSTIFY_RIGHT, 0, &txtWidth, &txtHeight);
1561 	    plcItemPtr->xTxt = -txtWidth;
1562 	    plcItemPtr->yTxt = 0;
1563 	    break;
1564 	case TK_ANCHOR_E:
1565 	    layout = Tk_ComputeTextLayout(plcItemPtr->font, plcItemPtr->text,
1566 		    -1, -1, TK_JUSTIFY_RIGHT, 0, &txtWidth, &txtHeight);
1567 	    plcItemPtr->xTxt = -txtWidth;
1568 	    plcItemPtr->yTxt = -txtHeight / 2;
1569 	    break;
1570 	case TK_ANCHOR_SE:
1571 	    layout = Tk_ComputeTextLayout(plcItemPtr->font, plcItemPtr->text,
1572 		    -1, -1, TK_JUSTIFY_RIGHT, 0, &txtWidth, &txtHeight);
1573 	    plcItemPtr->xTxt = -txtWidth;
1574 	    plcItemPtr->yTxt = -txtHeight;
1575 	    break;
1576 	case TK_ANCHOR_S:
1577 	    layout = Tk_ComputeTextLayout(plcItemPtr->font, plcItemPtr->text,
1578 		    -1, -1, TK_JUSTIFY_CENTER, 0, &txtWidth, &txtHeight);
1579 	    plcItemPtr->xTxt = -txtWidth / 2;
1580 	    plcItemPtr->yTxt = -txtHeight;
1581 	    break;
1582 	case TK_ANCHOR_SW:
1583 	    layout = Tk_ComputeTextLayout(plcItemPtr->font, plcItemPtr->text,
1584 		    -1, -1, TK_JUSTIFY_LEFT, 0, &txtWidth, &txtHeight);
1585 	    plcItemPtr->xTxt = 0;
1586 	    plcItemPtr->yTxt = -txtHeight;
1587 	    break;
1588 	case TK_ANCHOR_W:
1589 	    layout = Tk_ComputeTextLayout(plcItemPtr->font, plcItemPtr->text,
1590 		    -1, -1, TK_JUSTIFY_LEFT, 0, &txtWidth, &txtHeight);
1591 	    plcItemPtr->xTxt = 0;
1592 	    plcItemPtr->yTxt = -txtHeight / 2;
1593 	    break;
1594 	case TK_ANCHOR_NW:
1595 	    layout = Tk_ComputeTextLayout(plcItemPtr->font, plcItemPtr->text,
1596 		    -1, -1, TK_JUSTIFY_LEFT, 0, &txtWidth, &txtHeight);
1597 	    plcItemPtr->xTxt = 0;
1598 	    plcItemPtr->yTxt = 0;
1599 	    break;
1600 	case TK_ANCHOR_CENTER:
1601 	    layout = Tk_ComputeTextLayout(plcItemPtr->font, plcItemPtr->text,
1602 		    -1, -1, TK_JUSTIFY_CENTER, 0, &txtWidth, &txtHeight);
1603 	    plcItemPtr->xTxt = -txtWidth / 2;
1604 	    plcItemPtr->yTxt = -txtHeight / 2;
1605 	    break;
1606     }
1607     plcItemPtr->layout = layout;
1608     plcItemPtr->txtWidth = txtWidth;
1609     plcItemPtr->txtHeight = txtHeight;
1610 }
1611 
1612 /*
1613  *------------------------------------------------------------------------
1614  *
1615  * parseArrowOption --
1616  *
1617  *	This procedure is invoked during option processing to handle
1618  *	the "-arrow" option.
1619  *
1620  * Results:
1621  *	A standard Tcl result.
1622  *
1623  * Side effects:
1624  *	The state for a given item gets replaced by the state
1625  *	indicated in the value argument.
1626  *
1627  *------------------------------------------------------------------------
1628  */
1629 
1630 static int
parseArrowOption(clientData,interp,tkwin,value,widgRec,offset)1631 parseArrowOption(clientData, interp, tkwin, value, widgRec, offset)
1632     ClientData clientData;	/* Not used */
1633     Tcl_Interp *interp;		/* Current interpreter */
1634     Tk_Window tkwin;		/* Window containing the geomap item */
1635     char *value;		/* Value of the option */
1636     char *widgRec;		/* Pointer to item record */
1637     int offset;			/* Offset into widgRec */
1638 {
1639     PlaceItem *plcItemPtr = (PlaceItem *)(widgRec + offset);
1640     double az, len, tip_sz;
1641 
1642     if (sscanf(value, "%lf %lf %lf", &az, &len, &tip_sz) == 3) {
1643 	plcItemPtr->arrow_az = AngleFmDeg(az);
1644 	plcItemPtr->arrow_len = len;
1645 	plcItemPtr->arrow_tip_sz = tip_sz;
1646 	plcItemPtr->updateMap = 1;
1647 	return TCL_OK;
1648     } else {
1649 	Tcl_AppendResult(interp, "Arrow should be specified as "
1650 		"{azimuth length tip_size}", NULL);
1651 	return TCL_ERROR;
1652     }
1653 }
1654 
1655 /*
1656  *------------------------------------------------------------------------
1657  *
1658  * printArrowOption --
1659  *
1660  *	This procedure is invoked by the Tk configuration code
1661  *	to produce a printable string for the "-arrow" configuration
1662  *	option.
1663  *
1664  * Results:
1665  *	The return value is a string describing the state for
1666  *	the item referred to by "widgRec".  In addition, *freeProcPtr
1667  *	is filled in with the address of a procedure to call to free
1668  *	the result string when it's no longer needed.
1669  *
1670  * Side effects:
1671  * 	None.
1672  *
1673  *------------------------------------------------------------------------
1674  */
1675 
1676 static char *
printArrowOption(clientData,tkwin,widgRec,offset,freeProc)1677 printArrowOption(clientData, tkwin, widgRec, offset, freeProc)
1678     ClientData clientData;	/* Not used */
1679     Tk_Window tkwin;		/* Window containing the geomap item */
1680     char *widgRec;		/* Pointer to item record */
1681     int offset;			/* Offset into widgRec */
1682     Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
1683 				 * information about how to reclaim
1684 				 * storage for return string. */
1685 {
1686     PlaceItem *plcItemPtr = (PlaceItem *)(widgRec + offset);
1687     char az[TCL_DOUBLE_SPACE], len[TCL_INTEGER_SPACE], tip_sz[TCL_DOUBLE_SPACE];
1688     char *coordPtr[3];
1689 
1690     coordPtr[0] = az;
1691     coordPtr[1] = len;
1692     coordPtr[2] = tip_sz;
1693     *freeProc = Tcl_Free;
1694     Tcl_PrintDouble(NULL, AngleToDeg(plcItemPtr->arrow_az), az);
1695     sprintf(len, "%lf", plcItemPtr->arrow_len);
1696     Tcl_PrintDouble(NULL, plcItemPtr->arrow_tip_sz, tip_sz);
1697     return Tcl_Merge(3, coordPtr);
1698 }
1699 
1700 /*
1701  *------------------------------------------------------------------------
1702  *
1703  * updateMap --
1704  *
1705  * 	This procedure updates the mapPt member of a PlaceItem.
1706  *
1707  * Results:
1708  *	None.
1709  * Side effects:
1710  * 	Members in item record that depend on projection are updated.
1711  *
1712  *------------------------------------------------------------------------
1713  */
1714 
1715 static void
updateMap(clientData)1716 updateMap(clientData)
1717     ClientData clientData;	/* Geoplace item in need of update */
1718 {
1719     PlaceItem *plcItemPtr = (PlaceItem *)clientData;
1720     Tclgeomap_Place geoPlace = plcItemPtr->tclGeoPlace;
1721     GeoProj proj = (GeoProj)plcItemPtr->proj;
1722     GeoPt geoPt;
1723     MapPt mapPt, step2;
1724 
1725     if ( !geoPlace || !proj ) {
1726 	plcItemPtr->mapPt = MapPtNowhere();
1727 	plcItemPtr->updateMap = 0;
1728 	hide(plcItemPtr);
1729 	return;
1730     }
1731 
1732     /*
1733      * Compute the new map coordinates of the place.
1734      */
1735 
1736     plcItemPtr->mRefPt = LatLonToProj(plcItemPtr->refPt, proj);
1737     geoPt = Tclgeomap_PlaceLoc(geoPlace);
1738     mapPt = plcItemPtr->mapPt = LatLonToProj(geoPt, proj);
1739 
1740     /*
1741      * If necessary, figure out which way the arrow should be pointing on
1742      * the map.
1743      */
1744 
1745     if (plcItemPtr->arrow_len != 0 && plcItemPtr->arrowColor != None) {
1746 	double arrow_len = plcItemPtr->arrow_len;
1747 	double d_abs, d_ord, hypot;
1748 	double sin_theta, cos_theta;
1749 	double sin_alpha = sin(TKGEOMAP_PLACE_TIP_ANG * M_PI / 180.0);
1750 	double cos_alpha = cos(TKGEOMAP_PLACE_TIP_ANG * M_PI / 180.0);
1751 	double t = plcItemPtr->arrow_len * plcItemPtr->arrow_tip_sz;
1752 	double cos_cos, cos_sin, sin_cos, sin_sin;
1753 
1754 	step2 = LatLonToProj(GeoStep(geoPt, plcItemPtr->arrow_az,
1755 		    AngleFmDeg(0.25)), proj);
1756 	d_abs = step2.abs - mapPt.abs;
1757 	d_ord = step2.ord - mapPt.ord;
1758 	hypot = sqrt(d_abs * d_abs + d_ord * d_ord);
1759 	cos_theta = d_abs / hypot;
1760 	sin_theta = d_ord / hypot;
1761 	cos_cos = cos_theta * cos_alpha;
1762 	sin_sin = sin_theta * sin_alpha;
1763 	sin_cos = sin_theta * cos_alpha;
1764 	cos_sin = cos_theta * sin_alpha;
1765 	plcItemPtr->arrow_base_x = -ceil(0.5 * arrow_len * cos_theta);
1766 	plcItemPtr->arrow_base_y = ceil(0.5 * arrow_len * sin_theta);
1767 	plcItemPtr->arrow_head_x = ceil(0.5 * arrow_len * cos_theta);
1768 	plcItemPtr->arrow_head_y = -ceil(0.5 * arrow_len * sin_theta);
1769 	plcItemPtr->arrow_ltip_x = - ceil(t * (cos_cos + sin_sin));
1770 	plcItemPtr->arrow_ltip_y = ceil(t * (sin_cos - cos_sin));
1771 	plcItemPtr->arrow_rtip_x = - ceil(t * (cos_cos - sin_sin));
1772 	plcItemPtr->arrow_rtip_y = ceil(t * (sin_cos + cos_sin));
1773     }
1774 
1775     plcItemPtr->updateMap = 0;
1776     plcItemPtr->updateCvs = 1;
1777 }
1778 
1779 /*
1780  *------------------------------------------------------------------------
1781  *
1782  * updateCvs --
1783  *
1784  * 	This procedure updates the mapLnArr member of a geoplace item.
1785  *
1786  * Results:
1787  *	None.
1788  *
1789  * Side effects:
1790  * 	The coords member and bounding box of the geoplace item are updated.
1791  *
1792  *------------------------------------------------------------------------
1793  */
1794 
1795 static void
updateCvs(plcItemPtr)1796 updateCvs(plcItemPtr)
1797     PlaceItem *plcItemPtr;	/* Geoplace item to update */
1798 {
1799     Tk_Canvas canvas;		/* Canvas in which to render the item */
1800     Tk_Window tkwin;		/* Window with canvas */
1801     double cvsPerM;		/* Points per meter on canvas */
1802     double cvsPerMapM;		/* Points per meter on projection plane */
1803     int x1_, x2_, y1_, y2_;	/* Boundary limits for parts of the item */
1804     int x1, x2, y1, y2;
1805 
1806     canvas = plcItemPtr->canvas;
1807     tkwin = Tk_CanvasTkwin(canvas);
1808     Tk_CanvasEventuallyRedraw(plcItemPtr->canvas,
1809 	    plcItemPtr->header.x1, plcItemPtr->header.y1,
1810 	    plcItemPtr->header.x2, plcItemPtr->header.y2);
1811     if ( !plcItemPtr->tclGeoPlace || !plcItemPtr->proj ) {
1812 	plcItemPtr->mapPt = MapPtNowhere();
1813 	plcItemPtr->updateMap = 0;
1814 	hide(plcItemPtr);
1815 	return;
1816     }
1817     if (       ( !plcItemPtr->dotColor || plcItemPtr->dotSize <= 0)
1818 	    && ( !plcItemPtr->bmColor || !plcItemPtr->bitmap)
1819 	    && ( !plcItemPtr->textColor || !plcItemPtr->text) ) {
1820 	hide(plcItemPtr);
1821 	return;
1822     }
1823     if (plcItemPtr->updateMap) {
1824 	updateMap(plcItemPtr);
1825     }
1826     if (MapPtIsNowhere(plcItemPtr->mapPt)) {
1827 	hide(plcItemPtr);
1828 	return;
1829     }
1830     Tk_CanvasGetCoord(NULL, canvas, "100.0c", &cvsPerM);
1831     cvsPerMapM = cvsPerM * plcItemPtr->scale;
1832     plcItemPtr->xPlc = plcItemPtr->xRef
1833 	+ (plcItemPtr->mapPt.abs - plcItemPtr->mRefPt.abs) * cvsPerMapM;
1834     plcItemPtr->yPlc = plcItemPtr->yRef
1835 	- (plcItemPtr->mapPt.ord - plcItemPtr->mRefPt.ord) * cvsPerMapM;
1836 
1837     /*
1838      * Recompute bounding box
1839      */
1840 
1841     x1 = plcItemPtr->xPlc - (plcItemPtr->dotSize + 1) / 2 - 1;
1842     x2 = plcItemPtr->xPlc + (plcItemPtr->dotSize + 1) / 2 + 1;
1843     y1 = plcItemPtr->yPlc - (plcItemPtr->dotSize + 1) / 2 - 1;
1844     y2 = plcItemPtr->yPlc + (plcItemPtr->dotSize + 1) / 2 + 1;
1845     if ( (plcItemPtr->bitmap != None) && (plcItemPtr->bmColor != None) ) {
1846 	int w, h;
1847 
1848 	Tk_SizeOfBitmap(Tk_Display(tkwin), plcItemPtr->bitmap, &w, &h);
1849 	x1_ = floor(plcItemPtr->xPlc - w / 2) - 1;
1850 	x2_ = ceil(plcItemPtr->xPlc + w / 2) + 1;
1851 	y1_ = floor(plcItemPtr->yPlc - h / 2) - 1;
1852 	y2_ = ceil(plcItemPtr->yPlc + h / 2) + 1;
1853 
1854 	x1 = (x1 < x1_) ? x1 : x1_;
1855 	x2 = (x2 > x2_) ? x2 : x2_;
1856 	y1 = (y1 < y1_) ? y1 : y1_;
1857 	y2 = (y2 > y2_) ? y2 : y2_;
1858     }
1859     if (plcItemPtr->text && (plcItemPtr->textColor != None)
1860 	    && plcItemPtr->layout ) {
1861 
1862 	x1_ = plcItemPtr->xPlc + plcItemPtr->xTxt;
1863 	x2_ = x1_ + plcItemPtr->txtWidth;
1864 	y1_ = plcItemPtr->yPlc + plcItemPtr->yTxt;
1865 	y2_ = y1_ + plcItemPtr->txtHeight;
1866 
1867 	x1 = (x1 < x1_) ? x1 : x1_;
1868 	x2 = (x2 > x2_) ? x2 : x2_;
1869 	y1 = (y1 < y1_) ? y1 : y1_;
1870 	y2 = (y2 > y2_) ? y2 : y2_;
1871     }
1872     if (plcItemPtr->arrow_len != 0) {
1873 	int t = ceil(plcItemPtr->arrow_len * plcItemPtr->arrow_tip_sz);
1874 	int arrow_len = plcItemPtr->arrow_len;
1875 	x1_ = plcItemPtr->xPlc - (arrow_len + 1) / 2 - t - 1;
1876 	x2_ = plcItemPtr->xPlc + (arrow_len + 1) / 2 + t + 1;
1877 	y1_ = plcItemPtr->yPlc - (arrow_len + 1) / 2 - t - 1;
1878 	y2_ = plcItemPtr->yPlc + (arrow_len + 1) / 2 + t + 1;
1879 
1880 	x1 = (x1 < x1_) ? x1 : x1_;
1881 	x2 = (x2 > x2_) ? x2 : x2_;
1882 	y1 = (y1 < y1_) ? y1 : y1_;
1883 	y2 = (y2 > y2_) ? y2 : y2_;
1884     }
1885     if ( x1 >= x2 || y1 >= y2 ) {
1886 	hide(plcItemPtr);
1887 	return;
1888     }
1889     plcItemPtr->header.x1 = x1;
1890     plcItemPtr->header.x2 = x2;
1891     plcItemPtr->header.y1 = y1;
1892     plcItemPtr->header.y2 = y2;
1893     Tk_CanvasEventuallyRedraw(plcItemPtr->canvas,
1894 	    plcItemPtr->header.x1, plcItemPtr->header.y1,
1895 	    plcItemPtr->header.x2, plcItemPtr->header.y2);
1896 
1897     plcItemPtr->updateCvs = 0;
1898 }
1899