1/*
2 * tkgeomapLnArr.c --
3 *
4 * 	This file defines functions that display geolinearrays 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: tkgeomapLnArr.c,v 1.9 2004/11/19 16:52:25 tkgeomap Exp $
13 *
14 **********************************************************************
15 *
16 */
17
18#include "tkgeomap.h"
19#include "tkgeomapInt.h"
20
21/*
22 * This structure stores one geomap_lnarr item.
23 */
24
25typedef 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    GeoPt refPt;		/* Geographic coordinates of reference point */
32    Tclgeomap_Proj proj;	/* Cartographic projection */
33    Rotation rxn;		/* Map rotation (degrees map is rotated
34				 * clockwise from North-is-up) */
35    double scale;		/* Cartographic scale (pure number ratio) */
36    Tclgeomap_LnArr tclGeoLnArr;/* Geolinearray the item displays */
37    MapLnArr mapLnArr;		/* Map coordinates of the lat-lon's in the
38				 * geomap_lnarr */
39    int nLines;			/* Number of lines in rendition of mapLnArr
40				 * on canvas */
41    double **coordPtrPtr;	/* Array of canvas coordinates corresponding to
42				 * the map-points in mapLnArr.  Dimensions are
43				 * [nLines][num pts in line] */
44    int *nPtsPtr;		/* Number of points in each line of
45				 * coordPtrPtr.  Dimension is [nLines] */
46    int	updateMap;		/* If true, mapLnArr must be recomputed */
47    int width;			/* Line width */
48    XColor *lineColor;		/* Outline color */
49    int lineStyle;		/* LineSolid, LineOnOffDash, or
50				 * LineDoubleDash */
51    int dashes;			/* Dash length */
52    GC lineGC;			/* Graphics context for drawing outline */
53    int smooth;			/* If true, smooth the line.  Currently
54    				 * smoothing is done only with Tk's bezier
55				 * function */
56    int splineSteps;		/* Number of steps in each spline segment */
57    XColor *fillColor;		/* Color to fill shapes */
58    Pixmap stipple;		/* Stipple pattern to fill shapes */
59    int shape;			/* Complex, Convex, or Nonconvex */
60    GC fillGC;			/* Graphics context for filling */
61    int dotSize;		/* Dot size at segment ends */
62} LnArrItem;
63
64/*
65 * Prototypes for procedures defined in this file:
66 */
67
68static int		createProc _ANSI_ARGS_((Tcl_Interp *interp,
69				Tk_Canvas canvas, struct Tk_Item *itemPtr,
70				int objc, Tcl_Obj *CONST objv[]));
71static int		configProc _ANSI_ARGS_((Tcl_Interp *interp,
72				Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
73				Tcl_Obj *CONST objv[], int flags));
74static int		coordProc _ANSI_ARGS_((Tcl_Interp *interp,
75				Tk_Canvas canvas, Tk_Item *itemPtr, int objc,
76				Tcl_Obj *CONST objv[]));
77static void		deleteProc _ANSI_ARGS_((Tk_Canvas canvas,
78				Tk_Item *itemPtr, Display *display));
79static void		forgetLnArr _ANSI_ARGS_((ClientData clientData));
80static void		forgetProj _ANSI_ARGS_((ClientData clientData));
81static void		hide _ANSI_ARGS_((LnArrItem *lnArrItemPtr));
82static void		displayProc _ANSI_ARGS_((Tk_Canvas canvas,
83				Tk_Item *itemPtr, Display *display,
84				Drawable dst, int x, int y, int width,
85				int height));
86static double		pointProc _ANSI_ARGS_((Tk_Canvas canvas,
87				Tk_Item *itemPtr, double *coordPtr));
88static int		areaProc _ANSI_ARGS_((Tk_Canvas canvas,
89				Tk_Item *itemPtr, double *rectPtr));
90static int		postscriptProc _ANSI_ARGS_((Tcl_Interp *interp,
91				Tk_Canvas canvas, Tk_Item *itemPtr,
92				int prepass));
93static void		scaleProc _ANSI_ARGS_((Tk_Canvas canvas,
94				Tk_Item *itemPtr, double originX,
95				double originY, double scaleX, double scaleY));
96static void		translateProc _ANSI_ARGS_((Tk_Canvas canvas,
97				Tk_Item *itemPtr, double deltaX,
98				double deltaY));
99static int		parseGeoLnArrOption _ANSI_ARGS_((ClientData clientData,
100				Tcl_Interp *interp, Tk_Window tkwin,
101				char *value, char *widgRec, int offset));
102static char *		printGeoLnArrOption _ANSI_ARGS_((ClientData clientData,
103				Tk_Window tkwin, char *widgRec, int offset,
104				Tcl_FreeProc **freeProcPtr));
105static int		parseRefPtOption _ANSI_ARGS_((ClientData clientData,
106				Tcl_Interp *interp, Tk_Window tkwin,
107				char *value, char *widgRec, int offset));
108static char *		printRefPtOption _ANSI_ARGS_((ClientData clientData,
109				Tk_Window tkwin, char *widgRec, int offset,
110				Tcl_FreeProc **freeProcPtr));
111static int		parseProjOption _ANSI_ARGS_((ClientData clientData,
112				Tcl_Interp *interp, Tk_Window tkwin,
113				char *value, char *widgRec, int offset));
114static char *		printProjOption _ANSI_ARGS_((ClientData clientData,
115				Tk_Window tkwin, char *widgRec, int offset,
116				Tcl_FreeProc **freeProcPtr));
117static int		parseRotationOption _ANSI_ARGS_((ClientData clientData,
118				Tcl_Interp *interp, Tk_Window tkwin,
119				char *value, char *widgRec, int offset));
120static char *		printRotationOption _ANSI_ARGS_((ClientData clientData,
121				Tk_Window tkwin, char *widgRec, int offset,
122				Tcl_FreeProc **freeProcPtr));
123static int		parseLineStyleOption _ANSI_ARGS_((ClientData clientData,
124				Tcl_Interp *interp, Tk_Window tkwin,
125				char *value, char *widgRec, int offset));
126static char *		printLineStyleOption _ANSI_ARGS_((ClientData clientData,
127				Tk_Window tkwin, char *widgRec, int offset,
128				Tcl_FreeProc **freeProcPtr));
129static int		parseShapeOption _ANSI_ARGS_((ClientData clientData,
130				Tcl_Interp *interp, Tk_Window tkwin,
131				char *value, char *widgRec, int offset));
132static char *		printShapeOption _ANSI_ARGS_((ClientData clientData,
133				Tk_Window tkwin, char *widgRec, int offset,
134				Tcl_FreeProc **freeProcPtr));
135static void		updateMap _ANSI_ARGS_((ClientData clientData));
136static void		updateCvs _ANSI_ARGS_((LnArrItem *lnArrItemPtr));
137
138/*
139 * Custom configuration options
140 */
141
142static Tk_CustomOption geoLnArrOption = {
143    parseGeoLnArrOption,
144    printGeoLnArrOption,
145    NULL
146};
147
148static Tk_CustomOption refPtOption = {
149    parseRefPtOption,
150    printRefPtOption,
151    NULL
152};
153
154static Tk_CustomOption projOption = {
155    parseProjOption,
156    printProjOption,
157    NULL
158};
159
160static Tk_CustomOption rotationOption = {
161    parseRotationOption,
162    printRotationOption,
163    NULL
164};
165
166static Tk_CustomOption shapeOption = {
167    parseShapeOption,
168    printShapeOption,
169    NULL
170};
171
172static Tk_CustomOption lineStyleOption = {
173    parseLineStyleOption,
174    printLineStyleOption,
175    NULL
176};
177
178/*
179 * The members of tagsOption will be set in TkgeomapLnArrInit
180 */
181
182static Tk_CustomOption tagsOption = {
183    NULL, NULL, NULL
184};
185
186/*
187 * Configuration options for the geomap_lnarr item
188 */
189
190static Tk_ConfigSpec configSpecs[] = {
191    {TK_CONFIG_CUSTOM, "-lnarr", NULL, NULL, "", 0, TK_CONFIG_NULL_OK,
192	&geoLnArrOption},
193    {TK_CONFIG_CUSTOM, "-refpoint", NULL, NULL, "0.0 0.0", 0, 0,
194	&refPtOption},
195    {TK_CONFIG_CUSTOM, "-projection", NULL, NULL, "", 0, 0,
196	&projOption},
197    {TK_CONFIG_CUSTOM, "-rotation", NULL, NULL, "0.0", 0, 0,
198	&rotationOption},
199    {TK_CONFIG_DOUBLE, "-scale", NULL, NULL, "1.0e-7",
200	Tk_Offset(LnArrItem, scale), 0, NULL},
201    {TK_CONFIG_COLOR, "-outline", NULL, NULL, "NavyBlue",
202	Tk_Offset(LnArrItem, lineColor), TK_CONFIG_NULL_OK, NULL},
203    {TK_CONFIG_PIXELS, "-width", NULL, NULL, "1",
204	Tk_Offset(LnArrItem, width), 0, NULL},
205    {TK_CONFIG_CUSTOM, "-linestyle", NULL, NULL, "LineSolid",
206	0, 0, &lineStyleOption},
207    {TK_CONFIG_INT, "-dashes", NULL, NULL, "4",
208	Tk_Offset(LnArrItem, dashes), 0, NULL},
209    {TK_CONFIG_BOOLEAN, "-smooth", NULL, NULL, "0",
210	Tk_Offset(LnArrItem, smooth), 0, NULL},
211    {TK_CONFIG_INT, "-splinesteps", NULL, NULL, "12",
212	Tk_Offset(LnArrItem, splineSteps), 0, NULL},
213    {TK_CONFIG_COLOR, "-fill", NULL, NULL, "",
214	Tk_Offset(LnArrItem, fillColor), TK_CONFIG_NULL_OK, NULL},
215    {TK_CONFIG_BITMAP, "-stipple", NULL, NULL, NULL,
216	Tk_Offset(LnArrItem, stipple), TK_CONFIG_NULL_OK, NULL},
217    {TK_CONFIG_CUSTOM, "-shape", "shape", "Shape", "Complex", 0, 0,
218	&shapeOption},
219    {TK_CONFIG_PIXELS, "-dotsize", NULL, NULL, "0",
220	Tk_Offset(LnArrItem, dotSize), 0, NULL},
221    {TK_CONFIG_CUSTOM, "-tags", NULL, NULL, NULL, 0, TK_CONFIG_NULL_OK,
222	&tagsOption},
223    {TK_CONFIG_END,  NULL,  NULL,  NULL, NULL, 0, 0, NULL}
224};
225
226/*
227 * The structures below defines the geomap_lnarr item type in terms of
228 * procedures that can be invoked by generic item code.
229 */
230
231static Tk_ItemType geoLnArrType = {
232    "geomap_lnarr",			/* item type name */
233    sizeof(LnArrItem),			/* itemSize */
234    createProc,				/* createProc */
235    configSpecs,			/* configSpecs */
236    configProc,				/* configureProc */
237    coordProc,				/* coordProc */
238    deleteProc,				/* deleteProc */
239    displayProc,			/* displayProc */
240    TK_CONFIG_OBJS,			/* alwaysredraw or flags (?) */
241    pointProc,				/* pointProc */
242    areaProc,				/* areaProc */
243    postscriptProc,	                /* postscriptProc */
244    scaleProc,				/* scaleProc */
245    translateProc,			/* translateProc */
246    (Tk_ItemIndexProc *) NULL,		/* indexProc */
247    (Tk_ItemCursorProc *) NULL,		/* icursorProc */
248    (Tk_ItemSelectionProc *) NULL,	/* selectionProc */
249    (Tk_ItemInsertProc *) NULL,		/* insertProc */
250    (Tk_ItemDCharsProc *) NULL,		/* dTextProc */
251    (Tk_ItemType *) NULL,		/* nextPtr */
252};
253
254/*
255 *------------------------------------------------------------------------
256 *
257 * TkgeomapLnArrInit --
258 *
259 *	This procedure adds the geomap_lnarr item type to the canvas widget.
260 *	See the user documentation for a description of the its effect
261 *	on Tcl.
262 *
263 * Results:
264 *	A standard Tcl result.
265 *
266 * Side effects:
267 * 	Prerequisite packages are initialized, and the "geomap_lnarr" item is
268 * 	added to the Tk canvas interface.
269 *
270 *------------------------------------------------------------------------
271 */
272
273int
274TkgeomapLnArrInit(interp)
275    Tcl_Interp *interp;			/* Current interpreter */
276{
277    static int loaded;			/* If true, package is loaded */
278
279    if (loaded) {
280	return TCL_OK;
281    }
282#ifdef USE_TCL_STUBS
283    if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
284	return TCL_ERROR;
285    }
286#endif
287#ifdef USE_TK_STUBS
288    if (Tk_InitStubs(interp, TK_VERSION, 0) == NULL) {
289	return TCL_ERROR;
290    }
291#endif
292
293    /*
294     * Set tagsOption.  This must be done at run time because the stubs
295     * library set the functions to non-static values, so they cannot be
296     * used in an initializer.
297     */
298
299    tagsOption.parseProc = Tk_CanvasTagsParseProc;
300    tagsOption.printProc = Tk_CanvasTagsPrintProc;
301
302    Tk_CreateItemType(&geoLnArrType);
303    loaded = 1;
304    return TCL_OK;
305}
306
307/*
308 *--------------------------------------------------------------
309 *
310 * createProc --
311 *
312 *	This procedure is invoked to create a new geomap_lnarr
313 *	item in a canvas.
314 *
315 * Results:
316 *	A standard Tcl return value.  If an error occurred in
317 *	creating the item, then an error message is left in
318 *	the interp's result;  in this case itemPtr is left uninitialized,
319 *	so it can be safely freed by the caller.
320 *
321 * Side effects:
322 *	A new geomap_lnarr item is created.
323 *
324 *--------------------------------------------------------------
325 */
326
327static int
328createProc(interp, canvas, itemPtr, objc, objv)
329    Tcl_Interp *interp;			/* Interpreter for error reporting. */
330    Tk_Canvas canvas;			/* Canvas to hold new item. */
331    Tk_Item *itemPtr;			/* Record to hold new item;  header
332					 * has been initialized by caller. */
333    int objc;				/* Number of arguments in objv. */
334    Tcl_Obj *CONST objv[];		/* Arguments describing linearray. */
335{
336    LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
337    double xRef, yRef;
338
339
340    /*
341     * Set location of reference point from coordinates given on command line.
342     */
343
344    if ((Tk_CanvasGetCoordFromObj(interp, canvas, objv[0], &xRef) != TCL_OK)
345	    || (Tk_CanvasGetCoordFromObj(interp, canvas, objv[1], &yRef)
346		!= TCL_OK)) {
347	return TCL_ERROR;
348    }
349    lnArrItemPtr->xRef = xRef;
350    lnArrItemPtr->yRef = yRef;
351
352    /*
353     * Initialize values.  Bogus initializers will be set properly
354     * when the item is configured.
355     */
356
357    lnArrItemPtr->interp = interp;
358    lnArrItemPtr->canvas = canvas;
359    lnArrItemPtr->refPt = GeoPtFmDeg(0.0, 0.0);
360    lnArrItemPtr->proj = NULL;
361    lnArrItemPtr->rxn = NewRotation(0.0);
362    lnArrItemPtr->scale = 0.0;
363    lnArrItemPtr->tclGeoLnArr = NULL;
364    lnArrItemPtr->mapLnArr = NULL;
365    lnArrItemPtr->coordPtrPtr = NULL;
366    lnArrItemPtr->nPtsPtr = NULL;
367    lnArrItemPtr->nLines = 0;
368    lnArrItemPtr->updateMap = 1;
369    lnArrItemPtr->width = 0;
370    lnArrItemPtr->lineColor = None;
371    lnArrItemPtr->lineStyle = LineSolid;
372    lnArrItemPtr->dashes = 4;
373    lnArrItemPtr->lineGC = None;
374    lnArrItemPtr->fillColor = None;
375    lnArrItemPtr->stipple = None;
376    lnArrItemPtr->shape = Nonconvex;
377    lnArrItemPtr->fillGC = None;
378    lnArrItemPtr->dotSize = 0;
379
380    /*
381     * Configure the new geomap_lnarr item.
382     */
383
384    if (configProc(interp, canvas, itemPtr, objc - 2, objv + 2, 0) != TCL_OK) {
385	deleteProc(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
386	return TCL_ERROR;
387    }
388
389    return TCL_OK;
390}
391
392/*
393 *------------------------------------------------------------------------
394 *
395 * parseGeoLnArrOption --
396 *
397 *	This procedure is invoked during option processing to handle
398 *	the "-lnarr" option.
399 *
400 * Results:
401 *	A standard Tcl result.
402 *
403 * Side effects:
404 *	The state for a given item gets replaced by the state
405 *	indicated in the value argument.
406 *
407 *------------------------------------------------------------------------
408 */
409
410static int
411parseGeoLnArrOption(clientData, interp, tkwin, value, widgRec, offset)
412    ClientData clientData;	/* Not used */
413    Tcl_Interp *interp;		/* Current interpreter */
414    Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
415    char *value;		/* Value of the option */
416    char *widgRec;		/* Pointer to item record */
417    int offset;			/* Offset into widgRec */
418{
419    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
420    char *arrNm = value;
421    Tclgeomap_LnArr tclGeoLnArr;
422    int n;
423
424    if ( strlen(value) > 0 ) {
425	/*
426	 * User has requested that a linearray be displayed in the item.
427	 */
428
429	tclGeoLnArr = Tclgeomap_GetLnArr(interp, arrNm);
430	if ( !tclGeoLnArr ) {
431	    Tcl_AppendResult(interp, "No linearray named ", value, NULL);
432	    return TCL_ERROR;
433	}
434	if ( tclGeoLnArr == lnArrItemPtr->tclGeoLnArr ) {
435	    return TCL_OK;
436	}
437	if (lnArrItemPtr->tclGeoLnArr) {
438	    Tclgeomap_CnxLnArrDeleteTask(lnArrItemPtr->tclGeoLnArr,
439		    lnArrItemPtr);
440	}
441	if (lnArrItemPtr->mapLnArr) {
442	    MapLnArrDestroy(lnArrItemPtr->mapLnArr);
443	}
444	for (n = 0; n < lnArrItemPtr->nLines; n++) {
445	    CKFREE((char *)lnArrItemPtr->coordPtrPtr[n]);
446	    lnArrItemPtr->coordPtrPtr[n] = NULL;
447	}
448	lnArrItemPtr->tclGeoLnArr = tclGeoLnArr;
449	Tclgeomap_AddLnArrDeleteTask(tclGeoLnArr, forgetLnArr, lnArrItemPtr);
450	lnArrItemPtr->mapLnArr = NULL;
451	lnArrItemPtr->nLines = 0;
452	lnArrItemPtr->updateMap = 1;
453    } else {
454	/*
455	 * User has requested that NO linearray be displayed in the item.
456	 */
457
458	if (lnArrItemPtr->tclGeoLnArr) {
459	    Tclgeomap_CnxLnArrDeleteTask(lnArrItemPtr->tclGeoLnArr,
460		    lnArrItemPtr);
461	    MapLnArrDestroy(lnArrItemPtr->mapLnArr);
462	    lnArrItemPtr->mapLnArr = NULL;
463	    for (n = 0; n < lnArrItemPtr->nLines; n++) {
464		CKFREE((char *)lnArrItemPtr->coordPtrPtr[n]);
465		lnArrItemPtr->coordPtrPtr[n] = NULL;
466	    }
467	    lnArrItemPtr->tclGeoLnArr = NULL;
468	    lnArrItemPtr->nLines = 0;
469	    lnArrItemPtr->updateMap = 0;
470	}
471    }
472    return TCL_OK;
473}
474
475/*
476 *------------------------------------------------------------------------
477 *
478 * printGeoLnArrOption --
479 *
480 *	This procedure is invoked by the Tk configuration code
481 *	to produce a printable string for the "-refpoint" configuration
482 *	option.
483 *
484 * Results:
485 *	The return value is a string describing the state for
486 *	the item referred to by "widgRec".  In addition, *freeProcPtr
487 *	is filled in with the address of a procedure to call to free
488 *	the result string when it's no longer needed.
489 *
490 * Side effects:
491 * 	None.
492 *
493 *------------------------------------------------------------------------
494 */
495
496static char *
497printGeoLnArrOption(clientData, tkwin, widgRec, offset, freeProc)
498    ClientData clientData;	/* Not used */
499    Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
500    char *widgRec;		/* Pointer to item record */
501    int offset;			/* Offset into widgRec */
502    Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
503				 * information about how to reclaim
504				 * storage for return string. */
505{
506    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
507    CONST char *arrNm;
508    char *rtn;
509
510    arrNm = lnArrItemPtr->tclGeoLnArr
511	?  Tclgeomap_LnArrName(lnArrItemPtr->tclGeoLnArr)
512	: "";
513    rtn = Tcl_Alloc(strlen(arrNm) + 1);
514    strcpy(rtn, arrNm);
515    *freeProc = Tcl_Free;
516    return rtn;
517}
518
519/*
520 *------------------------------------------------------------------------
521 *
522 * parseRefPtOption --
523 *
524 *	This procedure is invoked during option processing to handle
525 *	the "-refpoint" option.
526 *
527 * Results:
528 *	A standard Tcl result.
529 *
530 * Side effects:
531 *	The state for a given item gets replaced by the state
532 *	indicated in the value argument.
533 *
534 *------------------------------------------------------------------------
535 */
536
537static int
538parseRefPtOption(clientData, interp, tkwin, value, widgRec, offset)
539    ClientData clientData;	/* Not used */
540    Tcl_Interp *interp;		/* Current interpreter */
541    Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
542    char *value;		/* Value of the option */
543    char *widgRec;		/* Pointer to item record */
544    int offset;			/* Offset into widgRec */
545{
546
547    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
548    GeoPt refPt;
549    double lat, lon;
550
551    if (sscanf(value, "%lf %lf", &lat, &lon) == 2) {
552	refPt = GeoPtFmDeg(lat, lon);
553	lnArrItemPtr->refPt = GwchLonPt(refPt);
554	return TCL_OK;
555    } else {
556	Tcl_AppendResult(interp, "Expected {lat lon}, got ", value, NULL);
557	return TCL_ERROR;
558    }
559}
560
561/*
562 *------------------------------------------------------------------------
563 *
564 * printRefPtOption --
565 *
566 *	This procedure is invoked by the Tk configuration code
567 *	to produce a printable string for the "-refpoint" configuration
568 *	option.
569 *
570 * Results:
571 *	The return value is a string describing the state for
572 *	the item referred to by "widgRec".  In addition, *freeProcPtr
573 *	is filled in with the address of a procedure to call to free
574 *	the result string when it's no longer needed.
575 *
576 * Side effects:
577 * 	None.
578 *
579 *------------------------------------------------------------------------
580 */
581
582static char *
583printRefPtOption(clientData, tkwin, widgRec, offset, freeProc)
584    ClientData clientData;	/* Not used */
585    Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
586    char *widgRec;		/* Pointer to item record */
587    int offset;			/* Offset into widgRec */
588    Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
589				 * information about how to reclaim
590				 * storage for return string. */
591{
592    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
593    char lat[TCL_DOUBLE_SPACE], lon[TCL_DOUBLE_SPACE];
594    char *coordPtr[2];
595    double dLat, dLon;
596
597    coordPtr[0] = lat;
598    coordPtr[1] = lon;
599    *freeProc = Tcl_Free;
600    GeoPtGetDeg(lnArrItemPtr->refPt, &dLat, &dLon);
601    Tcl_PrintDouble(NULL, dLat, lat);
602    Tcl_PrintDouble(NULL, dLon, lon);
603    return Tcl_Merge(2, coordPtr);
604}
605
606/*
607 *------------------------------------------------------------------------
608 *
609 * parseProjOption --
610 *
611 *	This procedure is invoked during option processing to handle
612 *	the "-projection" option.
613 *
614 * Results:
615 *	A standard Tcl result.
616 *
617 * Side effects:
618 *	The state for a given item gets replaced by the state
619 *	indicated in the value argument.
620 *
621 *------------------------------------------------------------------------
622 */
623
624static int
625parseProjOption(clientData, interp, tkwin, value, widgRec, offset)
626    ClientData clientData;	/* Not used */
627    Tcl_Interp *interp;		/* Current interpreter */
628    Tk_Window tkwin;		/* Window containing the linearray item */
629    char *value;		/* Value of the option */
630    char *widgRec;		/* Pointer to item record */
631    int offset;			/* Offset into widgRec */
632{
633    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
634    Tclgeomap_Proj proj;
635
636    if (*value == '\0') {
637	if (lnArrItemPtr->proj) {
638	    Tclgeomap_CnxProjUpdateTask(lnArrItemPtr->proj, lnArrItemPtr);
639	    Tclgeomap_CnxProjDeleteTask(lnArrItemPtr->proj, lnArrItemPtr);
640	}
641	lnArrItemPtr->proj = NULL;
642	return TCL_OK;
643    }
644    if ( !(proj = Tclgeomap_GetProj(interp, value)) ) {
645	Tcl_AppendResult(interp, "No projection named ", value, NULL);
646	return TCL_ERROR;
647    }
648    if (lnArrItemPtr->proj) {
649	Tclgeomap_CnxProjUpdateTask(lnArrItemPtr->proj, lnArrItemPtr);
650	Tclgeomap_CnxProjDeleteTask(lnArrItemPtr->proj, lnArrItemPtr);
651    }
652    lnArrItemPtr->proj = proj;
653    Tclgeomap_AddProjUpdateTask(lnArrItemPtr->proj, updateMap, lnArrItemPtr);
654    Tclgeomap_AddProjDeleteTask(lnArrItemPtr->proj, forgetProj, lnArrItemPtr);
655    lnArrItemPtr->updateMap = 1;
656    return TCL_OK;
657}
658
659/*
660 *------------------------------------------------------------------------
661 *
662 * printProjOption --
663 *
664 *	This procedure is invoked by the Tk configuration code
665 *	to produce a printable string for the "-projection" configuration
666 *	option.
667 *
668 * Results:
669 *	The return value is a string describing the state for
670 *	the item referred to by "widgRec".  In addition, *freeProcPtr
671 *	is filled in with the address of a procedure to call to free
672 *	the result string when it's no longer needed.
673 *
674 * Side effects:
675 * 	None.
676 *
677 *------------------------------------------------------------------------
678 */
679
680static char *
681printProjOption(clientData, tkwin, widgRec, offset, freeProc)
682    ClientData clientData;	/* Not used */
683    Tk_Window tkwin;		/* Window containing the geomap item */
684    char *widgRec;		/* Pointer to item record */
685    int offset;			/* Offset into widgRec */
686    Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
687				 * information about how to reclaim
688				 * storage for return string. */
689{
690    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
691    CONST char *projNm;
692    char *rtn;
693
694    projNm
695	= lnArrItemPtr->proj ? Tclgeomap_ProjName(lnArrItemPtr->proj) : "";
696    rtn = Tcl_Alloc(strlen(projNm) + 1);
697    strcpy(rtn, projNm);
698    *freeProc = Tcl_Free;
699    return rtn;
700}
701
702/*
703 *------------------------------------------------------------------------
704 *
705 * parseRotationOption --
706 *
707 *	This procedure is invoked during option processing to handle
708 *	the "-rotation" option.
709 *
710 * Results:
711 *	A standard Tcl result.
712 *
713 * Side effects:
714 *	The state for a given item gets replaced by the state
715 *	indicated in the value argument.
716 *
717 *------------------------------------------------------------------------
718 */
719
720static int
721parseRotationOption(clientData, interp, tkwin, value, widgRec, offset)
722    ClientData clientData;	/* Not used */
723    Tcl_Interp *interp;		/* Current interpreter */
724    Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
725    char *value;		/* Value of the option */
726    char *widgRec;		/* Pointer to item record */
727    int offset;			/* Offset into widgRec */
728{
729    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
730    static Tcl_Obj *brgObj;
731    double angle;
732    int i;			/* Index returned by Tcl_GetIndexFromObj */
733    static char *brgNms[] = {
734	"north",	"nneast",	"neast",	"eneast",
735	"east",		"eseast",	"seast",	"sseast",
736	"south",	"sswest",	"swest",	"wswest",
737	"west",		"wnwest",	"nwest",	"nnwest",	NULL
738    };				/* Bearing names */
739    static double brgs[] = {
740	   0.0,		 -22.5,		 -45.0,		 -67.5,
741	 -90.0,		-112.5,		-135.0,		-157.5,
742	 180.0,		 157.5,		 135.0,		 112.5,
743	  90.0,		  67.5,		  45.0,		  22.5
744    };
745
746    if ( !brgObj ) {
747	brgObj = Tcl_NewObj();
748    }
749
750    /*
751     * Get angle from value
752     */
753
754    Tcl_SetStringObj(brgObj, value, -1);
755    if (Tcl_GetIndexFromObj(NULL, brgObj, brgNms, "", 0, &i) == TCL_OK) {
756	angle = brgs[i];
757    } else if (Tcl_GetDoubleFromObj(NULL, brgObj, &angle) == TCL_OK) {
758	angle = angle - 360.0 * ceil((angle - 180.0) / 360.0);
759    } else {
760	Tcl_AppendResult(interp, "  Rotation should be a float-point number ",
761		"of one of: north, nneast, neast, eneast, east, eseast, "
762		"seast, sseast, south, sswest, swest, wswest, west, wnwest, "
763		"nwest, or nnwest", NULL);
764	return TCL_ERROR;
765    }
766
767    /*
768     * Set rotation, creating it in the item if necessary
769     */
770
771    if ( !lnArrItemPtr->rxn && !(lnArrItemPtr->rxn = NewRotation(angle))) {
772	Tcl_AppendResult(interp,
773		"Could not initialize rotation information", NULL);
774	return TCL_ERROR;
775    } else {
776	SetRotation(lnArrItemPtr->rxn, angle);
777    }
778    lnArrItemPtr->updateMap = 1;
779
780    return TCL_OK;
781}
782
783/*
784 *------------------------------------------------------------------------
785 *
786 * printRotationOption --
787 *
788 *	This procedure is invoked by the Tk configuration code
789 *	to produce a printable string for the "-rotation" configuration
790 *	option.
791 *
792 * Results:
793 *	The return value is a string describing the state for
794 *	the item referred to by "widgRec".  In addition, *freeProcPtr
795 *	is filled in with the address of a procedure to call to free
796 *	the result string when it's no longer needed.
797 *
798 * Side effects:
799 * 	None.
800 *
801 *------------------------------------------------------------------------
802 */
803
804static char *
805printRotationOption(clientData, tkwin, widgRec, offset, freeProc)
806    ClientData clientData;	/* Not used */
807    Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
808    char *widgRec;		/* Pointer to item record */
809    int offset;			/* Offset into widgRec */
810    Tcl_FreeProc **freeProc;	/* Pointer to variable to fill in with
811				 * information about how to reclaim
812				 * storage for return string. */
813{
814    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
815    char *result;
816
817    result = Tcl_Alloc(TCL_DOUBLE_SPACE);
818    *freeProc = Tcl_Free;
819    Tcl_PrintDouble(NULL, GetRotation(lnArrItemPtr->rxn), result);
820    return result;
821}
822
823/*
824 *------------------------------------------------------------------------
825 *
826 * parseShapeOption --
827 *
828 *	This procedure is invoked during option processing to handle
829 *	the "-shape" option.
830 *
831 * Results:
832 *	A standard Tcl result.
833 *
834 * Side effects:
835 *	The state for a given item gets replaced by the state
836 *	indicated in the value argument.
837 *
838 *------------------------------------------------------------------------
839 */
840
841static int
842parseShapeOption(clientData, interp, tkwin, value, widgRec, offset)
843    ClientData clientData;	/* Not used */
844    Tcl_Interp *interp;		/* Current interpreter */
845    Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
846    char *value;		/* Value of the option */
847    char *widgRec;		/* Pointer to item record */
848    int offset;			/* Offset into widgRec */
849{
850    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
851
852    if (strcmp(value, "Convex") == 0) {
853	lnArrItemPtr->shape = Convex;
854    } else if (strcmp(value, "Nonconvex") == 0) {
855	lnArrItemPtr->shape = Nonconvex;
856    } else if (strcmp(value, "Complex") == 0) {
857	lnArrItemPtr->shape = Complex;
858    } else {
859	Tcl_AppendResult(interp,
860		"Shape must be \"Convex\", \"Nonconvex\" or \"Complex\"", NULL);
861	return TCL_ERROR;
862    }
863    return TCL_OK;
864}
865
866/*
867 *------------------------------------------------------------------------
868 *
869 * printShapeOption --
870 *
871 *	This procedure is invoked by the Tk configuration code
872 *	to produce a printable string for the "-shape" configuration
873 *	option.
874 *
875 * Results:
876 *	The return value is a string describing the state for
877 *	the item referred to by "widgRec".  In addition, *freeProcPtr
878 *	is filled in with the address of a procedure to call to free
879 *	the result string when it's no longer needed.
880 *
881 * Side effects:
882 * 	None.
883 *
884 *------------------------------------------------------------------------
885 */
886
887static char *
888printShapeOption(clientData, tkwin, widgRec, offset, freeProc)
889    ClientData clientData;	/* Not used */
890    Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
891    char *widgRec;		/* Pointer to item record */
892    int offset;			/* Offset into widgRec */
893    Tcl_FreeProc **freeProc;	/* Not used */
894{
895    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
896    char *rslt = "Convex";
897
898    switch (lnArrItemPtr->shape) {
899	case Convex:
900	    rslt = "Convex";
901	    break;
902	case Nonconvex:
903	    rslt = "Nonconvex";
904	    break;
905	case Complex:
906	    rslt = "Complex";
907	    break;
908    }
909    return rslt;
910}
911
912/*
913 *------------------------------------------------------------------------
914 *
915 * parseLineStyleOption --
916 *
917 *	This procedure is invoked during option processing to handle
918 *	the "-linestyle" option.
919 *
920 * Results:
921 *	A standard Tcl result.
922 *
923 * Side effects:
924 *	The state for a given item gets replaced by the state
925 *	indicated in the value argument.
926 *
927 *------------------------------------------------------------------------
928 */
929
930static int
931parseLineStyleOption(clientData, interp, tkwin, value, widgRec, offset)
932    ClientData clientData;	/* Not used */
933    Tcl_Interp *interp;		/* Current interpreter */
934    Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
935    char *value;		/* Value of the option */
936    char *widgRec;		/* Pointer to item record */
937    int offset;			/* Offset into widgRec */
938{
939    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
940
941    if (strcmp(value, "LineSolid") == 0) {
942	lnArrItemPtr->lineStyle = LineSolid;
943    } else if (strcmp(value, "LineOnOffDash") == 0) {
944	lnArrItemPtr->lineStyle = LineOnOffDash;
945    } else if (strcmp(value, "LineDoubleDash") == 0) {
946	lnArrItemPtr->lineStyle = LineDoubleDash;
947    } else {
948	Tcl_AppendResult(interp,
949		"LineStyle must be \"LineSolid\", \"LineOnOffDash\" or"
950		" \"LineDoubleDash\"", NULL);
951	return TCL_ERROR;
952    }
953    return TCL_OK;
954}
955
956/*
957 *------------------------------------------------------------------------
958 *
959 * printLineStyleOption --
960 *
961 *	This procedure is invoked by the Tk configuration code
962 *	to produce a printable string for the "-linestyle" configuration
963 *	option.
964 *
965 * Results:
966 *	The return value is a string describing the state for
967 *	the item referred to by "widgRec".  In addition, *freeProcPtr
968 *	is filled in with the address of a procedure to call to free
969 *	the result string when it's no longer needed.
970 *
971 * Side effects:
972 * 	None.
973 *
974 *------------------------------------------------------------------------
975 */
976
977static char *
978printLineStyleOption(clientData, tkwin, widgRec, offset, freeProc)
979    ClientData clientData;	/* Not used */
980    Tk_Window tkwin;		/* Window containing the geomap_lnarr item */
981    char *widgRec;		/* Pointer to item record */
982    int offset;			/* Offset into widgRec */
983    Tcl_FreeProc **freeProc;	/* Not used */
984{
985    LnArrItem *lnArrItemPtr = (LnArrItem *)(widgRec + offset);
986    char *rslt = "Convex";
987
988    switch (lnArrItemPtr->lineStyle) {
989	case LineSolid:
990	    rslt = "LineSolid";
991	    break;
992	case LineOnOffDash:
993	    rslt = "LineOnOffDash";
994	    break;
995	case LineDoubleDash:
996	    rslt = "LineDoubleDash";
997	    break;
998    }
999    return rslt;
1000}
1001
1002/*
1003 *--------------------------------------------------------------
1004 *
1005 * configProc --
1006 *
1007 *	This procedure is invoked to configure various aspects
1008 *	of a geomap_lnarr item, such as its anchor position.
1009 *
1010 * Results:
1011 *	A standard Tcl result code.  If an error occurs, then
1012 *	an error message is left in the interp's result.
1013 *
1014 * Side effects:
1015 *	Configuration information may be set for itemPtr.
1016 *
1017 *--------------------------------------------------------------
1018 */
1019
1020static int
1021configProc(interp, canvas, itemPtr, objc, objv, flags)
1022    Tcl_Interp *interp;		/* Used for error reporting. */
1023    Tk_Canvas canvas;		/* Canvas containing itemPtr. */
1024    Tk_Item *itemPtr;		/* Geolinearray item to reconfigure. */
1025    int objc;			/* Number of elements in objv.  */
1026    Tcl_Obj *CONST objv[];	/* Arguments describing things to configure. */
1027    int flags;			/* Flags to pass to Tk_ConfigureWidget. */
1028{
1029    LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
1030    Tk_Window tkwin = Tk_CanvasTkwin(canvas);
1031    unsigned long mask;
1032    XGCValues gcVals;
1033    GC gc;
1034
1035    /*
1036     * Configure the item.
1037     */
1038
1039    if (Tk_ConfigureWidget(interp, Tk_CanvasTkwin(canvas),
1040		geoLnArrType.configSpecs, objc, (char **)objv,
1041		(char *)lnArrItemPtr, flags|TK_CONFIG_OBJS) != TCL_OK) {
1042	return TCL_ERROR;
1043    }
1044    if (lnArrItemPtr->lineColor != None) {
1045	gcVals.foreground = lnArrItemPtr->lineColor->pixel;
1046	gcVals.line_width = lnArrItemPtr->width;
1047	gcVals.line_style = lnArrItemPtr->lineStyle;
1048	gcVals.dashes = lnArrItemPtr->dashes;
1049	mask = GCForeground|GCLineWidth|GCLineStyle|GCDashList;
1050	gc = Tk_GetGC(tkwin, mask, &gcVals);
1051	if (lnArrItemPtr->lineGC != None) {
1052	    Tk_FreeGC(Tk_Display(tkwin), lnArrItemPtr->lineGC);
1053	}
1054	lnArrItemPtr->lineGC = gc;
1055    }
1056
1057    if (lnArrItemPtr->fillColor != None) {
1058	gcVals.foreground = lnArrItemPtr->fillColor->pixel;
1059	mask = GCForeground;
1060	if (lnArrItemPtr->stipple != None) {
1061	    gcVals.stipple = lnArrItemPtr->stipple;
1062	    gcVals.fill_style = FillStippled;
1063	    mask |= GCStipple|GCFillStyle;
1064	}
1065	gc = Tk_GetGC(tkwin, mask, &gcVals);
1066	if (lnArrItemPtr->fillGC != None) {
1067	    Tk_FreeGC(Tk_Display(tkwin), lnArrItemPtr->fillGC);
1068	}
1069	lnArrItemPtr->fillGC = gc;
1070    }
1071    updateCvs(lnArrItemPtr);
1072    return TCL_OK;
1073}
1074
1075/*
1076 *--------------------------------------------------------------
1077 *
1078 * coordProc --
1079 *
1080 *	This procedure is invoked to process the "coords" widget
1081 *	command on geomap_lnarr items.  See the user documentation for
1082 *	details on what it does.
1083 *
1084 * Results:
1085 *	Returns TCL_OK or TCL_ERROR, and sets the interp's result.
1086 *
1087 * Side effects:
1088 *	The coordinates for the given item may be changed.
1089 *
1090 *--------------------------------------------------------------
1091 */
1092
1093static int
1094coordProc(interp, canvas, itemPtr, objc, objv)
1095    Tcl_Interp *interp;			/* Used for error reporting. */
1096    Tk_Canvas canvas;			/* Canvas containing item. */
1097    Tk_Item *itemPtr;			/* Item whose coordinates are to be
1098					 * read or modified. */
1099    int objc;				/* Number of coordinates supplied in
1100					 * objv. */
1101    Tcl_Obj *CONST objv[];		/* Array of coordinates: x1, y1,
1102					 * x2, y2, ... */
1103{
1104    LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
1105
1106    if (objc == 0) {
1107	/*
1108	 * Set result to item coordinates
1109	 */
1110
1111	Tcl_Obj *obj = Tcl_NewObj();
1112	Tcl_Obj *subobj = Tcl_NewDoubleObj(lnArrItemPtr->xRef);
1113	Tcl_ListObjAppendElement(interp, obj, subobj);
1114	subobj = Tcl_NewDoubleObj(lnArrItemPtr->yRef);
1115	Tcl_ListObjAppendElement(interp, obj, subobj);
1116	Tcl_SetObjResult(interp, obj);
1117    } else if (objc == 1) {
1118	Tcl_Obj *lineObj, *rslt;	/* Result */
1119	int nl, np;			/* Loop indeces */
1120	double *coordPtr;		/* Canvas coordinate to put in result */
1121
1122	if (strcmp(Tcl_GetString(objv[0]), "dump") == 0) {
1123	    /*
1124	     * User wants dump of canvas coordinates for the linearray.
1125	     */
1126
1127	    updateCvs(lnArrItemPtr);
1128	    rslt = Tcl_NewObj();
1129	    if (lnArrItemPtr->nLines == 1) {
1130		/*
1131		 * Array has one line.  Set result to list of {x y x y ...}
1132		 * values.
1133		 */
1134
1135		for (coordPtr = lnArrItemPtr->coordPtrPtr[0], np = 0;
1136			np < lnArrItemPtr->nPtsPtr[0];
1137			np++) {
1138		    Tcl_ListObjAppendElement(interp, rslt,
1139			    Tcl_NewDoubleObj(*coordPtr++));
1140		    Tcl_ListObjAppendElement(interp, rslt,
1141			    Tcl_NewDoubleObj(*coordPtr++));
1142		}
1143	    } else {
1144		/*
1145		 * If array has several lines, set result to a list of lists of
1146		 * {x y x y ...} values.
1147		 */
1148
1149		for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1150		    lineObj = Tcl_NewObj();
1151		    for (coordPtr = lnArrItemPtr->coordPtrPtr[nl], np = 0;
1152			    np < lnArrItemPtr->nPtsPtr[nl];
1153			    np++) {
1154			Tcl_ListObjAppendElement(interp, lineObj,
1155				Tcl_NewDoubleObj(*coordPtr++));
1156			Tcl_ListObjAppendElement(interp, lineObj,
1157				Tcl_NewDoubleObj(*coordPtr++));
1158		    }
1159		    Tcl_ListObjAppendElement(interp, rslt, lineObj);
1160		}
1161	    }
1162	    Tcl_SetObjResult(interp, rslt);
1163	    return TCL_OK;
1164	} else {
1165	    /*
1166	     * New coordinates given on command line in form
1167	     * "pathName coords {x y}"
1168	     */
1169
1170	    Tcl_Obj **coordsPtr;
1171	    int n;
1172
1173	    if (Tcl_ListObjGetElements(interp, objv[0], &n, &coordsPtr)
1174		    != TCL_OK || n != 2) {
1175		Tcl_AppendResult(interp, "Could not split coords list\n", NULL);
1176		return TCL_ERROR;
1177	    }
1178	    if ((Tk_CanvasGetCoordFromObj(interp, canvas, coordsPtr[0],
1179			    &lnArrItemPtr->xRef) != TCL_OK)
1180		    || (Tk_CanvasGetCoordFromObj(interp, canvas, coordsPtr[1],
1181			    &lnArrItemPtr->yRef) != TCL_OK)) {
1182		return TCL_ERROR;
1183	    }
1184	    updateCvs(lnArrItemPtr);
1185	}
1186    } else if (objc == 2) {
1187	/*
1188	 * New coordinates given on command line in form "pathName coords x y"
1189	 */
1190
1191	if ((Tk_CanvasGetCoordFromObj(interp, canvas, objv[0],
1192			&lnArrItemPtr->xRef) != TCL_OK)
1193		|| (Tk_CanvasGetCoordFromObj(interp, canvas, objv[1],
1194			&lnArrItemPtr->yRef) != TCL_OK)) {
1195	    return TCL_ERROR;
1196	}
1197	updateCvs(lnArrItemPtr);
1198    } else {
1199	Tcl_AppendResult(interp, "Coordinates must be specified as",
1200		" \"x y\" or \"{x y}\" or \"dump\"", NULL);
1201	return TCL_ERROR;
1202    }
1203    return TCL_OK;
1204}
1205
1206/*
1207 *--------------------------------------------------------------
1208 *
1209 * deleteProc --
1210 *
1211 *	This procedure is called to clean up the data structure
1212 *	associated with a geomap_lnarr item.
1213 *
1214 * Results:
1215 *	None.
1216 *
1217 * Side effects:
1218 *	Resources associated with itemPtr are released.
1219 *
1220 *--------------------------------------------------------------
1221 */
1222
1223static void
1224deleteProc(canvas, itemPtr, display)
1225    Tk_Canvas canvas;			/* Info about overall canvas widget. */
1226    Tk_Item *itemPtr;			/* Item that is being deleted. */
1227    Display *display;			/* Display containing window for
1228					 * canvas. */
1229{
1230    LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
1231    int nl;
1232
1233    Tclgeomap_CnxProjUpdateTask(lnArrItemPtr->proj, lnArrItemPtr);
1234    Tclgeomap_CnxProjDeleteTask(lnArrItemPtr->proj, lnArrItemPtr);
1235    DeleteRotation(lnArrItemPtr->rxn);
1236    Tclgeomap_CnxLnArrDeleteTask(lnArrItemPtr->tclGeoLnArr,
1237	    lnArrItemPtr);
1238    MapLnArrDestroy(lnArrItemPtr->mapLnArr);
1239    for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1240	CKFREE((char *)lnArrItemPtr->coordPtrPtr[nl]);
1241    }
1242    CKFREE((char *)lnArrItemPtr->coordPtrPtr);
1243    if (lnArrItemPtr->lineGC != None) {
1244	Tk_FreeGC(display, lnArrItemPtr->lineGC);
1245    }
1246    if (lnArrItemPtr->fillGC != None) {
1247	Tk_FreeGC(display, lnArrItemPtr->fillGC);
1248    }
1249    Tk_FreeOptions(geoLnArrType.configSpecs, (char *)lnArrItemPtr,
1250	    display, 0);
1251}
1252
1253/*
1254 *--------------------------------------------------------------
1255 *
1256 * forgetLnArr --
1257 *
1258 * 	This procedure modifies a LnArrItem if the linearray it is
1259 * 	displaying is deleted.
1260 *
1261 * Results:
1262 * 	None.
1263 *
1264 * Side effects:
1265 * 	The item is removed from the display and appropriate members are
1266 * 	updated.
1267 *
1268 *--------------------------------------------------------------
1269 */
1270
1271static void
1272forgetLnArr(clientData)
1273    ClientData clientData;	/* Geolinearray item */
1274{
1275    LnArrItem *lnArrItemPtr = (LnArrItem *)clientData;
1276    Tk_Window tkwin = Tk_CanvasTkwin(lnArrItemPtr->canvas);
1277
1278    lnArrItemPtr->tclGeoLnArr = NULL;
1279    if (lnArrItemPtr->lineGC != None) {
1280	Tk_FreeGC(Tk_Display(tkwin), lnArrItemPtr->lineGC);
1281    }
1282    lnArrItemPtr->lineGC = None;
1283    if (lnArrItemPtr->fillGC != None) {
1284	Tk_FreeGC(Tk_Display(tkwin), lnArrItemPtr->fillGC);
1285    }
1286    lnArrItemPtr->fillGC = None;
1287    hide(lnArrItemPtr);
1288}
1289
1290/*
1291 *--------------------------------------------------------------
1292 *
1293 * forgetProj --
1294 *
1295 * 	This procedure modifies a LnArrItem if the projection it is
1296 * 	using is deleted.
1297 *
1298 * Results:
1299 * 	None.
1300 *
1301 * Side effects:
1302 * 	The item is removed from the display and appropriate members are
1303 * 	updated.
1304 *
1305 *--------------------------------------------------------------
1306 */
1307
1308static void
1309forgetProj(clientData)
1310    ClientData clientData;	/* Geolinearray item */
1311{
1312    LnArrItem *lnArrItemPtr = (LnArrItem *)clientData;
1313
1314    lnArrItemPtr->proj = NULL;
1315    hide(lnArrItemPtr);
1316}
1317
1318/*
1319 *--------------------------------------------------------------
1320 *
1321 * hide --
1322 *
1323 * 	This procedure removes a linearray from the display, although the
1324 * 	item continues to exist.
1325 *
1326 * Results:
1327 * 	None.
1328 *
1329 * Side effects:
1330 * 	None.
1331 *
1332 *--------------------------------------------------------------
1333 */
1334
1335static void
1336hide(lnArrItemPtr)
1337    LnArrItem *lnArrItemPtr;	/* Linearray to remove from display */
1338{
1339    int n;
1340
1341    for (n = 0; n < lnArrItemPtr->nLines; n++) {
1342	CKFREE((char *)lnArrItemPtr->coordPtrPtr[n]);
1343	lnArrItemPtr->coordPtrPtr[n] = NULL;
1344    }
1345    lnArrItemPtr->nLines = 0;
1346    Tk_CanvasEventuallyRedraw(lnArrItemPtr->canvas,
1347	    lnArrItemPtr->header.x1, lnArrItemPtr->header.y1,
1348	    lnArrItemPtr->header.x2, lnArrItemPtr->header.y2);
1349
1350    /*
1351     * Increment at lower left deleted at suggestion of Julien Demaria.
1352     */
1353
1354    lnArrItemPtr->header.x1 = lnArrItemPtr->header.y1 = INT_MIN;
1355    lnArrItemPtr->header.x2 = lnArrItemPtr->header.y2 = INT_MIN;
1356}
1357
1358/*
1359 *--------------------------------------------------------------
1360 *
1361 * displayProc --
1362 *
1363 *	This procedure is invoked to draw a geomap_lnarr item in a given
1364 *	drawable.
1365 *
1366 * Results:
1367 *	None.
1368 *
1369 * Side effects:
1370 *	ItemPtr is drawn in drawable using the transformation
1371 *	information in canvas.
1372 *
1373 *--------------------------------------------------------------
1374 */
1375
1376static void
1377displayProc(canvas, itemPtr, display, drawable, x, y, width, height)
1378    Tk_Canvas canvas;			/* Canvas that contains item. */
1379    Tk_Item *itemPtr;			/* Item to be displayed. */
1380    Display *display;			/* Display on which to draw item. */
1381    Drawable drawable;			/* Pixmap or window in which to draw
1382					 * item. */
1383    int x, y, width, height;		/* Describes region of canvas that
1384					 * must be redisplayed (not used). */
1385{
1386    LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
1387    static XPoint *xPtsPtr;
1388    XPoint *pPtr, *ePtr;
1389    double *coordPtr, xc, yc;
1390    unsigned nMax, sz, nl;
1391    int nPts;
1392    Tk_Window tkwin = Tk_CanvasTkwin(canvas);
1393
1394    if (!tkwin || !Tk_IsMapped(tkwin)) {
1395	return;
1396    }
1397    if ( lnArrItemPtr->nLines == 0 || !lnArrItemPtr->nPtsPtr
1398	    || !lnArrItemPtr->coordPtrPtr || !lnArrItemPtr->mapLnArr ) {
1399	return;
1400    }
1401    nMax = lnArrItemPtr->mapLnArr->nMax;
1402    if ((lnArrItemPtr->smooth) && (nMax > 2)) {
1403	sz = sizeof(XPoint) * TkgeomapMakeBezierCurve(canvas, NULL,
1404		(int)nMax, lnArrItemPtr->splineSteps, NULL, NULL);
1405    } else {
1406	sz = sizeof(XPoint) * nMax;
1407    }
1408    xPtsPtr = (XPoint *)CKREALLOC((char *)xPtsPtr, sz);
1409
1410    for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1411	nPts = lnArrItemPtr->nPtsPtr[nl];
1412
1413	/*
1414	 * Put the shape into drawable (X) coordinates.
1415	 */
1416
1417	if ((lnArrItemPtr->smooth) && (nMax > 2)) {
1418	    nPts = TkgeomapMakeBezierCurve(canvas,
1419		    lnArrItemPtr->coordPtrPtr[nl], nPts,
1420		    lnArrItemPtr->splineSteps, xPtsPtr, NULL);
1421	} else {
1422	    for (pPtr = xPtsPtr, ePtr = pPtr + nPts,
1423		    coordPtr = lnArrItemPtr->coordPtrPtr[nl];
1424		    pPtr < ePtr; pPtr++) {
1425		xc = *coordPtr++;
1426		yc = *coordPtr++;
1427		Tk_CanvasDrawableCoords(canvas, xc, yc, &pPtr->x, &pPtr->y);
1428	    }
1429	}
1430
1431	/*
1432	 * Fill in the area.
1433	 */
1434
1435	if (lnArrItemPtr->fillColor != None) {
1436	    XFillPolygon(display, drawable, lnArrItemPtr->fillGC, xPtsPtr,
1437		    nPts, lnArrItemPtr->shape, CoordModeOrigin);
1438	}
1439
1440
1441	/*
1442	 * Draw the line segments.
1443	 */
1444
1445	if (lnArrItemPtr->width > 0
1446		&& lnArrItemPtr->lineColor != None) {
1447	    XDrawLines(display, drawable, lnArrItemPtr->lineGC, xPtsPtr,
1448		    nPts, CoordModeOrigin);
1449	}
1450
1451	/*
1452	 * Draw dots at segment endpoints.
1453	 */
1454
1455	if (lnArrItemPtr->dotSize > 0 && lnArrItemPtr->lineColor != None) {
1456	    for (pPtr = xPtsPtr, ePtr = pPtr + nPts,
1457		    coordPtr = lnArrItemPtr->coordPtrPtr[nl];
1458		    pPtr < ePtr; pPtr++) {
1459		unsigned dotSize = lnArrItemPtr->dotSize;
1460		int spShift = dotSize / 2;
1461		short x, y;
1462
1463		xc = *coordPtr++;
1464		yc = *coordPtr++;
1465		Tk_CanvasDrawableCoords(canvas, xc, yc, &x, &y);
1466		if (dotSize == 1) {
1467		    XDrawPoint(display, drawable, lnArrItemPtr->lineGC, x, y);
1468		} else {
1469		    XFillArc(display, drawable, lnArrItemPtr->lineGC,
1470			    x - spShift, y - spShift, dotSize, dotSize,
1471			    0, 23040);
1472		}
1473	    }
1474	}
1475    }
1476}
1477
1478/*
1479 *--------------------------------------------------------------
1480 *
1481 * pointProc --
1482 *
1483 *	Computes the distance from a given point to a given
1484 *	geomap_lnarr item, in canvas units.
1485 *
1486 * Results:
1487 *	The return value is 0.0 if the point whose x and y coordinates
1488 *	are pointPtr[0] and pointPtr[1] is inside the geomap_lnarr.  If the
1489 *	point isn't inside the geomap_lnarr then the return value is the
1490 *	distance from the point to the geomap_lnarr.
1491 *
1492 * Side effects:
1493 *	None.
1494 *
1495 *--------------------------------------------------------------
1496 */
1497
1498static double
1499pointProc(canvas, itemPtr, pointPtr)
1500    Tk_Canvas canvas;			/* Canvas containing item. */
1501    Tk_Item *itemPtr;			/* Item to check against point. */
1502    double *pointPtr;			/* Pointer to x and y coordinates. */
1503{
1504    LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
1505    int nl, np, nPair;
1506    double **coordPtrPtr;
1507    int *nPtsPtr;
1508    double x0 = pointPtr[0], y0 = pointPtr[1];
1509    double end1Ptr[2], end2Ptr[2];
1510    double *coordPtr;
1511    double d, dx, dy, closest;
1512
1513    if ( !itemPtr || !lnArrItemPtr->coordPtrPtr || lnArrItemPtr->nLines == 0 ) {
1514	return DBL_MAX;
1515    }
1516    coordPtrPtr = lnArrItemPtr->coordPtrPtr;
1517    nPtsPtr = lnArrItemPtr->nPtsPtr;
1518    if (lnArrItemPtr->fillColor != None) {
1519	/*
1520	 * Treat all lines as closed polygons.
1521	 */
1522
1523	closest = DBL_MAX;
1524	for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1525	    d = TkgeomapPolygonToPoint(coordPtrPtr[nl], nPtsPtr[nl], pointPtr);
1526	    if (d == 0.0) {
1527		return 0.0;
1528	    } else if (d < closest) {
1529		closest = d;
1530	    }
1531	}
1532    } else if (lnArrItemPtr->width < 0) {
1533	/*
1534	 * Points are not connected.  Return distance to nearest point.
1535	 */
1536
1537	closest = DBL_MAX;
1538	for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1539	    coordPtr = coordPtrPtr[nl];
1540	    for (np = 0; np < nPtsPtr[nl]; np++) {
1541		dx = x0 - *coordPtr++;
1542		dy = y0 - *coordPtr++;
1543		d = dx * dx + dy * dy;
1544		closest = (d < closest) ? d : closest;
1545	    }
1546	}
1547	closest = sqrt(closest);
1548    } else {
1549	/*
1550	 * Lines are either closed polygons that are not filled, or they
1551	 * are open.
1552	 */
1553
1554	closest = DBL_MAX;
1555	for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1556	    coordPtr = coordPtrPtr[nl];
1557	    nPair = nPtsPtr[nl] - 1;
1558	    if (nPair == 0) {
1559		continue;
1560	    }
1561	    for (np = 0, coordPtr = coordPtrPtr[nl];
1562		    np < nPair; np++, coordPtr += 2) {
1563		end1Ptr[0] = coordPtr[0];
1564		end1Ptr[1] = coordPtr[1];
1565		end2Ptr[0] = coordPtr[2];
1566		end2Ptr[1] = coordPtr[3];
1567		d = TkgeomapLineToPoint(end1Ptr, end2Ptr, pointPtr);
1568		closest = (d < closest) ? d : closest;
1569	    }
1570	}
1571    }
1572    return closest;
1573}
1574
1575/*
1576 *--------------------------------------------------------------
1577 *
1578 * areaProc --
1579 *
1580 *	This procedure is called to determine whether an item
1581 *	lies entirely inside, entirely outside, or overlapping
1582 *	a given rectangle.
1583 *
1584 * Results:
1585 *	-1 is returned if the item is entirely outside the area
1586 *	given by rectPtr, 0 if it overlaps, and 1 if it is entirely
1587 *	inside the given area.
1588 *
1589 * Side effects:
1590 *	None.
1591 *
1592 *--------------------------------------------------------------
1593 */
1594
1595static int areaProc(canvas, itemPtr, rectPtr)
1596    Tk_Canvas canvas;		/* Canvas containing item. */
1597    Tk_Item *itemPtr;		/* Item to check against rectangle. */
1598    double *rectPtr;		/* Pointer to array of four coordinates
1599				 * (x1, y1, x2, y2) describing rectangular
1600				 * area.  */
1601{
1602    LnArrItem *lnArrItemPtr = (LnArrItem *)itemPtr;
1603    double x1, y1, x2, y2;	/* Limits of rectPtr */
1604    double x, y;
1605    int nl, np, nPts;
1606    double **coordPtrPtr = lnArrItemPtr->coordPtrPtr;
1607    int *nPtsPtr = lnArrItemPtr->nPtsPtr;
1608    double end1Ptr[2], end2Ptr[2];
1609    double *coordPtr;
1610    int currInside, prevInside;	/* Status of current and previous polygon, line,
1611				 * or point.  1 means inside, 0 means
1612				 * overlapping, and -1 means outside. */
1613
1614    if ( !lnArrItemPtr->coordPtrPtr || lnArrItemPtr->nLines == 0 ) {
1615	return -1;
1616    }
1617    x1 = rectPtr[0];
1618    y1 = rectPtr[1];
1619    x2 = rectPtr[2];
1620    y2 = rectPtr[3];
1621    if (lnArrItemPtr->fillColor != None) {
1622	/*
1623	 * Treat all lines as closed polygons.
1624	 */
1625
1626	prevInside = currInside
1627	    = TkgeomapPolygonToArea(coordPtrPtr[0], nPtsPtr[0], rectPtr);
1628	for (nl = 1; nl < lnArrItemPtr->nLines; nl++) {
1629	    currInside = TkgeomapPolygonToPoint(coordPtrPtr[nl], nPtsPtr[nl],
1630		    rectPtr);
1631	    if (currInside == 0 || currInside != prevInside) {
1632		return 0;
1633	    }
1634	    prevInside = currInside;
1635	}
1636	return currInside;
1637    } else if (lnArrItemPtr->width < 0) {
1638	/*
1639	 * Points are not connected.
1640	 */
1641
1642	x = coordPtrPtr[0][0];
1643	y = coordPtrPtr[0][1];
1644	prevInside = currInside
1645	    = (x > x1 && x < x2 && y > y1 && y < y2) ? 1 : 0;
1646	for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1647	    coordPtr = coordPtrPtr[nl];
1648	    for (np = 0; np < nPtsPtr[nl]; np++) {
1649		x = *coordPtr++;
1650		y = *coordPtr++;
1651		currInside = (x > x1 && x < x2 && y > y1 && y < y2) ? 1 : 0;
1652		if (currInside != prevInside) {
1653		    return 0;
1654		}
1655		prevInside = currInside;
1656	    }
1657	}
1658	return currInside;
1659    } else {
1660	/*
1661	 * Lines are either closed polygons that are not filled, or they
1662	 * are open lines.
1663	 */
1664
1665	nPts = nPtsPtr[0];
1666	end1Ptr[0] = coordPtrPtr[0][0];
1667	end1Ptr[1] = coordPtrPtr[0][1];
1668	end2Ptr[0] = coordPtrPtr[0][2 * nPts - 2];
1669	end2Ptr[1] = coordPtrPtr[0][2 * nPts - 1];
1670	if ((end1Ptr[0] == end2Ptr[0]) && (end1Ptr[1] == end2Ptr[1])) {
1671	    /*
1672	     * First line in the linearray is closed.  Treat it as a
1673	     * polygon.
1674	     */
1675
1676	    prevInside = currInside
1677		= TkgeomapPolygonToArea(coordPtrPtr[0], nPts, rectPtr);
1678	    if (lnArrItemPtr->nLines == 1) {
1679		return prevInside;
1680	    }
1681
1682	} else {
1683	    /*
1684	     * First line in the linearray is open.  Compute the distances
1685	     * to the segments.
1686	     */
1687
1688	    x = coordPtrPtr[0][0];
1689	    y = coordPtrPtr[0][1];
1690	    prevInside = currInside
1691		= (x > x1 && x < x2 && y > y1 && y < y2) ? 1 : 0;
1692	    coordPtr = coordPtrPtr[0];
1693	    for (np = 0; np < nPtsPtr[0]; np++) {
1694		x = *coordPtr++;
1695		y = *coordPtr++;
1696		currInside = (x > x1 && x < x2 && y > y1 && y < y2) ? 1 : 0;
1697		if (currInside != prevInside) {
1698		    return 0;
1699		}
1700		currInside = prevInside;
1701	    }
1702	}
1703	for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1704	    coordPtr = coordPtrPtr[nl];
1705	    nPts = nPtsPtr[nl];
1706	    end1Ptr[0] = coordPtr[0];
1707	    end1Ptr[1] = coordPtr[1];
1708	    end2Ptr[0] = coordPtr[2 * nPts - 2];
1709	    end2Ptr[1] = coordPtr[2 * nPts - 1];
1710	    if ((end1Ptr[0] == end2Ptr[0]) && (end1Ptr[1] == end2Ptr[1])) {
1711		/*
1712		 * This line in the linearray is closed.  Treat it as a
1713		 * polygon.
1714		 */
1715
1716		currInside = TkgeomapPolygonToArea(coordPtrPtr[0], nPts,
1717			rectPtr);
1718		if (currInside != prevInside) {
1719		    return 0;
1720		}
1721		currInside = prevInside;
1722	    } else {
1723		/*
1724		 * This line in the linearray is open.  Compute the distances
1725		 * to the segments.
1726		 */
1727
1728		for (np = 0; np < nPts; np++) {
1729		    x = *coordPtr++;
1730		    y = *coordPtr++;
1731		    currInside = (x > x1 && x < x2 && y > y1 && y < y2) ? 1 : 0;
1732		    if (currInside != prevInside) {
1733			return 0;
1734		    }
1735		    currInside = prevInside;
1736		}
1737	    }
1738	}
1739    }
1740    return -1;
1741}
1742
1743/*
1744 *--------------------------------------------------------------
1745 *
1746 * postscriptProc --
1747 *
1748 *	This procedure is called to generate Postscript for
1749 *	geomap_lnarr items.
1750 *
1751 * Results:
1752 *	The return value is a standard Tcl result.  If an error
1753 *	occurs in generating Postscript then an error message is
1754 *	left in interp->result, replacing whatever used to be there.
1755 *	If no error occurs, then Postscript for the item is appended
1756 *	to the result.
1757 *
1758 * Side effects:
1759 *	None.
1760 *
1761 *--------------------------------------------------------------
1762 */
1763
1764static int
1765postscriptProc(interp, canvas, itemPtr, prepass)
1766    Tcl_Interp *interp;			/* Leave Postscript or error message
1767					 * here. */
1768    Tk_Canvas canvas;			/* Information about overall canvas. */
1769    Tk_Item *itemPtr;			/* Item for which Postscript is
1770					 * wanted. */
1771    int prepass;			/* 1 means this is a prepass to
1772					 * collect font information;  0 means
1773					 * final Postscript is being created.*/
1774{
1775    LnArrItem *lnArrItemPtr;	/* Geomap in which to draw line array */
1776    Tk_Window tkwin = Tk_CanvasTkwin(canvas);
1777    static double *coord1Ptr;		/* Array of canvas coordinates for
1778    					 * one line.  This pointer holds
1779					 * the result of CKREALLOC. */
1780    double *coordPtr;			/* Array of canvas coordinates for
1781    					 * one line.  May be the same as
1782					 * coord1Ptr, or may point into the
1783					 * item's coordinates array. */
1784    double x, y;
1785    int nl, nPts, np;			/* Loop indices and limits */
1786    char bytes[200];			/* Printing space */
1787    double cnr[8];			/* 4 corner points in canvas coord's */
1788
1789    lnArrItemPtr = (LnArrItem *)itemPtr;
1790    if ( !lnArrItemPtr->tclGeoLnArr ) {
1791	return TCL_OK;
1792    }
1793    if ( lnArrItemPtr->nLines == 0 || !lnArrItemPtr->nPtsPtr
1794	    || !lnArrItemPtr->coordPtrPtr || !lnArrItemPtr->mapLnArr ) {
1795	Tcl_AppendResult(interp,
1796		"Could not generate postscript for linearray ",
1797		Tclgeomap_LnArrName(lnArrItemPtr->tclGeoLnArr), NULL);
1798	return TCL_ERROR;
1799    }
1800
1801    Tcl_AppendResult(interp, "%% Drawing linearray ",
1802		Tclgeomap_LnArrName(lnArrItemPtr->tclGeoLnArr), "\n", NULL);
1803
1804    /*
1805     * Create clipping region for item
1806     */
1807
1808    Tcl_AppendResult(interp, "%% Create clipping region for item\n", NULL);
1809    cnr[0] = 0;
1810    cnr[1] = 0;
1811    cnr[2] = Tk_Width(tkwin);
1812    cnr[3] = 0;
1813    cnr[4] = Tk_Width(tkwin);
1814    cnr[5] = Tk_Height(tkwin);
1815    cnr[6] = 0;
1816    cnr[7] = Tk_Height(tkwin);
1817    Tk_CanvasPsPath(interp, canvas, cnr, 4);
1818    Tcl_AppendResult(interp, "closepath clip\n", NULL);
1819
1820    for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
1821
1822	nPts = lnArrItemPtr->nPtsPtr[nl];
1823	if ( !lnArrItemPtr->smooth || nPts < 3 ) {
1824	    coordPtr = lnArrItemPtr->coordPtrPtr[nl];
1825	} else {
1826	    nPts = TkgeomapMakeBezierCurve(canvas, NULL,
1827		    lnArrItemPtr->nPtsPtr[nl], lnArrItemPtr->splineSteps,
1828		    NULL, NULL);
1829	    coordPtr = coord1Ptr = (double *)CKREALLOC((char *)coord1Ptr,
1830		    nPts * 2 * sizeof(double));
1831	    nPts = TkgeomapMakeBezierCurve(canvas,
1832		    lnArrItemPtr->coordPtrPtr[nl], lnArrItemPtr->nPtsPtr[nl],
1833		    lnArrItemPtr->splineSteps, NULL, coordPtr);
1834	}
1835
1836	/*
1837	 * Fill in the area.
1838	 */
1839
1840	if (lnArrItemPtr->fillColor) {
1841	    Tk_CanvasPsColor(interp, canvas, lnArrItemPtr->fillColor);
1842	    Tcl_AppendResult(interp, "newpath\n", NULL);
1843	    Tk_CanvasPsPath(interp, canvas, coordPtr, nPts);
1844	    Tcl_AppendResult(interp, "closepath\n", NULL);
1845	    if (lnArrItemPtr->stipple != None) {
1846		Tcl_AppendResult(interp, "clip\n", NULL);
1847		Tcl_AppendResult(interp, "newpath\n", NULL);
1848		Tk_CanvasPsPath(interp, canvas, coordPtr, nPts);
1849		Tcl_AppendResult(interp, "closepath\n", NULL);
1850		if (Tk_CanvasPsStipple(interp, canvas, lnArrItemPtr->stipple)
1851			== TCL_ERROR) {
1852		    return TCL_ERROR;
1853		}
1854	    } else {
1855		Tcl_AppendResult(interp, "fill\n", NULL);
1856	    }
1857	}
1858
1859	/*
1860	 * Draw the outline.
1861	 */
1862
1863	if (lnArrItemPtr->width > 0
1864		&& lnArrItemPtr->lineColor != None) {
1865	    sprintf(bytes, "%d", lnArrItemPtr->width);
1866	    Tcl_AppendResult(interp, bytes, " setlinewidth\n", NULL);
1867	    if (lnArrItemPtr->lineStyle == LineSolid) {
1868		Tcl_AppendResult(interp, "[] 0 setdash\n", NULL);
1869	    } else if (lnArrItemPtr->lineStyle == LineOnOffDash) {
1870		sprintf(bytes, "[%d %d] 0 setdash\n",
1871			lnArrItemPtr->dashes, lnArrItemPtr->dashes);
1872		Tcl_AppendResult(interp, bytes, NULL);
1873	    }
1874	    Tk_CanvasPsColor(interp, canvas, lnArrItemPtr->lineColor);
1875	    Tcl_AppendResult(interp, "newpath\n", NULL);
1876	    Tk_CanvasPsPath(interp, canvas, coordPtr, nPts);
1877	    Tcl_AppendResult(interp, "stroke\n", NULL);
1878	}
1879
1880	/*
1881	 * Draw dots at segment endpoints.
1882	 */
1883
1884	if (lnArrItemPtr->dotSize != 0) {
1885	    Tk_CanvasPsColor(interp, canvas, lnArrItemPtr->lineColor);
1886	    for (np = 0; np < nPts; np++) {
1887		x = *coordPtr++;
1888		y = *coordPtr++;
1889		if (lnArrItemPtr->dotSize != 0) {
1890		    sprintf(bytes, "newpath %f %f %f",
1891			    x, y, 0.5 * lnArrItemPtr->dotSize);
1892		    Tcl_AppendResult(interp, bytes, " 0 360 arc fill\n", NULL);
1893		}
1894	    }
1895	}
1896    }
1897
1898    return TCL_OK;
1899}
1900
1901/*
1902 *--------------------------------------------------------------
1903 *
1904 * scaleProc --
1905 *
1906 *	This function rescales a geomap_lnarr item.  It should not be confused
1907 *	with the "-scale" configuration option, which sets the cartographic
1908 *	scale of the map containing the item.
1909 *
1910 * Results:
1911 *	None.
1912 *
1913 * Side effects:
1914 * 	The reference point for a geomap_lnarr item is moved according to the
1915 * 	transformation:
1916 *		xRef' = originX + scaleX*(xRef-originX)
1917 *		yRef' = originY + scaleY*(yRef-originY)
1918 *	The geomap_lnarr's points on the canvas may or may not move.
1919 *
1920 *--------------------------------------------------------------
1921 */
1922
1923static void
1924scaleProc(canvas, itemPtr, originX, originY, scaleX, scaleY)
1925    Tk_Canvas canvas;			/* Canvas containing rectangle. */
1926    Tk_Item *itemPtr;			/* Rectangle to be scaled. */
1927    double originX, originY;		/* Origin about which to scale rect. */
1928    double scaleX;			/* Amount to scale in X direction. */
1929    double scaleY;			/* Amount to scale in Y direction. */
1930{
1931    LnArrItem *lnArrItemPtr = (LnArrItem *) itemPtr;
1932
1933    lnArrItemPtr->xRef = originX + scaleX * (lnArrItemPtr->xRef - originX);
1934    lnArrItemPtr->yRef = originY + scaleY * (lnArrItemPtr->yRef - originY);
1935    updateCvs(lnArrItemPtr);
1936    return;
1937}
1938
1939/*
1940 *--------------------------------------------------------------
1941 *
1942 * translateProc --
1943 *
1944 *	This procedure is called to move an item by a given amount.
1945 *
1946 * Results:
1947 *	None.
1948 *
1949 * Side effects:
1950 *	The position of the item is offset by (xDelta, yDelta), and
1951 *	the bounding box is updated in the generic part of the item
1952 *	structure.
1953 *
1954 *--------------------------------------------------------------
1955 */
1956
1957static void
1958translateProc(canvas, itemPtr, deltaX, deltaY)
1959    Tk_Canvas canvas;			/* Canvas containing item. */
1960    Tk_Item *itemPtr;			/* Item that is being moved. */
1961    double deltaX, deltaY;		/* Amount by which item is to be
1962					 * moved. */
1963{
1964    LnArrItem *lnArrItemPtr = (LnArrItem *) itemPtr;
1965
1966    lnArrItemPtr->xRef += deltaX;
1967    lnArrItemPtr->yRef += deltaY;
1968    updateCvs(lnArrItemPtr);
1969}
1970
1971/*
1972 *------------------------------------------------------------------------
1973 *
1974 * updateMap --
1975 *
1976 * 	This procedure updates the mapLnArr member of a LnArrItem.
1977 *
1978 * Results:
1979 *	None.
1980 *
1981 * Side effects:
1982 * 	A new MapLnArr is created and added to a LnArrItem.
1983 * 	A request is made for update of the canvas coordinates.
1984 *
1985 *------------------------------------------------------------------------
1986 */
1987
1988static void
1989updateMap(clientData)
1990    ClientData clientData;	/* Geolinearray item in need of update */
1991{
1992    LnArrItem *lnArrItemPtr = (LnArrItem *)clientData;
1993    GeoLnArr geoLnArr = (GeoLnArr)lnArrItemPtr->tclGeoLnArr;
1994    GeoProj proj = (GeoProj)lnArrItemPtr->proj;
1995    Rotation rxn = lnArrItemPtr->rxn;
1996    MapLnArr mapLnArr;
1997
1998    if ( !geoLnArr || !proj || !rxn ||
1999	    !(mapLnArr = GeoLnArrToMap(geoLnArr, proj, rxn)) ) {
2000	if (lnArrItemPtr->mapLnArr) {
2001	    MapLnArrDestroy(lnArrItemPtr->mapLnArr);
2002	}
2003	lnArrItemPtr->mapLnArr = NULL;
2004	lnArrItemPtr->updateMap = 0;
2005	hide(lnArrItemPtr);
2006	return;
2007    }
2008    if (lnArrItemPtr->mapLnArr) {
2009	MapLnArrDestroy(lnArrItemPtr->mapLnArr);
2010    }
2011    lnArrItemPtr->mapLnArr = mapLnArr;
2012    lnArrItemPtr->updateMap = 0;
2013    updateCvs(lnArrItemPtr);
2014}
2015
2016/*
2017 *------------------------------------------------------------------------
2018 *
2019 * updateCvs --
2020 *
2021 * 	This procedure updates the canvas coordinates of a geomap_lnarr item.
2022 *
2023 * Results:
2024 *	None.
2025 *
2026 * Side effects:
2027 * 	The coords member and bounding box of the geomap_lnarr item are updated.
2028 *
2029 *------------------------------------------------------------------------
2030 */
2031
2032static void
2033updateCvs(lnArrItemPtr)
2034    LnArrItem *lnArrItemPtr;	/* Geolinearray item to update */
2035{
2036    Tk_Canvas canvas = lnArrItemPtr->canvas;
2037    Tk_Window tkwin = Tk_CanvasTkwin(canvas);
2038    MapLnArr mapLnArr;
2039    GeoProj proj = (GeoProj)lnArrItemPtr->proj;
2040    Rotation rxn = lnArrItemPtr->rxn;
2041    static Tcl_Obj *cvsPerMObj;		/* Object storing measure for 1 meter */
2042    double cvsPerM;			/* Canvas units per meter on display */
2043    double cvsPerMapM;			/* Canvas units per meter on map
2044					 * (2D surface which contains projected
2045					 * and rotated lat-lons) */
2046    double xRef = lnArrItemPtr->xRef;
2047    double yRef = lnArrItemPtr->yRef;
2048    int x, y;
2049    int x1, y1, x2, y2;
2050    int width, height;
2051    MapPt mRefPt;			/* Reference point in map coordinates */
2052    double ordMax, absMax, ordMin, absMin;
2053    double **cPtrPtr;			/* lnArrItemPtr->coordPtrPtr */
2054    int *nPtsPtr;			/* lnArrItemPtr->nPtsPtr */
2055    unsigned nl, nls, np;
2056    unsigned nlc;			/* Number of lines in canvas */
2057    unsigned r;				/* Dot radius */
2058    int noLines, noFill;		/* Booleans to indicate whether
2059					 * anything visible in display */
2060
2061    if ( !lnArrItemPtr->tclGeoLnArr || !proj || !rxn ) {
2062	if (lnArrItemPtr->mapLnArr) {
2063	    MapLnArrDestroy(lnArrItemPtr->mapLnArr);
2064	}
2065	lnArrItemPtr->mapLnArr = NULL;
2066	lnArrItemPtr->updateMap = 0;
2067	hide(lnArrItemPtr);
2068	return;
2069    }
2070    noLines = !lnArrItemPtr->lineColor
2071	|| (lnArrItemPtr->width <= 0 && lnArrItemPtr->dotSize <= 0);
2072    noFill = !lnArrItemPtr->fillColor;
2073    if (noLines && noFill) {
2074	hide(lnArrItemPtr);
2075	return;
2076    }
2077    if (lnArrItemPtr->updateMap) {
2078	updateMap(lnArrItemPtr);
2079    }
2080    if ( !(mapLnArr = lnArrItemPtr->mapLnArr) ) {
2081	hide(lnArrItemPtr);
2082	return;
2083    }
2084    mRefPt = Rotate(LatLonToProj(lnArrItemPtr->refPt, proj), rxn);
2085    if ( !cvsPerMObj ) {
2086	cvsPerMObj = Tcl_NewStringObj("100.0c", -1);
2087    }
2088    Tk_CanvasGetCoordFromObj(NULL, canvas, cvsPerMObj, &cvsPerM);
2089    cvsPerMapM = cvsPerM * lnArrItemPtr->scale;
2090    Tk_CanvasEventuallyRedraw(canvas,
2091	    lnArrItemPtr->header.x1, lnArrItemPtr->header.y1,
2092	    lnArrItemPtr->header.x2, lnArrItemPtr->header.y2);
2093    if (!tkwin || !Tk_IsMapped(tkwin)) {
2094	/*
2095	 * If canvas limits are not know yet, draw everything.
2096	 */
2097
2098	ordMax = DBL_MAX;
2099	absMax = DBL_MAX;
2100	ordMin = -DBL_MAX;
2101	absMin = -DBL_MAX;
2102    } else {
2103	/*
2104	 * If canvas limits are known, only draw lines on canvas.
2105	 */
2106
2107	width = Tk_Width(tkwin);
2108	height = Tk_Height(tkwin);
2109	ordMax = mRefPt.ord + yRef / cvsPerMapM;
2110	absMax = mRefPt.abs + (width - xRef) / cvsPerMapM;
2111	ordMin = mRefPt.ord - (height -yRef) / cvsPerMapM;
2112	absMin = mRefPt.abs - xRef / cvsPerMapM;
2113    }
2114    nls = mapLnArr->nLines;
2115
2116    /*
2117     * Reinitialize coords arrays and array sizes in lnArrItemPtr.
2118     */
2119
2120    for (nl = 0; nl < lnArrItemPtr->nLines; nl++) {
2121	CKFREE((char *)lnArrItemPtr->coordPtrPtr[nl]);
2122	lnArrItemPtr->coordPtrPtr[nl] = NULL;
2123	lnArrItemPtr->nPtsPtr[nl] = 0;
2124    }
2125    lnArrItemPtr->nLines = 0;
2126    cPtrPtr = lnArrItemPtr->coordPtrPtr;
2127    cPtrPtr = (double **)CKREALLOC((char *)cPtrPtr, nls * sizeof(double *));
2128    nPtsPtr = lnArrItemPtr->nPtsPtr;
2129    nPtsPtr = (int *)CKREALLOC((char *)nPtsPtr, nls * sizeof(int));
2130
2131    x1 = y1 = INT_MAX;
2132    x2 = y2 = INT_MIN;
2133    for (nl = 0, nlc = 0; nl < nls; nl++) {
2134	/*
2135	 * Compute canvas coordinates for one line from mapLnArr,
2136	 * if it has points in the window.
2137	 */
2138
2139	MapLn mapLn = MapLnArrGetLine(mapLnArr, nl);
2140	cPtrPtr[nl] = NULL;
2141	if (mapLn->ordMax > ordMin && mapLn->ordMin < ordMax
2142		&& mapLn->absMax > absMin && mapLn->absMin < absMax) {
2143	    double *coordPtr;
2144
2145	    nPtsPtr[nlc] = mapLn->nPts;
2146	    cPtrPtr[nlc] = (double *)CKALLOC(nPtsPtr[nlc] * 2 * sizeof(double));
2147	    for (np = 0, coordPtr = cPtrPtr[nlc]; np < nPtsPtr[nlc]; np++) {
2148		MapPt mapPt = MapLnGetPt(mapLn, np);
2149		x = xRef + (mapPt.abs - mRefPt.abs) * cvsPerMapM;
2150		x1 = (x < x1) ? x : x1;
2151		x2 = (x > x2) ? x : x2;
2152		*coordPtr++ = x;
2153		y = yRef - (mapPt.ord - mRefPt.ord) * cvsPerMapM;
2154		y1 = (y < y1) ? y : y1;
2155		y2 = (y > y2) ? y : y2;
2156		*coordPtr++ = y;
2157	    }
2158	    nlc++;
2159	}
2160    }
2161
2162    /*
2163     * Store new coords arrays and array sizes in lnArrItemPtr.
2164     */
2165
2166    lnArrItemPtr->coordPtrPtr
2167	= (double **)CKREALLOC((char *)cPtrPtr, nlc * sizeof(double *));
2168    lnArrItemPtr->nPtsPtr
2169	= (int *)CKREALLOC((char *)nPtsPtr, nlc * sizeof(int));
2170    lnArrItemPtr->nLines = nlc;
2171    r = (lnArrItemPtr->dotSize + 1) / 2;
2172    lnArrItemPtr->header.x1 = x1 - lnArrItemPtr->width - r - 1;
2173    lnArrItemPtr->header.x2 = x2 + lnArrItemPtr->width + r + 1;
2174    lnArrItemPtr->header.y1 = y1 - lnArrItemPtr->width - r - 1;
2175    lnArrItemPtr->header.y2 = y2 + lnArrItemPtr->width + r + 1;
2176    if ( lnArrItemPtr->header.x1 >= lnArrItemPtr->header.x2
2177	    || lnArrItemPtr->header.y1 >= lnArrItemPtr->header.y2 ) {
2178	hide(lnArrItemPtr);
2179	return;
2180    }
2181    Tk_CanvasEventuallyRedraw(lnArrItemPtr->canvas,
2182	    lnArrItemPtr->header.x1, lnArrItemPtr->header.y1,
2183	    lnArrItemPtr->header.x2, lnArrItemPtr->header.y2);
2184}
2185