1 package net.sourceforge.jgeocoder.tiger;
2 import static net.sourceforge.jgeocoder.AddressComponent.CITY;
3 import static net.sourceforge.jgeocoder.AddressComponent.COUNTY;
4 import static net.sourceforge.jgeocoder.AddressComponent.LAT;
5 import static net.sourceforge.jgeocoder.AddressComponent.LON;
6 import static net.sourceforge.jgeocoder.AddressComponent.STATE;
7 import static net.sourceforge.jgeocoder.AddressComponent.ZIP;
8
9 import java.io.File;
10 import java.util.Map;
11
12 import net.sourceforge.jgeocoder.AddressComponent;
13
14 import org.apache.commons.lang.StringUtils;
15 import org.apache.commons.lang.builder.EqualsBuilder;
16 import org.apache.commons.lang.builder.HashCodeBuilder;
17 import org.apache.commons.lang.builder.ToStringBuilder;
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20
21 import com.sleepycat.je.DatabaseException;
22 import com.sleepycat.je.Environment;
23 import com.sleepycat.je.EnvironmentConfig;
24 import com.sleepycat.persist.EntityStore;
25 import com.sleepycat.persist.PrimaryIndex;
26 import com.sleepycat.persist.SecondaryIndex;
27 import com.sleepycat.persist.StoreConfig;
28 import com.sleepycat.persist.model.Entity;
29 import com.sleepycat.persist.model.KeyField;
30 import com.sleepycat.persist.model.Persistent;
31 import com.sleepycat.persist.model.PrimaryKey;
32 import com.sleepycat.persist.model.Relationship;
33 import com.sleepycat.persist.model.SecondaryKey;
34
35 @Entity
36 class CityWithSpaces{
37 @PrimaryKey
38 private String _noSpace;
39 private String _withSpace;
40 public String getNoSpace() {
41 return _noSpace;
42 }
43 public String getWithSpace() {
44 return _withSpace;
45 }
46 public void setNoSpace(String noSpace) {
47 _noSpace = noSpace;
48 }
49 public void setWithSpace(String withSpace) {
50 _withSpace = withSpace;
51 }
52 @Override
53 public String toString() {
54 return ToStringBuilder.reflectionToString(this);
55 }
56 @Override
57 public int hashCode() {
58 return HashCodeBuilder.reflectionHashCode(this);
59 }
60 @Override
61 public boolean equals(Object obj) {
62 return EqualsBuilder.reflectionEquals(this, obj);
63 }
64 }
65
66 @Entity
67 class CityStateGeo{
68 @PrimaryKey
69 private Location _location;
70 private float _lat;
71 private float _lon;
72 public Location getLocation() {
73 return _location;
74 }
75 public void setLocation(Location location) {
76 _location = location;
77 }
78 public float getLat() {
79 return _lat;
80 }
81 public void setLat(float lat) {
82 _lat = lat;
83 }
84 public float getLon() {
85 return _lon;
86 }
87 public void setLon(float lon) {
88 _lon = lon;
89 }
90 @Override
91 public String toString() {
92 return ToStringBuilder.reflectionToString(this);
93 }
94 @Override
95 public int hashCode() {
96 return HashCodeBuilder.reflectionHashCode(this);
97 }
98 @Override
99 public boolean equals(Object obj) {
100 return EqualsBuilder.reflectionEquals(this, obj);
101 }
102 }
103
104 @Entity
105 class County{
106 @PrimaryKey
107 private Location _location;
108 private String[] _zips;
109 private float _lat;
110 private float _lon;
111 public Location getLocation() {
112 return _location;
113 }
114 public void setLocation(Location _location) {
115 this._location = _location;
116 }
117 public String[] getZips() {
118 return _zips;
119 }
120 public void setZips(String[] _zips) {
121 this._zips = _zips;
122 }
123 public float getLat() {
124 return _lat;
125 }
126 public void setLat(float _lat) {
127 this._lat = _lat;
128 }
129 public float getLon() {
130 return _lon;
131 }
132 public void setLon(float _lon) {
133 this._lon = _lon;
134 }
135 @Override
136 public String toString() {
137 return ToStringBuilder.reflectionToString(this);
138 }
139 @Override
140 public int hashCode() {
141 return HashCodeBuilder.reflectionHashCode(this);
142 }
143 @Override
144 public boolean equals(Object obj) {
145 return EqualsBuilder.reflectionEquals(this, obj);
146 }
147 }
148
149 @Entity
150 class ZipCode{
151 @PrimaryKey
152 private String _zip;
153 @SecondaryKey(relate=Relationship.MANY_TO_ONE)
154 private Location _location;
155 @SecondaryKey(relate=Relationship.MANY_TO_ONE)
156 private String _county;
157 private float _lat;
158 private float _lon;
159 private String _zipClass;
160 public String getZip() {
161 return _zip;
162 }
163 public void setZip(String zip) {
164 _zip = zip;
165 }
166 public Location getLocation() {
167 return _location;
168 }
169 public void setLocation(Location location) {
170 _location = location;
171 }
172 public String getCounty() {
173 return _county;
174 }
175 public void setCounty(String county) {
176 _county = county;
177 }
178 public float getLat() {
179 return _lat;
180 }
181 public void setLat(float lat) {
182 _lat = lat;
183 }
184 public float getLon() {
185 return _lon;
186 }
187 public void setLon(float lon) {
188 _lon = lon;
189 }
190 public String getZipClass() {
191 return _zipClass;
192 }
193 public void setZipClass(String zipClass) {
194 _zipClass = zipClass;
195 }
196 @Override
197 public String toString() {
198 return ToStringBuilder.reflectionToString(this);
199 }
200 @Override
201 public int hashCode() {
202 return HashCodeBuilder.reflectionHashCode(this);
203 }
204 @Override
205 public boolean equals(Object obj) {
206 return EqualsBuilder.reflectionEquals(this, obj);
207 }
208
209 }
210 @Persistent
211 class Location{
212 @KeyField(1)
213 private String _city;
214 @KeyField(2)
215 private String _state;
216
217 public String getCity() {
218 return _city;
219 }
220 public void setCity(String city) {
221 _city = city;
222 }
223 public String getState() {
224 return _state;
225 }
226 public void setState(String state) {
227 _state = state;
228 }
229 @Override
230 public String toString() {
231 return ToStringBuilder.reflectionToString(this);
232 }
233 @Override
234 public int hashCode() {
235 return HashCodeBuilder.reflectionHashCode(this);
236 }
237 @Override
238 public boolean equals(Object obj) {
239 return EqualsBuilder.reflectionEquals(this, obj);
240 }
241 }
242 /***
243 * TODO javadocs me
244 * @author jliang
245 *
246 */
247 class ZipCodeDAO{
248 private static final Log LOGGER = LogFactory.getLog(ZipCodeDAO.class);
249 private PrimaryIndex<String, ZipCode> _zipCodeByZip;
250 private SecondaryIndex<Location, String, ZipCode> _zipCodeByLocation;
251 private PrimaryIndex<Location, CityStateGeo> _cityStateGeoByLocation;
252 private PrimaryIndex<String, CityWithSpaces> _cityWithSpaceByNoSpace;
253 private PrimaryIndex<Location, County> _countyByLocation;
254 public ZipCodeDAO(EntityStore store) throws DatabaseException{
255 _zipCodeByZip = store.getPrimaryIndex(String.class, ZipCode.class);
256 _zipCodeByLocation = store.getSecondaryIndex(_zipCodeByZip, Location.class, "_location");
257 _cityStateGeoByLocation = store.getPrimaryIndex(Location.class, CityStateGeo.class);
258 _cityWithSpaceByNoSpace = store.getPrimaryIndex(String.class, CityWithSpaces.class);
259 _countyByLocation = store.getPrimaryIndex(Location.class, County.class);
260 }
261
262 public PrimaryIndex<Location, County> getCountyByLocation() {
263 return _countyByLocation;
264 }
265
266 public PrimaryIndex<String, CityWithSpaces> getCityWithSpaceByNoSpace() {
267 return _cityWithSpaceByNoSpace;
268 }
269
270 public PrimaryIndex<Location, CityStateGeo> getCityStateGeoByLocation() {
271 return _cityStateGeoByLocation;
272 }
273 public SecondaryIndex<Location, String, ZipCode> getZipCodeByLocation() {
274 return _zipCodeByLocation;
275 }
276 public PrimaryIndex<String, ZipCode> getZipCodeByZip() {
277 return _zipCodeByZip;
278 }
279
280 public boolean fillInCSByZip(Map<AddressComponent, String> m, String zip) throws DatabaseException{
281 return fillInCSByZip(m, _zipCodeByZip.get(zip));
282 }
283
284 private boolean fillInCSByZip(Map<AddressComponent, String> m, ZipCode zipcode) throws DatabaseException{
285 if(zipcode == null){
286 return false;
287 }
288 String city = zipcode.getLocation().getCity();
289 CityWithSpaces cws = getCityWithSpaceByNoSpace().get(city);
290 if(cws != null){
291 m.put(CITY, cws.getWithSpace());
292 }else{
293 m.put(CITY, city);
294 }
295 m.put(COUNTY, zipcode.getCounty());
296 m.put(STATE, zipcode.getLocation().getState());
297 return true;
298 }
299
300 private County getCounty(Location loc){
301 try {
302 return _countyByLocation.get(loc);
303 } catch (DatabaseException e) {
304 if(LOGGER.isDebugEnabled()){
305 LOGGER.debug("Unable to get county by city state", e);
306 }
307 return null;
308 }
309 }
310
311 public County getCounty(String city, String state){
312 if(StringUtils.isBlank(city)||StringUtils.isBlank(state)){
313 return null;
314 }
315 city = city.replaceAll("//s+|//bCOUNTY$|//bPARISH$|//bBOROUGH$", "");
316 Location loc = new Location();
317 loc.setCity(city); loc.setState(state);
318 return getCounty(loc);
319 }
320
321 public boolean geocodeByCityState(Map<AddressComponent, String> m){
322 String city = m.get(AddressComponent.CITY), state = m.get(AddressComponent.STATE);
323 if(StringUtils.isBlank(city)||StringUtils.isBlank(state)){
324 return false;
325 }
326 boolean onlyCounty = city.endsWith("COUNTY");
327 city = city.replaceAll("//s+|//bCOUNTY$|//bPARISH$|//bBOROUGH$", "");
328 try {
329 Location loc = new Location();
330 loc.setCity(city); loc.setState(state);
331 CityStateGeo geo = onlyCounty? null : _cityStateGeoByLocation.get(loc);
332 County county = getCounty(loc);
333 if(geo!= null || county != null){
334 if(onlyCounty && county != null){
335 m.put(LAT, String.valueOf(county.getLat()));
336 m.put(LON, String.valueOf(county.getLon()));
337 return true;
338 }else if(geo != null){
339 m.put(LAT, String.valueOf(geo.getLat()));
340 m.put(LON, String.valueOf(geo.getLon()));
341 return true;
342 }else if(county != null){
343 m.put(LAT, String.valueOf(county.getLat()));
344 m.put(LON, String.valueOf(county.getLon()));
345 return true;
346 }
347 }
348 } catch (DatabaseException e) {
349 if(LOGGER.isDebugEnabled()){
350 LOGGER.debug("Unable to geocode with city state", e);
351 }
352 return false;
353 }
354 return false;
355 }
356
357 public boolean geocodeByZip(Map<AddressComponent, String> m){
358 String zip = m.get(ZIP);
359 if(StringUtils.isBlank(zip)){
360 return false;
361 }
362 try {
363 ZipCode zipcode = _zipCodeByZip.get(zip);
364 if(zipcode != null){
365 if(m.get(LAT) == null){
366 m.put(LAT, String.valueOf(zipcode.getLat()));
367 }
368 if(m.get(LON) == null){
369 m.put(LON, String.valueOf(zipcode.getLon()));
370 }
371 return true;
372 }
373 } catch (Exception e) {
374 if(LOGGER.isDebugEnabled()){
375 LOGGER.debug("Unable to geocode with zip", e);
376 }
377 return false;
378 }
379 return false;
380 }
381 }
382
383 class ZipCodesDb{
384 private Environment _env = null;
385 private EntityStore _store = null;
386 public Environment getEnv() {
387 return _env;
388 }
389 public EntityStore getStore() {
390 return _store;
391 }
392
393 public void init(JGeocoderConfig jgconfig, File envHome, boolean readOnly, boolean transactional) throws DatabaseException{
394
395 EnvironmentConfig config = new EnvironmentConfig();
396 config.setAllowCreate(!readOnly);
397 config.setReadOnly(readOnly);
398 config.setTransactional(transactional);
399 if(jgconfig != null){
400 if(jgconfig.getBerkeleyDbCachePercent() >= 0 ){
401 config.setCacheSize(jgconfig.getBerkeleyDbCacheSize());
402 }
403 if(jgconfig.getBerkeleyDbCachePercent() >= 0){
404 config.setCachePercent(jgconfig.getBerkeleyDbCachePercent());
405 }
406 }
407 _env = new Environment(envHome, config);
408 StoreConfig config2 = new StoreConfig();
409 config2.setAllowCreate(!readOnly);
410 config2.setReadOnly(readOnly);
411 config2.setTransactional(transactional);
412 _store = new EntityStore(_env, "ZipCodeEntityStore", config2);
413 }
414
415 public void init(File envHome, boolean readOnly, boolean transactional) throws DatabaseException{
416 init(null, envHome, readOnly, transactional);
417 }
418
419 public void shutdown() throws DatabaseException{
420 if(_store != null){
421 _store.close();
422 }
423 if(_env != null){
424 _env.close();
425 }
426 }
427
428 }