/*
* tclgeomapPlace.c --
*
* This file defines the structures and functions that add the ability
* to manage named geographic locations in Tcl.
*
* Copyright (c) 2004 Gordon D. Carrie. All rights reserved.
*
* Licensed under the Open Software License version 2.1
*
* Please address questions and feedback to user0@tkgeomap.org
*
* @(#) $Id: tclgeomapPlace.c,v 1.5 2004/09/22 21:57:51 tkgeomap Exp $
*
********************************************
*
*/
#include "tclgeomap.h"
#include "tclgeomapInt.h"
/*
* Forward declarations
*/
static Tclgeomap_Place createPlace _ANSI_ARGS_((Tcl_Interp *, char *, GeoPt));
static int geoplaceCallback _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static int placeCmdCallback _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static int new _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static int set _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static int distance _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static int azrng _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static int nearest _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static int step _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static int inrange _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[]));
static void deleteProc _ANSI_ARGS_((ClientData));
/*
* All geoplaces are entered in the following table. One-word-keys are
* Tclgeomap_Place structures. Values are not used.
*/
static Tcl_HashTable places;
/*
* The following array and enum are used to process units on command line.
*/
static char *units[] = {"nmiles", "smiles", "km", "gsdeg", NULL};
enum UnitIdx {NMILES, SMILES, KM, GSDEG};
/*
*------------------------------------------------------------------------
*
* TclgeomapPlaceInit --
*
* This procedure initializes the Tclgeomap_Place interface and provides
* the tclgeoplace package.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* The "geomap::place" command is added to the interpreter.
* The places table (defined above) is initialized.
* The geoplaceSubCmdNmPtr and plcCmdSubCmdNmPtr subcommand arrays are
* initialized.
*
*
*------------------------------------------------------------------------
*/
int
TclgeomapPlaceInit(interp)
Tcl_Interp *interp; /* Current Tcl interpreter */
{
static int loaded; /* Tell if package already loaded */
if (loaded) {
return TCL_OK;
}
#ifdef USE_TCL_STUBS
if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
return TCL_ERROR;
}
#endif
Tcl_CreateObjCommand(interp, "::geomap::place", geoplaceCallback, NULL,
NULL);
Tcl_InitHashTable(&places, TCL_ONE_WORD_KEYS);
loaded = 1;
return TCL_OK;
}
/*
*------------------------------------------------------------------------
*
* geoplaceCallback --
*
* This is the callback for the "geomap::place" command.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* This procedure invokes the callback corresponding to the first
* argument given to the "geomap::place" command. Side effects depend
* on the subcommand called.
*
*------------------------------------------------------------------------
*/
int
geoplaceCallback(clientData, interp, objc, objv)
ClientData clientData; /* Not used */
Tcl_Interp *interp; /* Current interpreter */
int objc; /* Number of arguments */
Tcl_Obj *const objv[]; /* Argument objects */
{
char *nmPtr[] = {
"new", "set", "distance", "azrng", "nearest", "step", "inrange",
NULL
};
Tcl_ObjCmdProc *procPtr[] = {
new, set, distance, azrng, nearest, step, inrange
};
int i;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], nmPtr, "subcommand", 0, &i)
!= TCL_OK) {
return TCL_ERROR;
}
return (procPtr[i])(NULL, interp, objc, objv);
}
/*
*------------------------------------------------------------------------
*
* placeCmdCallback --
*
* This is the callback for a commands of form "placeName subcommand ..."
*
* Results:
* A standard Tcl result.
*
* Side effects:
* This procedure invokes the callback corresponding to the first
* argument given to the "placeName" command. Side effects depend
* on the subcommand called.
*
*------------------------------------------------------------------------
*/
int
placeCmdCallback(clientData, interp, objc, objv)
ClientData clientData; /* A Tclgeomap_Place struct */
Tcl_Interp *interp; /* Current interpreter */
int objc; /* Number of arguments */
Tcl_Obj *const objv[]; /* Argument objects */
{
int i;
static char *nmPtr[] = {
"set", "nearest", "step", "inrange", NULL
};
Tcl_ObjCmdProc *procPtr[] = {
set, nearest, step, inrange
};
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], nmPtr, "subcommand", 0, &i)
!= TCL_OK) {
return TCL_ERROR;
}
return (procPtr[i])(clientData, interp, objc, objv);
}
/*
*------------------------------------------------------------------------
*
* createPlace --
*
* This procedure creates a new place in the database.
*
* Results:
* If successful, this procedure returns a Tclgeomap_Place structure whose
* coordinates are set to the given lat-lon. The return value is
* dynamically allocated and should eventually be freed with a call to
* CKFREE.
*
* Side effects:
* A new Tcl command is created, whose name is the place name,
* to access and manipulate the place.
* The new Tclgeomap_Place structure is added to the places table.
*
*------------------------------------------------------------------------
*/
Tclgeomap_Place
createPlace(interp, name, geoPt)
Tcl_Interp *interp; /* Current interpreter */
char *name; /* Place name = place command name. Can
* contain namespace qualifiers, otherwise it
* becomes global. */
GeoPt geoPt; /* Coordinates of the new place */
{
struct Tclgeomap_Place *plcPtr; /* Structure for the new place */
int newPtr; /* Not used */
plcPtr = (Tclgeomap_Place)CKALLOC(sizeof(*plcPtr));
Tcl_CreateHashEntry(&places, (char *)plcPtr, &newPtr);
plcPtr->interp = interp;
plcPtr->geoPt = geoPt;
Tcl_InitHashTable(&plcPtr->updateTasks, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(&plcPtr->deleteTasks, TCL_ONE_WORD_KEYS);
plcPtr->cmd = Tcl_CreateObjCommand(interp, name, placeCmdCallback,
(ClientData)plcPtr, deleteProc);
return plcPtr;
}
/*
*------------------------------------------------------------------------
*
* Tclgeomap_AddPlaceUpdateTask --
*
* This procedure arranges for a function to be called when a place
* moves.
*
* Results:
* None.
*
* Side effects:
* See user documentation.
*
*------------------------------------------------------------------------
*/
void
Tclgeomap_AddPlaceUpdateTask(placePtr, proc, clientData)
Tclgeomap_Place placePtr;
Tclgeomap_PlaceUpdateProc proc;
ClientData clientData;
{
int n;
Tcl_HashEntry *entry;
if ( !placePtr || !proc || !clientData ) {
return;
}
entry = Tcl_CreateHashEntry(&placePtr->updateTasks, clientData, &n);
Tcl_SetHashValue(entry, (ClientData)proc);
}
/*
*------------------------------------------------------------------------
*
* Tclgeomap_CnxPlaceUpdateTask --
*
* This procedure cancels a callback added by Tclgeomap_AddPlaceUpdateTask.
*
* Results:
* None.
*
* Side effects:
* See the user documentation.
*
*------------------------------------------------------------------------
*/
void
Tclgeomap_CnxPlaceUpdateTask(placePtr, clientData)
Tclgeomap_Place placePtr;
ClientData clientData;
{
Tcl_HashEntry *entry;
if ( !placePtr || !clientData ) {
return;
}
if ( !(entry = Tcl_FindHashEntry(&placePtr->updateTasks,
(char *)clientData)) ) {
return;
}
Tcl_DeleteHashEntry(entry);
}
/*
*------------------------------------------------------------------------
*
* Tclgeomap_AddPlaceDeleteTask --
*
* This procedure arranges for a function to be called when a place
* is deleted.
*
* Results:
* None.
*
* Side effects:
* See the user documentation.
*
*------------------------------------------------------------------------
*/
void
Tclgeomap_AddPlaceDeleteTask(placePtr, proc, clientData)
Tclgeomap_Place placePtr;
Tclgeomap_PlaceDeleteProc proc;
ClientData clientData;
{
int n;
Tcl_HashEntry *entry;
if ( !placePtr || !proc || !clientData ) {
return;
}
entry = Tcl_CreateHashEntry(&placePtr->deleteTasks, clientData, &n);
Tcl_SetHashValue(entry, (ClientData)proc);
}
/*
*------------------------------------------------------------------------
*
* Tclgeomap_CnxPlaceDeleteTask --
*
* This procedure cancels a callback added by Tclgeomap_AddPlaceDeleteTask.
*
* Results:
* None.
*
* Side effects:
* See the user documentation.
*
*------------------------------------------------------------------------
*/
void
Tclgeomap_CnxPlaceDeleteTask (placePtr, clientData)
Tclgeomap_Place placePtr;
ClientData clientData;
{
Tcl_HashEntry *entry;
if ( !placePtr || !clientData ) {
return;
}
if ( !(entry = Tcl_FindHashEntry(&placePtr->deleteTasks,
(char *)clientData)) ) {
return;
}
Tcl_DeleteHashEntry(entry);
}
/*
*------------------------------------------------------------------------
*
* Tclgeomap_GetPlace --
*
* Return a Tclgeomap_Place struct given the place name.
*
* Results:
* A Tclgeomap_Place struct or NULL.
*
* Side effects:
* None.
*------------------------------------------------------------------------
*/
Tclgeomap_Place
Tclgeomap_GetPlace(interp, name)
Tcl_Interp *interp; /* Current interpreter */
CONST char *name; /* Alleged place name */
{
Tcl_CmdInfo infoPtr; /* Command info for command named name */
if ( Tcl_GetCommandInfo(interp, (char *)name, &infoPtr)
&& Tcl_FindHashEntry(&places, infoPtr.objClientData)) {
return (Tclgeomap_Place)infoPtr.objClientData;
} else {
return NULL;
}
}
/*
*------------------------------------------------------------------------
*
* Tclgeomap_PlaceName --
*
* This procedure returns the name of a place.
*
* Results:
* See the user documentation.
*
* Side effects:
* See the user documentation.
*
*------------------------------------------------------------------------
*/
CONST char *
Tclgeomap_PlaceName(placePtr)
Tclgeomap_Place placePtr;
{
return placePtr
? Tcl_GetCommandName(placePtr->interp, placePtr->cmd)
: NULL;
}
/*
*------------------------------------------------------------------------
*
* Tclgeomap_PlaceLoc --
*
* This procedure returns the {lat lon} coordinates of a Tclgeomap_Place.
*
* Results:
* A GeoPt (declared in geography.h)
*
* Side effects:
* None.
*
*------------------------------------------------------------------------
*/
GeoPt
Tclgeomap_PlaceLoc(plcPtr)
struct Tclgeomap_Place *plcPtr; /* Place of interest */
{
return plcPtr->geoPt;
}
/*
*------------------------------------------------------------------------
*
* deleteProc --
*
* This is the deletion procedure for the Tcl command created for a
* place.
*
* Results:
* None.
*
* Side effects:
* A Tclgeomap_Place structure is deleted and its entry is removed from
* the places hash table.
*
*------------------------------------------------------------------------
*/
void
deleteProc(clientData)
ClientData clientData; /* The place to remove */
{
Tcl_HashEntry *entry; /* Entry for delete tasks loop */
Tcl_HashSearch search; /* Delete task loop parameter */
ClientData cd; /* Clientdata for an delete proc */
Tclgeomap_PlaceDeleteProc *deleteProc;
/* Procedure from deleteTasks table */
struct Tclgeomap_Place *plcPtr; /* The place being deleted */
plcPtr = (Tclgeomap_Place)clientData;
for (entry = Tcl_FirstHashEntry(&plcPtr->deleteTasks, &search);
entry != NULL; entry = Tcl_NextHashEntry(&search)) {
cd = (ClientData)Tcl_GetHashKey(&plcPtr->deleteTasks, entry);
deleteProc = (Tclgeomap_PlaceDeleteProc *)Tcl_GetHashValue(entry);
(*deleteProc)(cd);
}
Tcl_DeleteHashEntry(Tcl_FindHashEntry(&places, (char *)plcPtr));
Tcl_DeleteHashTable(&plcPtr->updateTasks);
Tcl_DeleteHashTable(&plcPtr->deleteTasks);
CKFREE((char *)plcPtr);
}
/*
*------------------------------------------------------------------------
*
* new --
*
* This is the callback for the "geomap::place new ..." command.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* This procedure allocates and initializes a new Tclgeomap_Place
* structure, and adds it to the places table.
* It creates a new Tcl command named for the place to access it.
* The data structures and command associated with the place will be
* destroyed when the place command is destroyed.
*
*------------------------------------------------------------------------
*/
int
new(clientData, interp, objc, objv)
ClientData clientData; /* Not used */
Tcl_Interp *interp; /* The current interpreter */
int objc; /* Number of arguments */
Tcl_Obj *const objv[]; /* Argument objects */
{
char *name; /* Place name */
GeoPt geoPt; /* Lat-lon of the new place */
Tcl_CmdInfo info; /* Not used. */
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "placeName {lat lon}");
return TCL_ERROR;
}
if (Tclgeomap_GetGeoPtFromObj(interp, objv[3], &geoPt) != TCL_OK) {
return TCL_ERROR;
}
name = Tcl_GetString(objv[2]);
if (Tcl_GetCommandInfo(interp, name, &info)) {
Tcl_AppendResult(interp, "Could not create place named ",
name, " because a command by that name already exists.\n",
NULL);
return TCL_ERROR;
}
createPlace(interp, name, geoPt);
Tcl_SetObjResult(interp, Tcl_NewStringObj(name, -1));
return TCL_OK;
}
/*
*------------------------------------------------------------------------
*
* set --
*
* This is the callback for commands of form "geomap::place set ..." and
* "placeName set ...".
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*------------------------------------------------------------------------
*/
int
set(clientData, interp, objc, objv)
ClientData clientData; /* If not NULL, a Tclgeomap_Place
* structure */
Tcl_Interp *interp; /* The current interpreter */
int objc; /* Number of arguments */
Tcl_Obj *const objv[]; /* Argument objects */
{
char *name; /* Fully qualified name of place */
Tcl_Obj *geoPtObj; /* Lat-lon for the place */
GeoPt geoPt; /* GeoPt from geoPtObj */
struct Tclgeomap_Place *plcPtr; /* Place of interest */
Tcl_HashEntry *entry; /* Entry for update loop */
Tcl_HashSearch search; /* Update loop parameter */
ClientData cd; /* Clientdata for an update proc */
Tclgeomap_PlaceUpdateProc *updateProc;
/* Procedure from updateTasks table */
if (clientData) {
/*
* Command has form "placeName set" or "placeName set {lat lon}"
*/
plcPtr = (Tclgeomap_Place)clientData;
if (objc == 2) {
Tcl_SetObjResult(interp, Tclgeomap_NewGeoPtObj(plcPtr->geoPt));
} else if (objc == 3) {
geoPtObj = objv[2];
if (Tclgeomap_GetGeoPtFromObj(interp, geoPtObj, &geoPt)
!= TCL_OK)
return TCL_ERROR;
plcPtr->geoPt = geoPt;
for (entry = Tcl_FirstHashEntry(&plcPtr->updateTasks, &search);
entry != NULL;
entry = Tcl_NextHashEntry(&search)) {
cd = (ClientData)Tcl_GetHashKey(&plcPtr->updateTasks, entry);
updateProc
= (Tclgeomap_PlaceUpdateProc *)Tcl_GetHashValue(entry);
(*updateProc)(cd);
}
Tcl_SetObjResult(interp, objv[2]);
} else {
Tcl_WrongNumArgs(interp, 2, objv, "?{lat lon}?");
return TCL_ERROR;
}
} else {
/*
* Command has form "geomap::place set placeName"
* or "geomap::place set placeName {lat lon}".
*/
if (objc == 3) {
name = Tcl_GetString(objv[2]);
if ( !(plcPtr = Tclgeomap_GetPlace(interp, name)) ) {
Tcl_AppendResult(interp, "No place named ", name, NULL);
return TCL_ERROR;
} else {
Tcl_SetObjResult(interp,
Tclgeomap_NewGeoPtObj(plcPtr->geoPt));
}
} else if (objc == 4) {
geoPtObj = objv[3];
if (Tclgeomap_GetGeoPtFromObj(interp, geoPtObj, &geoPt)
!= TCL_OK) {
return TCL_ERROR;
}
name = Tcl_GetString(objv[2]);
if ((plcPtr = Tclgeomap_GetPlace(interp, name))) {
plcPtr->geoPt = geoPt;
} else {
plcPtr = createPlace(interp, name, geoPt);
}
Tcl_SetObjResult(interp, objv[3]);
} else {
Tcl_WrongNumArgs(interp, 2, objv, "placeName ?{lat lon}?");
return TCL_ERROR;
}
}
return TCL_OK;
}
/*
*------------------------------------------------------------------------
*
* distance --
*
* This is the callback for the "geomap::place distance" command.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*------------------------------------------------------------------------
*/
int
distance(clientData, interp, objc, objv)
ClientData clientData; /* Not used */
Tcl_Interp *interp; /* The current interpreter */
int objc; /* Number of arguments */
Tcl_Obj *const objv[]; /* Argument objects */
{
char *plcNm; /* Place name on command line */
Tclgeomap_Place plc1Ptr, plc2Ptr; /* Input named places */
GeoPt geoPt1, geoPt2; /* Input lat-lon's */
double dist; /* Result */
if (objc != 4 && objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv,
"placeOR{lat lon} placeOR{lat lon} ?unit?");
return TCL_ERROR;
}
/*
* Get geoPt1 from objv[2], which is either a place name or
* a {lat lon} pair.
*/
if (Tclgeomap_GetGeoPtFromObj(NULL, objv[2], &geoPt1) != TCL_OK) {
plcNm = Tcl_GetString(objv[2]);
if ((plc1Ptr = Tclgeomap_GetPlace(interp, plcNm)) ) {
geoPt1 = plc1Ptr->geoPt;
} else {
Tcl_AppendResult(interp, plcNm, " not a location", NULL);
return TCL_ERROR;
}
}
/*
* Get geoPt2 from objv[3], which is either a place name or
* a {lat lon} pair.
*/
if (Tclgeomap_GetGeoPtFromObj(NULL, objv[3], &geoPt2) != TCL_OK) {
plcNm = Tcl_GetString(objv[3]);
if ((plc2Ptr = Tclgeomap_GetPlace(interp, plcNm)) ) {
geoPt2 = plc2Ptr->geoPt;
} else {
Tcl_AppendResult(interp, plcNm, " not a location", NULL);
return TCL_ERROR;
}
}
dist = AngleToDeg(GeoDistance(geoPt1, geoPt2));
if (objc == 5) {
/*
* Apply optional distance unit.
*/
Tcl_Obj *unit = objv[4];
int idx;
if (Tcl_GetIndexFromObj(interp, unit, units, "unit", 0, &idx)
!= TCL_OK) {
return TCL_ERROR;
}
switch ((enum UnitIdx)idx) {
case NMILES: dist *= NMIPERDEG; break;
case SMILES: dist *= SMIPERDEG; break;
case KM: dist *= KMPERDEG; break;
case GSDEG: break;
}
}
Tcl_SetObjResult(interp, Tcl_NewDoubleObj(dist));
return TCL_OK;
}
/*
*------------------------------------------------------------------------
*
* azrng --
*
* This is the callback for the "geomap::place azrng ..." command.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*------------------------------------------------------------------------
*/
int
azrng(clientData, interp, objc, objv)
ClientData clientData; /* Not used */
Tcl_Interp *interp; /* The current interpreter */
int objc; /* Number of arguments */
Tcl_Obj *const objv[]; /* Argument objects */
{
char *plcNm; /* Place name on command line */
Tclgeomap_Place plc1Ptr, plc2Ptr; /* Input named places */
GeoPt geoPt1, geoPt2; /* Input lat-lon's */
double azm, dist; /* Input azimuth and distance */
Tcl_Obj *rslt; /* Hold result */
rslt = Tcl_GetObjResult(interp);
if (objc != 4 && objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv,
"placeOR{lat lon} placeOR{lat lon} ?unit?");
return TCL_ERROR;
}
/*
* Get geoPt1 from objv[2], which is either a place name or
* a {lat lon} pair.
*/
if (Tclgeomap_GetGeoPtFromObj(NULL, objv[2], &geoPt1) != TCL_OK) {
plcNm = Tcl_GetString(objv[2]);
if ((plc1Ptr = Tclgeomap_GetPlace(interp, plcNm)) ) {
geoPt1 = plc1Ptr->geoPt;
} else {
Tcl_AppendResult(interp, plcNm, " not a location", NULL);
return TCL_ERROR;
}
}
/*
* Get geoPt2 from objv[3], which is either a place name or
* a {lat lon} pair.
*/
if (Tclgeomap_GetGeoPtFromObj(NULL, objv[3], &geoPt2) != TCL_OK) {
plcNm = Tcl_GetString(objv[3]);
if ((plc2Ptr = Tclgeomap_GetPlace(interp, plcNm)) ) {
geoPt2 = plc2Ptr->geoPt;
} else {
Tcl_AppendResult(interp, plcNm, " not a location", NULL);
return TCL_ERROR;
}
}
dist = AngleToDeg(GeoDistance(geoPt1, geoPt2));
if (objc == 5) {
/*
* Apply optional distance unit.
*/
Tcl_Obj *unit = objv[4];
int idx;
if (Tcl_GetIndexFromObj(interp, unit, units, "unit", 0, &idx)
!= TCL_OK) {
return TCL_ERROR;
}
switch ((enum UnitIdx)idx) {
case NMILES: dist *= NMIPERDEG; break;
case SMILES: dist *= SMIPERDEG; break;
case KM: dist *= KMPERDEG; break;
case GSDEG: break;
}
}
azm = AngleToDeg(Azimuth(geoPt1, geoPt2));
Tcl_ListObjAppendElement(interp, rslt, Tcl_NewDoubleObj(azm));
Tcl_ListObjAppendElement(interp, rslt, Tcl_NewDoubleObj(dist));
Tcl_SetObjResult(interp, rslt);
return TCL_OK;
}
/*
*------------------------------------------------------------------------
*
* nearest --
*
* This is the callback for commands of form "geomap::place nearest ..."
* and "placeName nearest ...".
* usage details.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*------------------------------------------------------------------------
*/
int
nearest(clientData, interp, objc, objv)
ClientData clientData; /* If not NULL, a Tclgeomap_Place struct */
Tcl_Interp *interp; /* The current interpreter */
int objc; /* Number of arguments */
Tcl_Obj *const objv[]; /* Argument objects */
{
char *plcNm; /* Name of place of interest */
Tclgeomap_Place
plcPtr, /* Place of interest */
skipPtr = NULL, /* If place of interest is in database,
* store it here so that we do not compare it
* to itself during search */
schPlcPtr; /* Current place in search loop */
GeoPt geoPt; /* Lat-lon of place of interest */
CONST char *nearPlcNm = NULL;/* Name of nearest place so far */
Tcl_Obj *cmdLnPList = NULL; /* List of places to search on command line */
Angle d180 = AngleFmDeg(180.0);
Angle nearDist = d180 + 1000;/* Hold distance to nearest place so far.
* Note that GeoDistance returns great circle
* arc measured in microdegrees. */
Angle cDistance; /* Distance to current place (microdegrees) */
Tcl_Obj **placeList; /* Optional list of places on command line */
int placeCnt; /* Number of places in placeList */
int np; /* Loop index */
char *lsElemNm; /* Name of place from list on command line */
if (clientData) {
/*
* Command is of form "placeName nearest {place place ...}"
*/
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "{place place ...}");
return TCL_ERROR;
}
plcPtr = (Tclgeomap_Place)clientData;
geoPt = plcPtr->geoPt;
skipPtr = plcPtr;
cmdLnPList = objv[2];
} else {
/*
* Command is of form
* "geomap::place nearest placeORgeoPt {place place ...}"
*/
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv,
"placeOR{lat lon} {place place ...}");
return TCL_ERROR;
}
if (Tclgeomap_GetGeoPtFromObj(NULL, objv[2], &geoPt) != TCL_OK) {
plcNm = Tcl_GetString(objv[2]);
if ( !(plcPtr = Tclgeomap_GetPlace(interp, plcNm)) ) {
Tcl_AppendResult(interp, plcNm, " not a location.", NULL);
return TCL_ERROR;
}
geoPt = plcPtr->geoPt;
skipPtr = plcPtr;
}
if (objc == 4) {
cmdLnPList = objv[3];
}
}
/*
* Search list given on command line
*/
if (Tcl_ListObjGetElements(interp, cmdLnPList, &placeCnt, &placeList)
!= TCL_OK) {
return TCL_ERROR;
}
for (np = 0, nearDist = d180 + 1000; np < placeCnt; np++) {
lsElemNm = Tcl_GetString(placeList[np]);
if ( !(schPlcPtr = Tclgeomap_GetPlace(interp, lsElemNm)) ) {
Tcl_AppendResult(interp, "No place named ", lsElemNm,
" in current namespace.", NULL);
return TCL_ERROR;
}
if (schPlcPtr == skipPtr) {
continue;
}
cDistance = GeoDistance(schPlcPtr->geoPt, geoPt);
if (cDistance < nearDist) {
nearDist = cDistance;
nearPlcNm = Tcl_GetCommandName(interp, schPlcPtr->cmd);
}
}
if (nearPlcNm) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(nearPlcNm, -1));
return TCL_OK;
} else {
Tcl_AppendResult(interp, "No places to compare", NULL);
return TCL_ERROR;
}
}
/*
*------------------------------------------------------------------------
*
* step --
*
* This is the callback for the "geomap::place step ..." and
* "placeName step ..." commands.
* for usage details.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*------------------------------------------------------------------------
*/
int
step(clientData, interp, objc, objv)
ClientData clientData; /* Not used */
Tcl_Interp *interp; /* The current interpreter */
int objc; /* Number of arguments */
Tcl_Obj *const objv[]; /* Argument objects */
{
char *plcNm; /* Place name */
GeoPt geoPt; /* Starting location */
GeoPt endPt; /* Point at bearing and range from geoPt */
struct Tclgeomap_Place *plcPtr;
/* Current place */
double brg, rng; /* Desired bearing and range */
Tcl_Obj
*brgObj, /* Bearing on command line */
*rngObj; /* Range on command line */
Tcl_Obj *unit = NULL; /* Optional unit on command line */
int idx; /* Index returned by Tcl_GetIndexFromObj */
static char *brgs[] = {
"north", "nneast", "neast", "eneast",
"east", "eseast", "seast", "sseast",
"south", "sswest", "swest", "wswest",
"west", "wnwest", "nwest", "nnwest", NULL
}; /* Bearing names */
enum brgIdx {
N, NNE, NE, ENE,
E, ESE, SE, SSE,
S, SSW, SW, WSW,
W, WNW, NW, NNW
};
/* Bearing indices */
if (clientData) {
/*
* Command is of form "placeName step az rng ?unit?"
*/
if (objc != 4 && objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv,
"bearing range ?unit?");
return TCL_ERROR;
}
plcPtr = (Tclgeomap_Place)clientData;
geoPt = plcPtr->geoPt;
brgObj = objv[2];
rngObj = objv[3];
if (objc == 5) {
unit = objv[4];
}
} else {
/*
* Command is of form
* "geomap::place step placeNameOR{lat lon} az rng ?unit?"
*/
if (objc != 5 && objc != 6) {
Tcl_WrongNumArgs(interp, 2, objv,
"placeName bearing range ?unit?");
return TCL_ERROR;
}
if (Tclgeomap_GetGeoPtFromObj(NULL, objv[2], &geoPt) != TCL_OK) {
plcNm = Tcl_GetString(objv[2]);
if ( !(plcPtr = Tclgeomap_GetPlace(interp, plcNm)) ) {
Tcl_AppendResult(interp, plcNm, " not a location.", NULL);
return TCL_ERROR;
}
geoPt = plcPtr->geoPt;
}
brgObj = objv[3];
rngObj = objv[4];
if (objc == 6) {
unit = objv[5];
}
}
/*
* Read bearing, which can be a number of degrees or a string
* from the brgs array.
*/
if (Tcl_GetDoubleFromObj(interp, brgObj, &brg) != TCL_OK) {
Tcl_ResetResult(interp);
if (Tcl_GetIndexFromObj(interp, brgObj, brgs, "bearing", 0, &idx)
!= TCL_OK) {
Tcl_AppendResult(interp, ", or a double value", NULL);
return TCL_ERROR;
}
switch ((enum brgIdx)idx) {
case N: brg = 0.0; break;
case NNE: brg = 22.5; break;
case NE: brg = 45.0; break;
case ENE: brg = 67.5; break;
case E: brg = 90.0; break;
case ESE: brg = 112.5; break;
case SE: brg = 135.0; break;
case SSE: brg = 157.5; break;
case S: brg = 180.0; break;
case SSW: brg = 202.5; break;
case SW: brg = 225.0; break;
case WSW: brg = 247.5; break;
case W: brg = 270.0; break;
case WNW: brg = 292.5; break;
case NW: brg = 315.0; break;
case NNW: brg = 337.5; break;
}
}
if (Tcl_GetDoubleFromObj(interp, rngObj, &rng) != TCL_OK) {
return TCL_ERROR;
}
if (unit) {
if (Tcl_GetIndexFromObj(interp, unit, units, "unit", 0, &idx)
!= TCL_OK) {
return TCL_ERROR;
}
switch ((enum UnitIdx)idx) {
case NMILES: rng /= NMIPERDEG; break;
case SMILES: rng /= SMIPERDEG; break;
case KM: rng /= KMPERDEG; break;
case GSDEG: break;
}
}
endPt = GeoStep(geoPt, AngleFmDeg(brg), AngleFmDeg(rng));
Tcl_SetObjResult(interp, Tclgeomap_NewGeoPtObj(endPt));
return TCL_OK;
}
/*
*------------------------------------------------------------------------
*
* inrange --
*
* This is the callback for the "geomap::place inrange ..." and
* "placeName inrange ..." commands.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*------------------------------------------------------------------------
*/
int
inrange(clientData, interp, objc, objv)
ClientData clientData; /* Not used */
Tcl_Interp *interp; /* The current interpreter */
int objc; /* Number of arguments */
Tcl_Obj *const objv[]; /* Argument objects */
{
GeoPt
refPt, /* References to use in units conversion */
origin = {0.0, 0.0};
double rng; /* Look for places within range of ctr */
Tclgeomap_Place
schPlcPtr, /* Current place in search loop */
ctrPtr = NULL; /* Place we are measuring from */
char *ctrNm; /* Name of place we are measuring from */
GeoPt ctr; /* Location we are measuring from */
int rngArgC; /* Number of elements in rng argument */
Tcl_Obj **rngArgv; /* Range argument: rng ?unit? */
Tcl_Obj *rslt = NULL;
Tcl_Obj *cmdLnRng; /* Range term on command line */
Tcl_Obj *cmdLnPList = NULL; /* List of places to search on command line */
int np, placeCnt;
Tcl_Obj **placeList;
char *lsElemNm; /* Name of place from list */
if (clientData) {
/*
* Command has form "placeName inrange {rng ?unit?} ?list?"
*/
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv,
"{range ?unit?} {place place ...}");
return TCL_ERROR;
}
ctrPtr = (Tclgeomap_Place)clientData;
ctr = ctrPtr->geoPt;
cmdLnRng = objv[2];
cmdLnPList = objv[3];
} else {
/*
* Command has form
* "geomap::place inrange placeORgeoPt {rng ?unit?} ?list?"
*/
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv,
" placeNameOR{lat lon} {range ?unit?} {place place ...}");
return TCL_ERROR;
}
if (Tclgeomap_GetGeoPtFromObj(NULL, objv[2], &ctr) != TCL_OK) {
ctrNm = Tcl_GetString(objv[2]);
if ( !(ctrPtr = Tclgeomap_GetPlace(interp, ctrNm)) ) {
Tcl_AppendResult(interp, ctrNm, " not a location.", NULL);
return TCL_ERROR;
}
ctr = ctrPtr->geoPt;
}
cmdLnRng = objv[3];
cmdLnPList = objv[4];
}
/*
* Get range. If range is a two element list, second element is optional
* unit.
*/
if (Tcl_ListObjGetElements(interp, cmdLnRng, &rngArgC, &rngArgv) != TCL_OK
|| Tcl_GetDoubleFromObj(interp, rngArgv[0], &rng) != TCL_OK) {
return TCL_ERROR;
}
if (rngArgC == 2) {
Tcl_Obj *unit = rngArgv[1];
int idx;
if (Tcl_GetIndexFromObj(interp, unit, units, "unit", 0, &idx)
!= TCL_OK) {
return TCL_ERROR;
}
switch ((enum UnitIdx)idx) {
case NMILES: rng /= NMIPERDEG; break;
case SMILES: rng /= SMIPERDEG; break;
case KM: rng /= KMPERDEG; break;
case GSDEG: break;
}
}
/*
* This algorithm uses GeoQuickDistance for the comparisons, which
* actually computes the Cartesian distance between two points. To
* get the distance in "GeoQuickDistance units" make a fictitious point
* {rng 0} and use GeoQuickDistance to compute the distance from {0 0}
* to {rng 0}.
*/
refPt.lat = AngleFmDeg(rng);
refPt.lon = 0;
rng = GeoQuickDistance(refPt, origin);
/*
* Search list of places from command line.
*/
rslt = Tcl_NewObj();
if (Tcl_ListObjGetElements(interp, cmdLnPList, &placeCnt, &placeList)
!= TCL_OK) {
return TCL_ERROR;
}
for (np = 0; np < placeCnt; np++) {
lsElemNm = Tcl_GetString(placeList[np]);
if ( !(schPlcPtr = Tclgeomap_GetPlace(interp, lsElemNm)) ) {
Tcl_AppendResult(interp, "No place named ", lsElemNm, NULL);
return TCL_ERROR;
}
if (schPlcPtr == ctrPtr) {
continue;
}
if (GeoQuickDistance(schPlcPtr->geoPt, ctr) < rng) {
Tcl_ListObjAppendElement(interp, rslt,
Tcl_NewStringObj(lsElemNm, -1));
}
}
Tcl_SetObjResult(interp, rslt);
return TCL_OK;
}
syntax highlighted by Code2HTML, v. 0.9.1