View Javadoc

1   package net.sourceforge.jgeocoder.tiger;
2   
3   import org.apache.commons.lang.builder.EqualsBuilder;
4   import org.apache.commons.lang.builder.HashCodeBuilder;
5   import org.apache.commons.lang.builder.ToStringBuilder;
6   
7   
8   /*
9    * geocoding estimation functions are ported from sql codes found on 
10   * http://www.johnsample.com/
11   * 
12   * 
13   *   
14   */
15  
16  class Geo{
17    public float lat, lon;
18    public int zip;
19    public long tlid;
20    public Geo(){}
21    public Geo(float lat, float lon, int zip, long tlid) {
22      this.lat = lat;
23      this.lon = lon;
24      this.zip = zip;
25      this.tlid = tlid;
26    }
27    @Override
28    public String toString() {
29      return ToStringBuilder.reflectionToString(this);
30    }
31    @Override
32    public int hashCode() {
33      return HashCodeBuilder.reflectionHashCode(this);
34    }
35    @Override
36    public boolean equals(Object obj) {
37      return EqualsBuilder.reflectionEquals(this, obj);
38    }
39  }
40  
41  class Distance{
42    public float totalLat=0f, totalLon=0f;
43  }
44  /***
45   * TODO javadocs me
46   * @author jliang
47   *
48   */
49  class Geocoder {
50    
51    static Geo geocodeFromHit(int streetnum, TigerLineHit hit){
52      return geocode(streetnum, hit.tlid, Integer.valueOf(hit.frAddR), Integer.valueOf(hit.frAddL), 
53          Integer.valueOf(hit.toAddR), Integer.valueOf(hit.toAddL), 
54          Integer.valueOf(hit.zipL), Integer.valueOf(hit.zipR), hit.toLat, hit.toLon, hit.frLon, hit.frLat, 
55          hit.lon1, hit.lat1, hit.lon2, hit.lat2, hit.lon3, hit.lat3, hit.lon4, hit.lat4, hit.lon5, hit.lat5,
56          hit.lon6, hit.lat6, hit.lon7, hit.lat7, hit.lon8, hit.lat8, hit.lon9, hit.lat9, hit.lon10, hit.lat10);
57    }
58    
59    static Geo geocode(int streetnum, long tlid, int fraddr, int fraddl, int toaddr, int toaddl, 
60      int zipL, int zipR, float tolat, float tolong, float frlong, float frlat,  
61      float long1, float lat1, float long2, float lat2, float long3, float lat3, float long4, float lat4,
62      float long5, float lat5, float long6, float lat6, float long7, float lat7, float long8, float lat8,
63      float long9, float lat9, float long10, float lat10 ){
64      
65      Distance distance = getDistance(tolat, tolong, frlong, frlat, long1, lat1, long2, lat2, long3, lat3, long4, lat4, long5, lat5, long6, lat6, long7, lat7, long8, lat8, long9, lat9, long10, lat10);
66      int addrStart, addrEnd, zip;
67      if(!isLeft(streetnum, fraddr, fraddl, toaddr, toaddl) && (fraddr%2 == streetnum%2)){
68        addrStart = fraddr; addrEnd = toaddr; zip = zipR;
69      }else{
70        addrStart = fraddl; addrEnd = toaddl; zip = zipL;
71      }
72      
73      int dec1 = addrEnd - streetnum, dec2 = addrEnd - addrStart;
74      float rel = dec1*1f / dec2;
75      if(rel == 1f){ //return start
76        return new Geo(frlat, frlong, zip, tlid);
77      }
78      if(rel == 0f){ //return end
79        return new Geo(tolat, tolong, zip, tlid);
80      }
81      //straight line estimate
82  //    float lat = frlat + (rel * distance.totalLat), lon = frlong + (rel * distance.totalLon);
83      
84      float tempEndLat = frlat + distance.totalLat, tempEndLon = frlong + distance.totalLon;
85      float totalDist = getLineDistance(frlong, frlat, tempEndLon, tempEndLat);
86      Geo ret = pointFromChainRatio(totalDist, rel, tolat, tolong, frlong, frlat, long1, lat1, long2, lat2, long3, lat3, long4, lat4, long5, lat5, long6, lat6, long7, lat7, long8, lat8, long9, lat9, long10, lat10);
87      ret.zip = zip; ret.tlid = tlid;
88      return ret;
89    }
90    
91    private static Geo pointFromChainRatio(float totalLength, float ratio,  
92            float tolat, float tolong, float frlong, float frlat,  
93            float long1, float lat1, float long2, float lat2, float long3, float lat3, float long4, float lat4,
94            float long5, float lat5, float long6, float lat6, float long7, float lat7, float long8, float lat8,
95            float long9, float lat9, float long10, float lat10
96            ){
97      float lastLat = frlat, lastLon = frlong;
98      boolean found = false;
99      float totalRatio=0f, totalTravel=0f, travelTarget=ratio*totalLength;
100 //    ---For each lng/lat pair that isn't empty, calculate distance from last non empty pair.
101 //    ---Is the totalRatio + ratio of this segment < the ratio we are looking for?
102 //    ---If yes, add the ratio of this distance to the total ratio
103 //    ---If no, trim the ratio so that it applies only to this segment and return calculated point.
104     float thisLen, thisRatio, useStartLon=frlong, useStartLat=frlat, useEndLon=tolong, useEndLat=tolat, useRatio=ratio;
105 
106     // 10 freaking times of this
107     if(lat1 != 0 && !found){
108       thisLen = getLineDistance(long1, lat1, lastLon, lastLat);
109       thisRatio = thisLen / totalLength;
110       if(thisLen + totalTravel >= travelTarget){
111         useStartLat = lastLat; useStartLon = lastLon;
112         useEndLat = lat1; useEndLon = long1;
113         useRatio = thisRatio; found = true;
114       }else{
115         totalRatio = totalRatio + thisRatio;
116         lastLon = long1; lastLat = lat1;
117         totalTravel = totalTravel + thisLen;
118       }
119     }
120     
121     if(lat2 != 0 && !found){
122       thisLen = getLineDistance(long2, lat2, lastLon, lastLat);
123       thisRatio = thisLen / totalLength;
124       if(thisLen + totalTravel >= travelTarget){
125         useStartLat = lastLat; useStartLon = lastLon;
126         useEndLat = lat2; useEndLon = long2;
127         useRatio = thisRatio; found = true;
128       }else{
129         totalRatio = totalRatio + thisRatio;
130         lastLon = long2; lastLat = lat2;
131         totalTravel = totalTravel + thisLen;
132       }
133     }
134     
135     if(lat3 != 0 && !found){
136       thisLen = getLineDistance(long3, lat3, lastLon, lastLat);
137       thisRatio = thisLen / totalLength;
138       if(thisLen + totalTravel >= travelTarget){
139         useStartLat = lastLat; useStartLon = lastLon;
140         useEndLat = lat3; useEndLon = long3;
141         useRatio = thisRatio; found = true;
142       }else{
143         totalRatio = totalRatio + thisRatio;
144         lastLon = long3; lastLat = lat3;
145         totalTravel = totalTravel + thisLen;
146       }
147     }
148     
149     if(lat4 != 0 && !found){
150       thisLen = getLineDistance(long4, lat4, lastLon, lastLat);
151       thisRatio = thisLen / totalLength;
152       if(thisLen + totalTravel >= travelTarget){
153         useStartLat = lastLat; useStartLon = lastLon;
154         useEndLat = lat4; useEndLon = long4;
155         useRatio = thisRatio; found = true;
156       }else{
157         totalRatio = totalRatio + thisRatio;
158         lastLon = long4; lastLat = lat4;
159         totalTravel = totalTravel + thisLen;
160       }
161     }
162     
163     if(lat5 != 0 && !found){
164       thisLen = getLineDistance(long5, lat5, lastLon, lastLat);
165       thisRatio = thisLen / totalLength;
166       if(thisLen + totalTravel >= travelTarget){
167         useStartLat = lastLat; useStartLon = lastLon;
168         useEndLat = lat5; useEndLon = long5;
169         useRatio = thisRatio; found = true;
170       }else{
171         totalRatio = totalRatio + thisRatio;
172         lastLon = long5; lastLat = lat5;
173         totalTravel = totalTravel + thisLen;
174       }
175     }
176     
177     if(lat6 != 0 && !found){
178       thisLen = getLineDistance(long6, lat6, lastLon, lastLat);
179       thisRatio = thisLen / totalLength;
180       if(thisLen + totalTravel >= travelTarget){
181         useStartLat = lastLat; useStartLon = lastLon;
182         useEndLat = lat6; useEndLon = long6;
183         useRatio = thisRatio; found = true;
184       }else{
185         totalRatio = totalRatio + thisRatio;
186         lastLon = long6; lastLat = lat6;
187         totalTravel = totalTravel + thisLen;
188       }
189     }
190     
191     if(lat7 != 0 && !found){
192       thisLen = getLineDistance(long7, lat7, lastLon, lastLat);
193       thisRatio = thisLen / totalLength;
194       if(thisLen + totalTravel >= travelTarget){
195         useStartLat = lastLat; useStartLon = lastLon;
196         useEndLat = lat7; useEndLon = long7;
197         useRatio = thisRatio; found = true;
198       }else{
199         totalRatio = totalRatio + thisRatio;
200         lastLon = long7; lastLat = lat7;
201         totalTravel = totalTravel + thisLen;
202       }
203     }
204     
205     if(lat8 != 0 && !found){
206       thisLen = getLineDistance(long8, lat8, lastLon, lastLat);
207       thisRatio = thisLen / totalLength;
208       if(thisLen + totalTravel >= travelTarget){
209         useStartLat = lastLat; useStartLon = lastLon;
210         useEndLat = lat8; useEndLon = long8;
211         useRatio = thisRatio; found = true;
212       }else{
213         totalRatio = totalRatio + thisRatio;
214         lastLon = long8; lastLat = lat8;
215         totalTravel = totalTravel + thisLen;
216       }
217     }
218     
219     if(lat9 != 0 && !found){
220       thisLen = getLineDistance(long9, lat9, lastLon, lastLat);
221       thisRatio = thisLen / totalLength;
222       if(thisLen + totalTravel >= travelTarget){
223         useStartLat = lastLat; useStartLon = lastLon;
224         useEndLat = lat9; useEndLon = long9;
225         useRatio = thisRatio; found = true;
226       }else{
227         totalRatio = totalRatio + thisRatio;
228         lastLon = long9; lastLat = lat9;
229         totalTravel = totalTravel + thisLen;
230       }
231     }
232     
233     if(lat10 != 0 && !found){
234       thisLen = getLineDistance(long10, lat10, lastLon, lastLat);
235       thisRatio = thisLen / totalLength;
236       if(thisLen + totalTravel >= travelTarget){
237         useStartLat = lastLat; useStartLon = lastLon;
238         useEndLat = lat10; useEndLon = long10;
239         useRatio = thisRatio; found = true;
240       }else{
241         totalRatio = totalRatio + thisRatio;
242         lastLon = long10; lastLat = lat10;
243         totalTravel = totalTravel + thisLen;
244       }
245     }
246     float rel = (ratio - totalRatio)/ useRatio;
247     float lonDist = useEndLon - useStartLon;
248     float latDist = useEndLat - useStartLat;
249     Geo ret = new Geo();
250     ret.lat = useEndLat - (rel * latDist);
251     ret.lon = useEndLon - (rel * lonDist);
252     return ret;
253   }
254   
255   private static float getLineDistance(float x1, float y1, float x2, float y2){
256     double dx = x2 - x1, dy = y2-y1;
257     return (float)Math.sqrt(dx*dx + dy*dy);
258   }
259   
260   private static boolean isLeft(int streetnum, int fraddr, int fraddl, int toaddr, int toaddl){
261     return fraddr == -1 || (!between(streetnum, fraddr, toaddr) && !between(streetnum, toaddr, fraddr)); 
262   }
263   
264   private static boolean between(int num, int start, int end){
265     return num >= start && num <= end;
266   }
267   
268   private static Distance getDistance(float tolat, float tolong, float frlong, float frlat,  
269       float long1, float lat1, float long2, float lat2, float long3, float lat3, float long4, float lat4,
270       float long5, float lat5, float long6, float lat6, float long7, float lat7, float long8, float lat8,
271       float long9, float lat9, float long10, float lat10){
272     Distance ret = new Distance();
273     float lastLat = frlat, lastLon = frlong;
274     if(lat1 != 0f){
275       ret.totalLat += Math.abs(lat1 - lastLat);
276       ret.totalLon += Math.abs(long1 - lastLon);
277       lastLat = lat1;
278       lastLon = long1;
279     }
280     if(lat2 != 0f){
281       ret.totalLat += Math.abs(lat2 - lastLat);
282       ret.totalLon += Math.abs(long2 - lastLon);
283       lastLat = lat2;
284       lastLon = long2;
285     }
286     
287     if(lat3 != 0f){
288       ret.totalLat += Math.abs(lat3 - lastLat);
289       ret.totalLon += Math.abs(long3 - lastLon);
290       lastLat = lat3;
291       lastLon = long3;
292     }
293     
294     if(lat4 != 0f){
295       ret.totalLat += Math.abs(lat4 - lastLat);
296       ret.totalLon += Math.abs(long4 - lastLon);
297       lastLat = lat4;
298       lastLon = long4;
299     }
300     
301     if(lat5 != 0f){
302       ret.totalLat += Math.abs(lat5 - lastLat);
303       ret.totalLon += Math.abs(long5 - lastLon);
304       lastLat = lat5;
305       lastLon = long5;
306     }
307     
308     if(lat6 != 0f){
309       ret.totalLat += Math.abs(lat6 - lastLat);
310       ret.totalLon += Math.abs(long6 - lastLon);
311       lastLat = lat6;
312       lastLon = long6;
313     }
314     
315     if(lat7 != 0f){
316       ret.totalLat += Math.abs(lat7 - lastLat);
317       ret.totalLon += Math.abs(long7 - lastLon);
318       lastLat = lat7;
319       lastLon = long7;
320     }
321     
322     if(lat8 != 0f){
323       ret.totalLat += Math.abs(lat8 - lastLat);
324       ret.totalLon += Math.abs(long8 - lastLon);
325       lastLat = lat8;
326       lastLon = long8;
327     }
328     
329     if(lat9 != 0f){
330       ret.totalLat += Math.abs(lat9 - lastLat);
331       ret.totalLon += Math.abs(long9 - lastLon);
332       lastLat = lat9;
333       lastLon = long9;
334     }
335     
336     if(lat10 != 0f){
337       ret.totalLat += Math.abs(lat10 - lastLat);
338       ret.totalLon += Math.abs(long10 - lastLon);
339       lastLat = lat10;
340       lastLon = long10;
341     }
342     
343     ret.totalLat += Math.abs(tolat - lastLat);
344     ret.totalLon += Math.abs(tolong - lastLon);
345     
346     return ret;
347   }
348     
349 }