CensusMapImpl.mesa
Copyright Ó 1992 by Xerox Corporation. All rights reserved.
Greene, January 15, 1991 5:14 pm PST
Chauser, June 3, 1992 3:55 pm PDT
DIRECTORY
ApproxSymTab,
Ascii,
Atom,
Browser2D,
Buttons,
Commander,
CommanderOps,
Containers,
Convert,
Draw2d,
FileNames,
FS,
IO,
Icons,
Imager,
ImagerColor,
ImagerPath,
Menus,
MessageWindow,
Process,
Real,
Rope,
Rules,
TEditDocument,
TEditSelectionOps,
TiogaMenuOps,
CensusFileReader,
Vector,
ViewerClasses,
ViewerEvents,
ViewerTools,
ViewerOps;
CensusMapImpl: CEDAR MONITOR LOCKS common USING common: Common
IMPORTS ApproxSymTab, Ascii, Atom, Browser2D, Buttons, Containers, Convert, Commander, CommanderOps, Draw2d, FileNames, FS, IO, Icons, Imager, ImagerPath, ImagerColor, Menus, MessageWindow, Process, Rope, Rules, TEditSelectionOps, TiogaMenuOps, CensusFileReader, Vector, ViewerEvents, ViewerTools, ViewerOps =
BEGIN
mA: CARD ¬ 10;
mS: CARD ¬ 10000;
showOriginalRecord: BOOL ¬ FALSE;
nameIndex: BOOL ¬ TRUE;
ItemData: TYPE = REF ItemDataRec;
ItemDataRec:
TYPE =
RECORD [
fileName: Rope.ROPE,
filePosition: INT,
fromX: REAL,
fromY: REAL,
toX: REAL,
toY: REAL,
color: ImagerColor.Color,
name: REF TEXT
];
Common: TYPE = REF CommonRec;
CommonRec:
TYPE =
MONITORED
RECORD [
banner: Rope.ROPE ¬ NIL,
streetNames: ApproxSymTab.Ref,
browser: Browser2D.BrowserHandle,
outer: Containers.Container,
menu: Menus.Menu,
nameInput, addressInput, showChoice: ViewerClasses.Viewer,
lastNameInput: Rope.ROPE,
guardMapDestroy: BOOL ¬ TRUE, --changes from TRUE to FALSE once after initial load
matches: EntryDoubleList,
matchesCurrent: EntryDoubleList,
matchesTail: EntryDoubleList,
statusChange: CONDITION,
matcherStatus: MatcherStatus ¬ notRunning,
alwaysButton, ifNecessaryButton, neverButton: Buttons.Button,
always: BOOL ¬ FALSE,
ifNecessary: BOOL ¬ TRUE,
never: BOOL ¬ FALSE
];
EntryDoubleList: TYPE = REF EntryDoubleListRec;
EntryDoubleListRec:
TYPE =
RECORD[
entry: ApproxSymTab.Entry,
next, prev: EntryDoubleList ¬ NIL
];
MatcherStatus: TYPE = {notRunning, computing, matchDelivered, computeNextMatch, abort};
BoundingBox: Browser2D.ItemBoundingBoxProc = {
data: ItemData ¬
NARROW[self.data];
{OPEN data;
box.lowerLeft.x ¬ MIN[fromX, toX];
box.upperRight.x ¬ MAX[fromX, toX];
box.lowerLeft.y ¬ MIN[fromY, toY];
box.upperRight.y ¬ MAX[fromY, toY];
};
};
DistanceTo: Browser2D.ItemDistanceToProc = {
id: ItemData ¬ NARROW[self.data];
o: Vector.VEC ¬ [id.fromX, id.fromY];
b: Vector.VEC ¬ [id.toX - id.fromX, id.toY - id.fromY];
p: Vector.VEC ¬ Vector.Sub[location, o];
bb: REAL ¬ Vector.Dot[b,b];
t: REAL;
IF bb = 0.0 THEN RETURN[Vector.Mag[p]];
t ¬ Vector.Dot[p,b]/bb;
IF t <= 0.0 THEN RETURN[Vector.Mag[p]];
IF t >= 1.0 THEN RETURN[Vector.Mag[Vector.Sub[p,b]]];
RETURN[Vector.Mag[Vector.Sub[p, Vector.Mul[b,t]]]];
};
SelectionNotify: Browser2D.ItemSelectionNotifyProc = {
data: ItemData ¬ NARROW[self.data];
common: Common ¬ NARROW[commonData];
scanItems: LIST OF Browser2D.Item;
viewer: ViewerClasses.Viewer;
IF how = userSelected
THEN {
IF common.streetNames #
NIL
THEN {
scanItems ¬ NARROW[common.streetNames.FetchBest[Rope.FromRefText[data.name], NIL, 0].entry.value];
UNTIL scanItems =
NIL
DO
common.browser.SelectItem[scanItems.first];
scanItems ¬ scanItems.rest;
ENDLOOP;
};
IF data.name # NIL THEN common.browser.SetMessageWindow[Rope.FromRefText[data.name], 1, 100];
IF showOriginalRecord
THEN {
viewer ¬ TiogaMenuOps.Open[data.fileName];
TEditSelectionOps.ShowGivenPositionRange[viewer, feedback, data.filePosition, data.filePosition + 300, TRUE, FALSE];
};
};
};
Paint: Browser2D.ItemPaintProc = {
data: ItemData ¬ NARROW[self.data];
traj: Imager.Trajectory;
context.SetStrokeEnd[square];
IF how # normal
THEN {
traj ¬ ImagerPath.MoveTo[ [data.fromX, data.fromY] ];
traj ¬ traj.LineTo[ [data.toX, data.toY] ];
SELECT how
FROM
userSelected, clientSelected =>{context.SetStrokeWidth[2.4]; context.SetColor[data.color]};
deSelect => {context.SetStrokeWidth[2.4]; context.SetColor[Imager.white]};
ENDCASE;
context.MaskStrokeTrajectory[traj];
};
SELECT how
FROM
normal, deSelect => {
context.SetStrokeWidth[0.0];
context.SetColor[data.color];
--context.MaskStrokeTrajectory[traj];
Draw2d.Line[context, [data.fromX, data.fromY], [data.toX, data.toY], solid, zip]
};
userSelected, clientSelected =>{
context.SetStrokeWidth[.7];
context.SetColor[red];
context.MaskStrokeTrajectory[traj];
};
ENDCASE;
};
mapFeatures: Browser2D.ItemClass ¬
NEW[Browser2D.ItemClassRec ¬ [
paint: Paint,
selectionNotify: SelectionNotify,
boundingBox: BoundingBox,
distanceTo: DistanceTo
]];
Street Name Storage
Strip:
PROC [in: Rope.
ROPE]
RETURNS [out: Rope.
ROPE] ~ {
start: INT ¬ Rope.SkipOver[in, 0, " "];
end: INT ¬ Rope.Length[in] - 1;
IF start > end THEN RETURN[NIL];
DO
IF in.Fetch[end] # ' THEN RETURN[Rope.Substr[in, start, end-start+1]];
end ¬ end - 1;
ENDLOOP;
};
CensusMap: Commander.CommandProc = {
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd
! CommanderOps.Failed => {msg ¬ errorMsg; GO TO errorExit}];
common: Common ¬ NEW[CommonRec];
{OPEN common;
fileName: Rope.ROPE ¬ NIL;
item: Browser2D.Item;
items: LIST OF Browser2D.Item ¬ NIL;
enclosingBox: Browser2D.BoundingBox ¬ [[Real.LargestNumber, Real.LargestNumber], [- Real.LargestNumber, - Real.LargestNumber]];
tB: Buttons.Button; rule: Rules.Rule;
PerLine: CensusFileReader.PerLineProc ~ {
itemData: ItemData ¬
NEW[ItemDataRec ¬ [
fileName: fileName,
filePosition: filePosition[],
fromX: - fromLong[],
fromY: fromLat[],
toX: - toLong[],
toY: toLat[],
color: ColorCode[nonStCo[]]
]];
FindName:
PROC [a, b, c, d: Rope.
ROPE]
RETURNS [
REF
TEXT] ~ {
found: BOOL;
entry: ApproxSymTab.Entry;
already: LIST OF Browser2D.Item;
sa: Rope.ROPE ¬ Strip[a];
root: Rope.ROPE ¬ Strip[b];
sc: Rope.ROPE ¬ Strip[c];
sd: Rope.ROPE ¬ Strip[d];
full: Rope.ROPE ¬ IF sa # NIL THEN Rope.Concat[sa, " "] ELSE NIL;
full ¬ Rope.Concat[full, root];
full ¬ IF sc # NIL THEN Rope.Cat[full, " ", sc] ELSE full;
full ¬ IF sd # NIL THEN Rope.Cat[full, " ", sd] ELSE full;
[found, entry] ¬ streetNames.FetchBest[full, NIL, 0];
IF found
THEN {
already ¬ NARROW[entry.value];
already.rest ¬ CONS[item, already.rest];
RETURN[entry.key];
};
already ¬ LIST[item];
[ , entry] ¬ streetNames.Insert[full, already];
RETURN[entry.key];
};
IF (itemData.toX # 0)
OR (itemData.toY # 0)
OR (itemData.fromX # 0)
OR (itemData.fromY # 0)
THEN {
item ¬ NEW[Browser2D.ItemRec ¬ [class: mapFeatures, data: itemData]];
IF (itemData.toX = 0) AND (itemData.toY = 0) THEN {itemData.toX ¬ itemData.fromX - 1.0; itemData.toY ¬ itemData.fromY - 1.0};
IF (itemData.fromX = 0) AND (itemData.fromY = 0) THEN {itemData.fromX ¬ itemData.toX - 1.0; itemData.fromY ¬ itemData.toY - 1.0};
IF nameIndex THEN itemData.name ¬ FindName[stPreDir[], stName[], stType[], stSufDir[]];
items ¬ CONS[item, items];
enclosingBox ¬ Browser2D.LeastCommonBoundingBox[enclosingBox, BoundingBox[item, NIL]];
};
};
IF nameIndex THEN streetNames ¬ ApproxSymTab.Create[];
IF argv.argc <= 1 THEN {msg ¬ "Usage: CensusMap map1 map2 . . ."; GO TO errorExit};
FOR i:
NAT
IN [1..argv.argc)
DO
shortName: Rope.ROPE ¬ argv[i];
longName: Rope.ROPE;
IF NOT Rope.Match["*.cmap*", shortName] THEN shortName ¬ Rope.Concat[shortName, ".cmap"];
longName ¬ Rope.Concat["/censusmaps/", shortName];
fileName ¬ FileNames.FileWithSearchRules[
root: longName,
defaultExtension: "cmap",
searchRules: NIL].fullPath;
IF fileName = NIL THEN {msg ¬ Rope.Cat["Could not find: ", longName, "\n Try: pma /censusmaps/ -vux:/net/eich/noir/greene/cmaps/"]; GO TO errorExit};
banner ¬ Rope.Cat[banner, ", ", argv[i]];
CensusFileReader.GetDIME[fileName, PerLine];
ENDLOOP;
browser ¬ Browser2D.Create[enclosingBox, 50.0, mapIcon, Rope.Concat["Map: ", Rope.Substr[banner,2]], common, BrowserDestroyed];
UNTIL items =
NIL
DO
browser.AddItem[items.first];
items ¬ items.rest;
ENDLOOP;
browser.CenterEverything[];
UnguardMapDestroy[common];
IF NOT nameIndex THEN RETURN;
common.menu ¬ Menus.CreateMenu[];
Menus.AppendMenuEntry[common.menu, Menus.CreateEntry["BestMatch", BestMatch, common]];
Menus.AppendMenuEntry[common.menu, Menus.CreateEntry["NextMatch", NextMatch, common]];
Menus.AppendMenuEntry[common.menu, Menus.CreateEntry["PrevMatch", PrevMatch, common]];
Menus.AppendMenuEntry[common.menu, Menus.CreateEntry["ShowStreetOnMap", ShowStreetOnMap, common]];
Menus.AppendMenuEntry[common.menu, Menus.CreateEntry["ShowAddressOnStreet", ShowAddressOnStreet, common]];
common.outer ¬ Containers.Create[[
name: Rope.Concat["Index: ", Rope.Substr[banner, 2]],
openHeight: 105,
icon: indexIcon,
menu: common.menu,
scrollable: FALSE
]];
common.outer.props ¬ Atom.PutPropOnList[common.outer.props, $common, common];
common.showChoice ¬ ViewerTools.MakeNewTextViewer[[
data: "> Punch 'BestMatch' and street name will appear here. Punch 'NextMatch' for more streets. <",
parent: common.outer,
wx: 20, wy: 5, wh: 15, ww: 600,
scrollable: FALSE,
border: FALSE]];
rule ¬ Rules.Create[
info: [
wx: 0, wy: 25, wh: 2, ww: common.outer.cw,
parent: common.outer
]
];
Containers.ChildXBound[common.outer, rule];
tB ¬ Buttons.Create[
info:
[name: "Street Name:",
wx: 10, wy: 30, wh: 15,
parent: common.outer,
border: FALSE],
proc: ForceName,
clientData: common];
common.nameInput ¬ ViewerTools.MakeNewTextViewer[[
data: "> Misspell a substring of the desired street here. <",
parent: common.outer,
wx: tB.wx + tB.ww + 5,
wy: tB.wy, wh: 15, ww: 310,
scrollable: FALSE,
border: FALSE]];
tB ¬ Buttons.Create[
info:
[name: "Address:",
wx: common.nameInput.wx + common.nameInput.ww + 5,
wy: common.nameInput.wy, wh: 15,
parent: common.outer,
border: FALSE],
proc: ForceAddress,
clientData: common];
common.addressInput ¬ ViewerTools.MakeNewTextViewer[[
data: "> Enter an integer here. <",
parent: common.outer,
wx: tB.wx + tB.ww + 5,
wy: tB.wy, wh: 15, ww: 200,
scrollable: FALSE,
border: FALSE]];
tB ¬ Buttons.Create[
info:
[name: "Selection Centering on Map:",
wx: 10, wy: 55, wh: 15,
parent: common.outer,
border: FALSE],
proc: ToggleCentering,
clientData: common];
tB ¬ common.alwaysButton ¬ Buttons.Create[
info: [
name: "Always",
parent: common.outer,
wx: tB.wx + tB.ww + 10,
wy: tB.wy, wh: 15,
scrollable: FALSE,
border: TRUE],
proc: SetAlways,
clientData: common];
tB ¬ common.ifNecessaryButton ¬ Buttons.Create[
info: [
name: "If Necessary",
parent: common.outer,
wx: tB.wx + tB.ww + 10,
wy: tB.wy, wh: 15,
scrollable: FALSE,
border: TRUE],
proc: SetIfNecessary,
clientData: common];
tB ¬ common.neverButton ¬ Buttons.Create[
info: [
name: "Never",
parent: common.outer,
wx: tB.wx + tB.ww + 10,
wy: tB.wy, wh: 15,
scrollable: FALSE,
border: TRUE],
proc: SetNever,
clientData: common];
ColorCenteringButtons[common];
[] ¬ ViewerEvents.RegisterEventProc[IndexDestroyed, destroy, common.outer];
}
EXITS errorExit => result ¬ $Failure;
};
ColorCenteringButtons:
PROC [common: Common] ~ {
OPEN common;
Buttons.SetDisplayStyle[alwaysButton, IF always THEN $WhiteOnBlack ELSE $BlackOnWhite];
Buttons.SetDisplayStyle[ifNecessaryButton, IF ifNecessary THEN $WhiteOnBlack ELSE $BlackOnWhite];
Buttons.SetDisplayStyle[neverButton, IF never THEN $WhiteOnBlack ELSE $BlackOnWhite];
};
IndexDestroyed: ViewerEvents.EventProc ~ {
common: Common;
IF event # destroy THEN RETURN;
common ¬ NARROW[Atom.GetPropFromList[viewer.props, $common]];
IF common # NIL THEN IndexDestroyedLocked[common];
};
IndexDestroyedLocked:
ENTRY
PROC [common: Common] ~ {
OPEN common;
ENABLE UNWIND => NULL;
UNTIL matcherStatus = notRunning
DO
matcherStatus ¬ abort; BROADCAST statusChange;
WAIT statusChange;
ENDLOOP;
ApproxSymTab.Destroy[streetNames];
streetNames ¬ NIL;
outer ¬ NIL;
menu ¬ NIL;
nameInput ¬ NIL;
addressInput ¬ NIL;
showChoice ¬ NIL;
};
BrowserDestroyed: Browser2D.DestroyProc ~ {
IF commonData = NIL THEN RETURN;
BrowserDestroyedLocked[NARROW[commonData]];
};
BrowserDestroyedLocked:
ENTRY
PROC [common: Common] ~ {
ENABLE UNWIND => NULL;
UNTIL NOT common.guardMapDestroy DO WAIT common.statusChange ENDLOOP;
common.browser ¬ NIL;
};
UnguardMapDestroy:
ENTRY
PROC [common: Common] ~ {
common.guardMapDestroy ¬ FALSE; BROADCAST common.statusChange;
};
ForceName: Buttons.ButtonProc ~ {
common: Common ¬ NARROW[clientData];
ViewerTools.SetSelection[common.nameInput];
};
ForceAddress: Buttons.ButtonProc ~ {
common: Common ¬ NARROW[clientData];
ViewerTools.SetSelection[common.addressInput];
};
ToggleCentering: Buttons.ButtonProc ~ {
common: Common ¬ NARROW[clientData];
ToggleCenteringLocked[common, mouseButton];
};
ToggleCenteringLocked:
ENTRY
PROC[common: Common, mouseButton: ViewerClasses.MouseButton] ~ {
OPEN common;
save: BOOL;
IF mouseButton = blue
THEN {
save ¬ never;
never ¬ ifNecessary;
ifNecessary ¬ always;
always ¬ save;
}
ELSE
IF mouseButton = red
THEN {
save ¬ always;
always ¬ ifNecessary;
ifNecessary ¬ never;
never ¬ save
};
ColorCenteringButtons[common];
};
SetAlways: Buttons.ButtonProc ~ {
common: Common ¬ NARROW[clientData];
SetAlwaysLocked[common];
};
SetAlwaysLocked:
ENTRY
PROC[common: Common] ~ {
OPEN common;
always ¬ TRUE; ifNecessary¬ FALSE; never ¬ FALSE;
ColorCenteringButtons[common];
};
SetIfNecessary: Buttons.ButtonProc ~ {
common: Common ¬ NARROW[clientData];
SetIfNecessaryLocked[common];
};
SetIfNecessaryLocked:
ENTRY
PROC[common: Common] ~ {
OPEN common;
always ¬ FALSE; ifNecessary¬ TRUE; never ¬ FALSE;
ColorCenteringButtons[common];
};
SetNever: Buttons.ButtonProc ~ {
common: Common ¬ NARROW[clientData];
SetNeverLocked[common];
};
SetNeverLocked:
ENTRY
PROC[common: Common] ~ {
OPEN common;
always ¬ FALSE; ifNecessary¬ FALSE; never ¬ TRUE;
ColorCenteringButtons[common];
};
HasNameChanged:
PROC [common: Common]
RETURNS [changed:
BOOL] ~ {
new: Rope.ROPE ¬ ForceRopeUpper[ViewerTools.GetContents[common.nameInput]];
RETURN[Rope.Compare[new, common.lastNameInput] # equal];
};
BestMatch: Menus.ClickProc ~ {
common: Common ¬ NARROW[clientData];
BestMatchLocked[common];
};
BestMatchLocked:
ENTRY
PROC [common: Common] ~ {
ENABLE UNWIND => NULL; BestMatchCore[common];
};
BestMatchCore:
INTERNAL
PROC [common: Common] ~ {
OPEN common;
IF
NOT HasNameChanged[common]
THEN {
matchesCurrent ¬ matches;
UpdateShowChoice[common];
}
ELSE {
lastNameInput ¬ ForceRopeUpper[ViewerTools.GetContents[common.nameInput]];
matches ¬ matchesCurrent ¬ matchesTail ¬ NIL;
UNTIL matcherStatus = notRunning
DO
matcherStatus ¬ abort;
BROADCAST statusChange;
WAIT statusChange ENDLOOP;
matcherStatus ¬ computeNextMatch;
TRUSTED{Process.Detach[FORK NearMatch[common]]};
UNTIL (matcherStatus = matchDelivered)
OR (matcherStatus = notRunning)
DO
WAIT statusChange ENDLOOP;
IF matcherStatus = matchDelivered
THEN {
matchesCurrent ¬ matches;
UpdateShowChoice[common];
};
};
};
ForceRopeUpper:
PROC [in: Rope.
ROPE]
RETURNS [out: Rope.
ROPE] ~ {
ForceCharUpper: Rope.TranslatorType ~ {RETURN[Ascii.Upper[old]]};
out ¬ Rope.Translate[base: in, translator: ForceCharUpper];
};
NextMatch: Menus.ClickProc ~ {
common: Common ¬ NARROW[clientData];
NextMatchLocked[common];
};
NextMatchLocked:
ENTRY
PROC [common: Common] ~ {
OPEN common;
ENABLE UNWIND => NULL;
possibleNext: EntryDoubleList;
IF HasNameChanged[common] THEN {BestMatchCore[common]; RETURN};
possibleNext ¬ matchesCurrent.next;
IF possibleNext #
NIL
THEN {
matchesCurrent ¬ possibleNext;
UpdateShowChoice[common];
}
ELSE {
IF matcherStatus = notRunning THEN RETURN;
matcherStatus ¬ computeNextMatch; BROADCAST statusChange;
UNTIL (matcherStatus = matchDelivered)
OR (matcherStatus = notRunning)
DO
WAIT statusChange ENDLOOP;
IF matcherStatus = matchDelivered
THEN {
matchesCurrent ¬ matchesCurrent.next;
UpdateShowChoice[common];
};
};
};
PrevMatch: Menus.ClickProc ~ {
common: Common ¬ NARROW[clientData];
PrevMatchLocked[common];
};
PrevMatchLocked:
ENTRY
PROC [common: Common] ~ {
OPEN common;
ENABLE UNWIND => NULL;
IF matchesCurrent.prev #
NIL
THEN {
matchesCurrent ¬ matchesCurrent.prev;
UpdateShowChoice[common];
};
};
NearMatch:
ENTRY
PROC [common: Common] ~ {
OPEN common;
DeliverMatchAction:
INTERNAL ApproxSymTab.EachEntryAction ~ {
newNode: EntryDoubleList ¬ NEW[EntryDoubleListRec ¬ [entry: entry, next: NIL]];
IF matchesTail =
NIL
THEN {
matches ¬ newNode;
newNode.prev ¬ NIL;
}
ELSE {
matchesTail.next ¬ newNode;
newNode.prev ¬ matchesTail;
};
matchesTail ¬ newNode;
matcherStatus ¬ matchDelivered; BROADCAST statusChange;
UNTIL (matcherStatus = computeNextMatch)
OR (matcherStatus = abort)
DO
LanguageDeficiencyWait[common];
ENDLOOP;
RETURN[matcherStatus = abort] --quit?
};
LanguageDeficiencyWait:
INTERNAL
PROC [common: Common] ~ {
WAIT common.statusChange;
};
streetNames.FetchAll[lastNameInput, DeliverMatchAction, mA, NIL, mS];
matcherStatus ¬ notRunning;
BROADCAST statusChange;
};
UpdateShowChoice:
PROC [common: Common] ~ {
OPEN common;
IF matchesCurrent = NIL THEN RETURN;
ViewerTools.SetContents[showChoice, Rope.FromRefText[matchesCurrent.entry.key]];
ViewerOps.BlinkViewer[showChoice];
};
ShowStreetOnMap: Menus.ClickProc ~ {
IF clientData = NIL THEN RETURN;
ShowStreetOnMapLocked[NARROW[clientData]];
};
ShowStreetOnMapLocked:
ENTRY
PROC [common: Common] ~ {
scanItems: LIST OF Browser2D.Item;
moved: BOOL ¬ FALSE;
IF common.browser = NIL THEN RETURN;
IF HasNameChanged[common] THEN BestMatchCore[common];
IF common.matchesCurrent #
NIL
THEN {
common.browser.ClearAllSelections[];
scanItems ¬ NARROW[common.matchesCurrent.entry.value];
UNTIL scanItems =
NIL
DO
common.browser.SelectItem[scanItems.first];
scanItems ¬ scanItems.rest;
ENDLOOP;
common.browser.SetMessageWindow[Rope.FromRefText[common.matchesCurrent.entry.key], 1, 100];
IF
NOT common.never
THEN {
moved ¬ common.browser.CenterSelections[[120.0, 120.0], [0.0, 0.0], common.always];
};
IF NOT moved THEN common.browser.BlinkSelections[3, 200];
};
};
ShowAddressOnStreet: Menus.ClickProc ~ {
IF clientData = NIL THEN RETURN;
ShowAddressOnStreetLocked[NARROW[clientData]];
};
ShowAddressOnStreetLocked:
ENTRY
PROC [common: Common] ~ {
failureMessage: Rope.ROPE;
{
moved: BOOLEAN ¬ FALSE;
addressRequested: Rope.ROPE ¬ ViewerTools.GetContents[common.addressInput];
addressInt:
INT ¬ Convert.IntFromRope[addressRequested ! Convert.Error =>
{failureMessage ¬ Rope.Cat["Could not interpret ", addressRequested, " as an integer"];
GOTO Failure;
}];
scanItems: LIST OF Browser2D.Item;
currentFileName: Rope.ROPE;
currentFileStream: IO.STREAM ¬ NIL;
rightSide: BOOLEAN ¬ FALSE;
segment: ItemData;
distanceOnSegment: REAL ¬ 0;
readyToShow: BOOL ¬ FALSE;
CheckForHit: CensusFileReader.PerLineProc ~ {
leftLow: INT ¬ Convert.IntFromRope[leftAdd1[] ! Convert.Error => GOTO Flush];
leftHigh: INT ¬ Convert.IntFromRope[leftAdd2[] ! Convert.Error => GOTO Flush];
rightLow: INT ¬ Convert.IntFromRope[rgtAdd1[] ! Convert.Error => GOTO Flush];
rightHigh: INT ¬ Convert.IntFromRope[rgtAdd2[] ! Convert.Error => GOTO Flush];
leftValid: BOOL ¬ (leftLow # 0) OR (rightLow # 0);
rightValid: BOOL ¬ (rightLow # 0) OR (rightHigh # 0);
low, high: INT;
IF NOT (leftValid OR rightValid) THEN RETURN;
IF leftValid
AND
NOT rightValid
THEN {
rightSide ¬ FALSE;
low ¬ leftLow;
high ¬ leftHigh;
};
IF rightValid
AND
NOT leftValid
THEN {
rightSide ¬ TRUE;
low ¬ rightLow;
high ¬ rightHigh;
};
IF rightValid
AND leftValid
THEN {
rightSide ¬ (addressInt MOD 2) = (rightHigh MOD 2);
IF rightSide
THEN {
low ¬ rightLow;
high ¬ rightHigh;
}
ELSE {
low ¬ leftLow;
high ¬ leftHigh;
};
};
IF addressInt < low OR addressInt > high THEN RETURN;
distanceOnSegment ¬ IF high = low THEN 0 ELSE (addressInt - low*1.0) / (high - low);
readyToShow ¬ TRUE;
};
IF common.browser = NIL THEN RETURN;
IF HasNameChanged[common] THEN BestMatchCore[common];
IF common.matchesCurrent = NIL THEN {failureMessage ¬ "Street not found"; GOTO Failure};
scanItems ¬ NARROW[common.matchesCurrent.entry.value];
UNTIL scanItems =
NIL
DO
segment ¬ NARROW[scanItems.first.data];
IF Rope.Compare[segment.fileName, currentFileName] # equal
THEN {
currentFileName ¬ segment.fileName;
IF currentFileStream # NIL THEN IO.Close[currentFileStream];
currentFileStream ¬ FS.StreamOpen[fileName: currentFileName, streamOptions: options];
};
CensusFileReader.GetDIMERecord[currentFileStream, segment.filePosition, CheckForHit];
IF readyToShow
THEN {
o: Vector.VEC ¬ [segment.fromX, segment.fromY];
b: Vector.VEC ¬ [segment.toX - segment.fromX, segment.toY - segment.fromY];
onTheStreet: Vector.VEC ¬ Vector.Add[o, Vector.Mul[b, distanceOnSegment]];
justOffTheStreet: Vector.
VEC ¬ Vector.Add[onTheStreet,
Vector.Mul[Vector.Unit[Vector.Normal[b]], IF rightSide THEN -2.5 ELSE 2.5]
];
newItemData: LocateItemData ¬ NEW[LocateItemDataRec ¬ [x: justOffTheStreet.x , y: justOffTheStreet.y]];
newItem: Browser2D.Item ¬ NEW[Browser2D.ItemRec ¬ [data: newItemData, class: locatedPoints]];
common.browser.ClearAllSelections[];
common.browser.AddItem[newItem, TRUE];
common.browser.SelectItem[newItem];
IF
NOT common.never
THEN {
moved ¬ common.browser.CenterSelections[[120.0, 120.0], [0.0, 0.0], common.always];
};
common.browser.BlinkSelections[IF moved THEN 3 ELSE 20, 200];
common.browser.SelectItem[scanItems.first];
RETURN;
};
scanItems ¬ scanItems.rest;
ENDLOOP;
failureMessage ¬ "Could not interpolate number on street";
GOTO Failure;
EXITS
Failure => {
MessageWindow.Clear[];
MessageWindow.Append[failureMessage];
MessageWindow.Blink[]
};
}};
LocateItemData: TYPE = REF LocateItemDataRec;
LocateItemDataRec: TYPE = RECORD [x, y: REAL];
LocateBoundingBox: Browser2D.ItemBoundingBoxProc = {
data: LocateItemData ¬
NARROW[self.data];
{OPEN data;
box.lowerLeft.x ¬ x;
box.upperRight.x ¬ x;
box.lowerLeft.y ¬ y;
box.upperRight.y ¬ y;
};
};
LocatePaint: Browser2D.ItemPaintProc = {
data: LocateItemData ¬ NARROW[self.data];
traj: Imager.Trajectory;
traj ¬ ImagerPath.MoveTo[ [data.x, data.y] ];
traj ¬ traj.LineTo[ [data.x, data.y] ];
context.SetStrokeEnd[round];
SELECT how
FROM
normal => {context.SetStrokeWidth[.8]; context.SetColor[Imager.black]};
userSelected, clientSelected =>{context.SetStrokeWidth[3.0]; context.SetColor[red]};
deSelect => {context.SetStrokeWidth[3.0]; context.SetColor[Imager.white]};
ENDCASE;
context.MaskStrokeTrajectory[traj];
IF how = deSelect
THEN {
context.SetStrokeWidth[.8];
context.SetColor[Imager.black];
context.MaskStrokeTrajectory[traj]};
};
locatedPoints: Browser2D.ItemClass ¬
NEW[Browser2D.ItemClassRec ¬ [
paint: LocatePaint,
boundingBox: LocateBoundingBox
]];
ColorCode:
PROC [code: Rope.
ROPE]
RETURNS [color: ImagerColor.Color] ~ {
c: CHAR ¬ code.Fetch[0];
RETURN[
SELECT c
FROM
'1 => table[1],
'2 => table[2],
'3 => table[3],
'4 => table[4],
ENDCASE => Imager.black]; --roads and walkways
};
options: FS.StreamOptions ¬ FS.defaultStreamOptions;
mapIcon: Icons.IconFlavor = Icons.NewIconFromFile["CensusMap.icons", 1];
indexIcon: Icons.IconFlavor = Icons.NewIconFromFile["CensusMap.icons", 0];
red: Imager.Color ¬ ImagerColor.ColorFromHSV[.75, 1.0, 1.0];
table: ARRAY [1..4] OF Imager.Color;
table[1] ¬ ImagerColor.ColorFromHSV[.05, 1.0, 1.0]; --orange
table[2] ¬ ImagerColor.ColorFromHSV[.65, .75, 1.0]; --blue
table[3] ¬ ImagerColor.ColorFromHSV[.15 ,1.0, 1.0]; --yellow
table[4] ¬ ImagerColor.ColorFromHSV[.32 ,1.0, 1.0]; --green
Commander.Register[key: "CensusMap", proc: CensusMap, doc: "Show/Search Census Dime/GBF File"];
options[tiogaRead] ¬ FALSE;
END.