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;
EXITS
Flush => NULL;
};
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.