// twhere.c // // by John D. de Boer #include "twist.h" int PLACES; // number of cities Place ***Places; // the cities & regions bool CheckingDelta; // set when the database delta-checking function is operative const real EarthRad = 6378.1; // equatorial radius in km static stringlist UPList; // place names that were not found // GeoRefs void GeoRefToVector(GeoRef *G, vector *V, const char *S) { long THETA; if (!G || !V) return; if (!G->good) WarnOld(cat(0,"GeoRef error: ",(char *)S)); THETA= (90 * 60) - G->lat; polartocartesian(1.0,THETA / (60.0 * fiftyseven),G->lng / (60.0 * fiftyseven),V); } static real Distance(const Place *A, const Place *B) { vector V; crossproduct(&(A->pos),&(B->pos),&V); return EarthRad * vectorlength(&V); } string GeoRefString(GeoRef *G) { string S; int L; if (!G || !G->good) return NULL; L= abso(G->lat) / 60; S=cat(1,fig(L),"¡"); L= mod(abso(G->lat),60); if (L) S=cat(3,S,fig(L)); if (G->lat) S=cat(1,S,G->lat>0 ? " N" : " S"); L= abso(G->lng) / 60; S=cat4(5,S," ",fig(L),"¡"); L= mod(abso(G->lng),60); if (L) S=cat(3,S,fig(L)); if (G->lng) S=cat(1,S,G->lng>0 ? " E" : " W"); return S; } static bool GeoRefsEqual(GeoRef *A, GeoRef *B) { if (!A || !B) return 0; if (!A->good && !B->good) return 1; if (A->good!=B->good) return 0; if (A->lat!=B->lat || A->lng!=B->lng || A->unc!=B->unc) return 0; return 1; } // Allocation void InitPlaces(void) { PLACES=0; Places=(Place ***)handle(0); initstringlist(&UPList); CheckingDelta=0; } Place *NewPlace(bool AddToList) { Place *R; R=pointer(sizeof(Place)); if (AddToList) { minimumsize(Places,PLACES + 1,sizeof(Place *)); (*Places)[PLACES++]=R; } R->leg='CITY'; zerovector(&(R->pos)); R->loc.good=0; R->loc.lat=R->loc.lng=0; R->loc.unc=0; R->rad=0; R->waypt=0; R->water=0; R->hassub=0; R->sup=R->rep=NULL; InitSpan(&(R->when)); R->name=R->aka=NULL; R->adj=NULL; setcolour(&(R->rgb),white); R->flag=NULL; R->flag2=NULL; return R; } static void OldPlace(Place *C) { if (!C) return; old(C->name); old(C->aka); old(C->adj); oldpicture(C->flag); // oldbitmap(); ? oldpicture(C->flag2); // oldbitmap(); ? old(C); } void PurgePlaces(void) { while (PLACES>0) OldPlace((*Places)[--PLACES]); purgestringlist(&UPList); } // static bool Bigger(const void *A, const void *B) { int P,Q; if (!A || !B) return 0; P=((Place *)A)->rad; Q=((Place *)B)->rad; return P>=Q; } void OrderPlacesBySize(void) { lock(Places); sort(*Places,PLACES,&Bigger); unlock(Places); } // static void MakeFlagColumn(Place *C) { bitmap *B1,*B2; colour RGB; int j; if (!C || !C->flag) return; B1=extractbitmap(C->flag); B2=newbitmap(1,B1->height); for (j=0;jheight;j++) { getpixel(B1,B1->width - 1,j,&RGB); setpixel(B2,0,j,&RGB); } C->flag2=wrapbitmap(B2); oldbitmap(B1); } void VerifyNewPlace(Place *C) { Place *Reg; real Dist; if (!C) return; GeoRefToVector(&(C->loc),&(C->pos),C->name); Reg=C->sup; if (C==Reg) DangerOld(cat(0,C->name," is its own region")); if (C->rad<500 && !Reg) WarnOld(cat(0,C->name," has no region specified for it")); if (Reg && C->rad>=Reg->rad) { DangerOld(cat3(0,C->name," is not smaller than the region it's in: ",Reg->name)); C->sup=NULL; } if (Reg) { Dist=Distance(C,Reg); if (Dist > 2.0 * Reg->rad) WarnOld(cat4(0,C->name," is far from its region, ",Reg->name," (beyond 2 radii)")); } if (C->flag) MakeFlagColumn(C); } // Database delta void CheckPlaceDelta(Place *C) { Place *Ex; if (!C) return; Ex=FindPlace(C->name); if (Ex) { if (streq(C->name,Ex->aka) && streq(C->aka,Ex->name)) NoteOld(cat(0,"Name and alternate swapped for ",Ex->name)); else if (C->aka && !Ex->aka) NoteOld(cat(0,"Alternate name added for ",Ex->name)); else if (!C->aka && Ex->aka) NoteOld(cat(0,"Alternate name missing for ",Ex->name)); else if (!streq(C->aka,Ex->aka)) NoteOld(cat3(0,"Alternate name of ",Ex->name," changed")); else if (!GeoRefsEqual(&(C->loc),&(Ex->loc))) NoteOld(cat3(0,"Location of ",Ex->name," changed")); } else WarnOld(cat(0,"New place definition: ",C->name)); OldPlace(C); } // These routines manage the period over which a place should appear on the map. void PlaceReference(Place *C, const Span *D) { Place *S; if (!D) return; StretchBoundsToSpan(D); if (!C) return; StretchSpanToDate(&(C->when),&(D->start)); StretchSpanToDate(&(C->when),&(D->end)); S=C->sup; if (S && S->rad>C->rad) PlaceReference(S,D); } // Resolving references to places static void UnknownPlace(const char *S) { if (stringisinlist(&UPList,S) || CheckingDelta) return; WarnOld(cat(0,"Unresolved reference to a place: ",(char *)S)); addstringtolist(&UPList,copyof(S)); } static bool ExactMatch(const Place *C, const char *S) { if (!C || !S) return 0; if (streq(S,C->name)) return 1; if (C->aka && streq(S,C->aka)) return 1; if (C->adj && streq(S,C->adj)) return 1; return 0; } Place *FindPlace(const char *S) { int i; Place *C; char *Comma,*Q; if (!S || !*S) return NULL; Comma=position(", ",S); Q= Comma ? leftstr(S,Comma - S) : NULL; for (i=0;isup) continue; if (ExactMatch(C->sup,Comma + 2) && ExactMatch(C,Q)) { old(Q); return C; } } else if (ExactMatch(C,S)) return C; } old(Q); if (!numeral(S[0])) UnknownPlace(S); return NULL; } bool PlaceMatch(Place *C, const char *S) { if (!C || !S) return 0; if (wordin(S,C->name,0) || wordin(S,C->aka,0) || wordin(S,C->adj,0)) return 1; return 0; } // Waypoints Place *CreateWaypoint(GeoRef *G) { int i; Place *C; GeoRef *L; if (!G || !G->good) return NULL; for (i=0;iloc); if (C->waypt && L->lat==G->lat && L->lng==G->lng && L->unc==G->unc) return C; } C=NewPlace(1); if (!C) return NULL; C->waypt=1; C->name=GeoRefString(G); C->loc=*G; GeoRefToVector(&(C->loc),&(C->pos),C->name); return C; }