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