001/* 002 * Copyright 2008-2012 Marc Wick, geonames.org 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 */ 017package org.geonames; 018 019import java.io.BufferedReader; 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.InputStreamReader; 023import java.lang.reflect.Field; 024import java.net.HttpURLConnection; 025import java.net.MalformedURLException; 026import java.net.Proxy; 027import java.net.URL; 028import java.net.URLConnection; 029import java.net.URLEncoder; 030import java.text.ParseException; 031import java.text.SimpleDateFormat; 032import java.util.ArrayList; 033import java.util.List; 034import java.util.TimeZone; 035import java.util.logging.Level; 036import java.util.logging.Logger; 037 038import org.jdom.Document; 039import org.jdom.Element; 040import org.jdom.JDOMException; 041import org.jdom.input.SAXBuilder; 042 043/** 044 * provides static methods to access the <a 045 * href="http://www.geonames.org/export/ws-overview.html">GeoNames web 046 * services</a>. 047 * <p> 048 * Note : values for some fields are only returned with sufficient {@link Style} 049 * . Accessing these fields (admin codes and admin names, elevation,population) 050 * will throw an {@link InsufficientStyleException} if the {@link Style} was not 051 * sufficient. 052 * 053 * @author marc@geonames 054 * 055 */ 056public class WebService { 057 058 private static Logger logger = Logger.getLogger("org.geonames"); 059 060 private static String USER_AGENT = "gnwsc/1.1.13"; 061 062 private static boolean isAndroid = false; 063 064 private static String geoNamesServer = "http://api.geonames.org"; 065 066 private static String geoNamesServerFailover = "http://api.geonames.org"; 067 068 private static long timeOfLastFailureMainServer; 069 070 private static long averageConnectTime; 071 072 private static long averageSampleSize = 20; 073 074 private static Style defaultStyle = Style.MEDIUM; 075 076 private static int readTimeOut = 120000; 077 078 private static int connectTimeOut = 10000; 079 080 private static String DATEFMT = "yyyy-MM-dd HH:mm:ss"; 081 082 private static Proxy proxy; 083 084 static { 085 USER_AGENT += " ("; 086 String os = System.getProperty("os.name"); 087 if (os != null) { 088 USER_AGENT += os + ","; 089 } 090 String osVersion = System.getProperty("os.version"); 091 if (osVersion != null) { 092 USER_AGENT += osVersion; 093 } 094 USER_AGENT += ")"; 095 096 // android version 097 try { 098 Class aClass = Class.forName("android.os.Build"); 099 if (aClass != null) { 100 isAndroid = true; 101 Field[] fields = aClass.getFields(); 102 if (fields != null) { 103 for (Field field : fields) { 104 if ("MODEL".equalsIgnoreCase(field.getName())) { 105 USER_AGENT += "(" + field.get(aClass) + ", "; 106 } 107 } 108 } 109 aClass = Class.forName("android.os.Build$VERSION"); 110 if (aClass != null) { 111 fields = aClass.getFields(); 112 if (fields != null) { 113 for (Field field : fields) { 114 if ("RELEASE".equalsIgnoreCase(field.getName())) { 115 USER_AGENT += field.get(aClass); 116 } 117 } 118 } 119 } 120 USER_AGENT += ")"; 121 } 122 } catch (Throwable t) { 123 } 124 } 125 126 /** 127 * user name to pass to commercial web services for authentication and 128 * authorization 129 */ 130 private static String userName; 131 132 /** 133 * token to pass to as optional authentication parameter to the commercial 134 * web services. 135 */ 136 private static String token; 137 138 /** 139 * adds the username stored in a static variable to the url. It also adds a 140 * token if one has been set with the static setter previously. 141 * 142 * @param url 143 * @return url with the username appended 144 */ 145 private static String addUserName(String url) { 146 if (userName != null) { 147 url = url + "&username=" + userName; 148 } 149 if (token != null) { 150 url = url + "&token=" + token; 151 } 152 return url; 153 } 154 155 /** 156 * adds the default style to the url. The default style can be set with the 157 * static setter. It is 'MEDIUM' if not set. 158 * 159 * @param url 160 * @return url with the style parameter appended 161 */ 162 private static String addDefaultStyle(String url) { 163 if (defaultStyle != Style.MEDIUM) { 164 url = url + "&style=" + defaultStyle.name(); 165 } 166 return url; 167 } 168 169 /** 170 * returns the currently active server. Normally this is the main server, if 171 * the main server recently failed then the failover server is returned. If 172 * the main server is not available we don't want to try with every request 173 * whether it is available again. We switch to the failover server and try 174 * from time to time whether the main server is again accessible. 175 * 176 * @return 177 */ 178 private static String getCurrentlyActiveServer() { 179 if (timeOfLastFailureMainServer == 0) { 180 // no problems with main server 181 return geoNamesServer; 182 } 183 // we had problems with main server 184 if (System.currentTimeMillis() - timeOfLastFailureMainServer > 1000l * 60l * 10l) { 185 // but is was some time ago and we switch back to the main server to 186 // retry. The problem may have been solved in the mean time. 187 timeOfLastFailureMainServer = 0; 188 return geoNamesServer; 189 } 190 if (System.currentTimeMillis() < timeOfLastFailureMainServer) { 191 throw new Error("time of last failure cannot be in future."); 192 } 193 // the problems have been very recent and we continue with failover 194 // server 195 if (geoNamesServerFailover != null) { 196 return geoNamesServerFailover; 197 } 198 return geoNamesServer; 199 } 200 201 /** 202 * @return the isAndroid 203 */ 204 public static boolean isAndroid() { 205 return isAndroid; 206 } 207 208 /** 209 * opens the connection to the url and sets the user agent. In case of an 210 * IOException it checks whether a failover server is set and connects to 211 * the failover server if it has been defined and if it is different from 212 * the normal server. 213 * 214 * @param url 215 * the url to connect to 216 * @return returns the inputstream for the connection 217 * @throws IOException 218 */ 219 private static InputStream connect(String url) throws IOException { 220 int status = 0; 221 String currentlyActiveServer = getCurrentlyActiveServer(); 222 try { 223 long begin = System.currentTimeMillis(); 224 HttpURLConnection httpConnection = null; 225 if (proxy == null) { 226 httpConnection = (HttpURLConnection) new URL( 227 currentlyActiveServer + url).openConnection(); 228 } else { 229 httpConnection = (HttpURLConnection) new URL( 230 currentlyActiveServer + url).openConnection(proxy); 231 } 232 httpConnection.setConnectTimeout(connectTimeOut); 233 httpConnection.setReadTimeout(readTimeOut); 234 httpConnection.setRequestProperty("User-Agent", USER_AGENT); 235 InputStream in = httpConnection.getInputStream(); 236 status = httpConnection.getResponseCode(); 237 238 if (status == 200) { 239 long elapsedTime = System.currentTimeMillis() - begin; 240 averageConnectTime = (averageConnectTime 241 * (averageSampleSize - 1) + elapsedTime) 242 / averageSampleSize; 243 // if the average elapsed time is too long we switch server 244 if (geoNamesServerFailover != null 245 && averageConnectTime > 5000 246 && !currentlyActiveServer 247 .equals(geoNamesServerFailover)) { 248 timeOfLastFailureMainServer = System.currentTimeMillis(); 249 } 250 return in; 251 } 252 } catch (IOException e) { 253 return tryFailoverServer(url, currentlyActiveServer, 0, e); 254 } 255 // we only get here if we had a statuscode <> 200 256 IOException ioException = new IOException("status code " + status 257 + " for " + url); 258 return tryFailoverServer(url, currentlyActiveServer, status, 259 ioException); 260 } 261 262 private static synchronized InputStream tryFailoverServer(String url, 263 String currentlyActiveServer, int status, IOException e) 264 throws MalformedURLException, IOException { 265 // we cannot reach the server 266 logger.log(Level.WARNING, "problems connecting to geonames server " 267 + currentlyActiveServer, e); 268 // is a failover server defined? 269 if (geoNamesServerFailover == null 270 // is it different from the one we are using? 271 || currentlyActiveServer.equals(geoNamesServerFailover)) { 272 if (currentlyActiveServer.equals(geoNamesServerFailover)) { 273 // failover server is not accessible, we throw exception 274 // and switch back to main server. 275 timeOfLastFailureMainServer = 0; 276 } 277 throw e; 278 } 279 timeOfLastFailureMainServer = System.currentTimeMillis(); 280 logger.info("trying to connect to failover server " 281 + geoNamesServerFailover); 282 // try failover server 283 URLConnection conn = null; 284 if (proxy == null) { 285 conn = new URL(geoNamesServerFailover + url).openConnection(); 286 } else { 287 conn = new URL(geoNamesServerFailover + url).openConnection(proxy); 288 } 289 290 String userAgent = USER_AGENT + " failover from " + geoNamesServer; 291 if (status != 0) { 292 userAgent += " " + status; 293 } 294 conn.setRequestProperty("User-Agent", userAgent); 295 InputStream in = conn.getInputStream(); 296 return in; 297 } 298 299 private static Element connectAndParse(String url) 300 throws GeoNamesException, IOException, JDOMException { 301 SAXBuilder parser = new SAXBuilder(); 302 Document doc = parser.build(connect(url)); 303 try { 304 Element root = rootAndCheckException(doc); 305 return root; 306 } catch (GeoNamesException geoNamesException) { 307 if (geoNamesException.getExceptionCode() == 13 308 || (geoNamesException.getMessage() != null && geoNamesException 309 .getMessage() 310 .indexOf( 311 "canceling statement due to statement timeout") > -1)) { 312 String currentlyActiveServer = getCurrentlyActiveServer(); 313 if (geoNamesServerFailover != null 314 && !currentlyActiveServer 315 .equals(geoNamesServerFailover)) { 316 timeOfLastFailureMainServer = System.currentTimeMillis(); 317 doc = parser.build(connect(url)); 318 Element root = rootAndCheckException(doc); 319 return root; 320 } 321 } 322 throw geoNamesException; 323 } 324 } 325 326 private static Element rootAndCheckException(Document doc) 327 throws GeoNamesException { 328 Element root = doc.getRootElement(); 329 checkException(root); 330 return root; 331 } 332 333 private static void checkException(Element root) throws GeoNamesException { 334 Element message = root.getChild("status"); 335 if (message != null) { 336 int code = 0; 337 try { 338 code = Integer.parseInt(message.getAttributeValue("value")); 339 } catch (NumberFormatException numberFormatException) { 340 } 341 throw new GeoNamesException(code, 342 message.getAttributeValue("message")); 343 } 344 } 345 346 /** 347 * check whether we can parse an exception and throw it if we can 348 * 349 * if the line starts with ERR: then we have a geonames exception. 350 * 351 * @param line 352 * @throws GeoNamesException 353 */ 354 private static void checkException(String line) throws GeoNamesException { 355 if (line.startsWith("ERR:")) { 356 String[] terms = line.split(":"); 357 if (terms.length == 3) { 358 throw new GeoNamesException(Integer.parseInt(terms[1]), 359 terms[2]); 360 } 361 throw new GeoNamesException("unhandled exception"); 362 } 363 } 364 365 private static Toponym getToponymFromElement(Element toponymElement) { 366 Toponym toponym = new Toponym(); 367 368 toponym.setName(toponymElement.getChildText("name")); 369 toponym.setAlternateNames(toponymElement.getChildText("alternateNames")); 370 toponym.setLatitude(Double.parseDouble(toponymElement 371 .getChildText("lat"))); 372 toponym.setLongitude(Double.parseDouble(toponymElement 373 .getChildText("lng"))); 374 375 String geonameId = toponymElement.getChildText("geonameId"); 376 if (geonameId != null) { 377 toponym.setGeoNameId(Integer.parseInt(geonameId)); 378 } 379 380 toponym.setContinentCode(toponymElement.getChildText("continentCode")); 381 toponym.setCountryCode(toponymElement.getChildText("countryCode")); 382 toponym.setCountryName(toponymElement.getChildText("countryName")); 383 384 toponym.setFeatureClass(FeatureClass.fromValue(toponymElement 385 .getChildText("fcl"))); 386 toponym.setFeatureCode(toponymElement.getChildText("fcode")); 387 388 toponym.setFeatureClassName(toponymElement.getChildText("fclName")); 389 toponym.setFeatureCodeName(toponymElement.getChildText("fCodeName")); 390 391 String population = toponymElement.getChildText("population"); 392 if (population != null && !"".equals(population)) { 393 toponym.setPopulation(Long.parseLong(population)); 394 } 395 String elevation = toponymElement.getChildText("elevation"); 396 if (elevation != null && !"".equals(elevation)) { 397 toponym.setElevation(Integer.parseInt(elevation)); 398 } 399 400 toponym.setAdminCode1(toponymElement.getChildText("adminCode1")); 401 toponym.setAdminName1(toponymElement.getChildText("adminName1")); 402 toponym.setAdminCode2(toponymElement.getChildText("adminCode2")); 403 toponym.setAdminName2(toponymElement.getChildText("adminName2")); 404 toponym.setAdminCode3(toponymElement.getChildText("adminCode3")); 405 toponym.setAdminName3(toponymElement.getChildText("adminName3")); 406 toponym.setAdminCode4(toponymElement.getChildText("adminCode4")); 407 toponym.setAdminName4(toponymElement.getChildText("adminName4")); 408 toponym.setAdminCode5(toponymElement.getChildText("adminCode5")); 409 toponym.setAdminName5(toponymElement.getChildText("adminName5")); 410 411 Element timezoneElement = toponymElement.getChild("timezone"); 412 if (timezoneElement != null) { 413 Timezone timezone = new Timezone(); 414 timezone.setTimezoneId(timezoneElement.getValue()); 415 timezone.setDstOffset(Double.parseDouble(timezoneElement 416 .getAttributeValue("dstOffset"))); 417 timezone.setGmtOffset(Double.parseDouble(timezoneElement 418 .getAttributeValue("gmtOffset"))); 419 toponym.setTimezone(timezone); 420 } 421 422 Element bboxElement = toponymElement.getChild("bbox"); 423 if (bboxElement != null) { 424 BoundingBox boundingBox = new BoundingBox( 425 Double.parseDouble(bboxElement.getChildText("west")), 426 Double.parseDouble(bboxElement.getChildText("east")), 427 Double.parseDouble(bboxElement.getChildText("south")), 428 Double.parseDouble(bboxElement.getChildText("north"))); 429 toponym.setBoundingBox(boundingBox); 430 } 431 432 return toponym; 433 } 434 435 private static WikipediaArticle getWikipediaArticleFromElement( 436 Element wikipediaArticleElement) { 437 WikipediaArticle wikipediaArticle = new WikipediaArticle(); 438 wikipediaArticle.setLanguage(wikipediaArticleElement 439 .getChildText("lang")); 440 wikipediaArticle 441 .setTitle(wikipediaArticleElement.getChildText("title")); 442 wikipediaArticle.setSummary(wikipediaArticleElement 443 .getChildText("summary")); 444 wikipediaArticle.setFeature(wikipediaArticleElement 445 .getChildText("feature")); 446 wikipediaArticle.setWikipediaUrl(wikipediaArticleElement 447 .getChildText("wikipediaUrl")); 448 wikipediaArticle.setThumbnailImg(wikipediaArticleElement 449 .getChildText("thumbnailImg")); 450 451 wikipediaArticle.setLatitude(Double.parseDouble(wikipediaArticleElement 452 .getChildText("lat"))); 453 wikipediaArticle.setLongitude(Double 454 .parseDouble(wikipediaArticleElement.getChildText("lng"))); 455 456 wikipediaArticle.setRank(Integer.parseInt(wikipediaArticleElement 457 .getChildText("rank"))); 458 459 String population = wikipediaArticleElement.getChildText("population"); 460 if (population != null && !"".equals(population)) { 461 wikipediaArticle.setPopulation(Integer.parseInt(population)); 462 } 463 464 String elevation = wikipediaArticleElement.getChildText("elevation"); 465 if (elevation != null && !"".equals(elevation)) { 466 wikipediaArticle.setElevation(Integer.parseInt(elevation)); 467 } 468 return wikipediaArticle; 469 } 470 471 private static TimeZone utc = TimeZone.getTimeZone("UTC"); 472 473 private static WeatherObservation getWeatherObservationFromElement( 474 Element weatherObservationElement) throws ParseException { 475 WeatherObservation weatherObservation = new WeatherObservation(); 476 weatherObservation.setObservation(weatherObservationElement 477 .getChildText("observation")); 478 SimpleDateFormat df = new SimpleDateFormat(DATEFMT); 479 df.setTimeZone(utc); 480 weatherObservation.setObservationTime(df 481 .parse(weatherObservationElement 482 .getChildText("observationTime"))); 483 weatherObservation.setStationName(weatherObservationElement 484 .getChildText("stationName")); 485 weatherObservation.setIcaoCode(weatherObservationElement 486 .getChildText("ICAO")); 487 weatherObservation.setCountryCode(weatherObservationElement 488 .getChildText("countryCode")); 489 String elevation = weatherObservationElement.getChildText("elevation"); 490 if (elevation != null && !"".equals(elevation)) { 491 weatherObservation.setElevation(Integer.parseInt(elevation)); 492 } 493 weatherObservation.setLatitude(Double 494 .parseDouble(weatherObservationElement.getChildText("lat"))); 495 weatherObservation.setLongitude(Double 496 .parseDouble(weatherObservationElement.getChildText("lng"))); 497 String temperature = weatherObservationElement 498 .getChildText("temperature"); 499 if (temperature != null && !"".equals(temperature)) { 500 weatherObservation.setTemperature(Double.parseDouble(temperature)); 501 } 502 String dewPoint = weatherObservationElement.getChildText("dewPoint"); 503 if (dewPoint != null && !"".equals(dewPoint)) { 504 weatherObservation.setDewPoint(Double.parseDouble(dewPoint)); 505 } 506 String humidity = weatherObservationElement.getChildText("humidity"); 507 if (humidity != null && !"".equals(humidity)) { 508 weatherObservation.setHumidity(Double.parseDouble(humidity)); 509 } 510 weatherObservation.setClouds(weatherObservationElement 511 .getChildText("clouds")); 512 weatherObservation.setWeatherCondition(weatherObservationElement 513 .getChildText("weatherCondition")); 514 weatherObservation.setWindSpeed(weatherObservationElement 515 .getChildText("windSpeed")); 516 return weatherObservation; 517 518 } 519 520 /** 521 * returns a list of postal codes for the given parameters. This method is 522 * for convenience. 523 * 524 * @param postalCode 525 * @param placeName 526 * @param countryCode 527 * @return 528 * @throws Exception 529 */ 530 public static List<PostalCode> postalCodeSearch(String postalCode, 531 String placeName, String countryCode) throws Exception { 532 PostalCodeSearchCriteria postalCodeSearchCriteria = new PostalCodeSearchCriteria(); 533 postalCodeSearchCriteria.setPostalCode(postalCode); 534 postalCodeSearchCriteria.setPlaceName(placeName); 535 postalCodeSearchCriteria.setCountryCode(countryCode); 536 return postalCodeSearch(postalCodeSearchCriteria); 537 } 538 539 /** 540 * returns a list of postal codes for the given search criteria matching a 541 * full text search on the GeoNames postal codes database. 542 * 543 * @param postalCodeSearchCriteria 544 * @return 545 * @throws Exception 546 */ 547 public static List<PostalCode> postalCodeSearch( 548 PostalCodeSearchCriteria postalCodeSearchCriteria) throws Exception { 549 List<PostalCode> postalCodes = new ArrayList<PostalCode>(); 550 551 String url = "/postalCodeSearch?"; 552 if (postalCodeSearchCriteria.getPostalCode() != null) { 553 url = url 554 + "postalcode=" 555 + URLEncoder.encode( 556 postalCodeSearchCriteria.getPostalCode(), "UTF8"); 557 } 558 if (postalCodeSearchCriteria.getPlaceName() != null) { 559 if (!url.endsWith("&")) { 560 url = url + "&"; 561 } 562 url = url 563 + "placename=" 564 + URLEncoder.encode( 565 postalCodeSearchCriteria.getPlaceName(), "UTF8"); 566 } 567 if (postalCodeSearchCriteria.getAdminCode1() != null) { 568 url = url 569 + "&adminCode1=" 570 + URLEncoder.encode( 571 postalCodeSearchCriteria.getAdminCode1(), "UTF8"); 572 } 573 574 if (postalCodeSearchCriteria.getCountryCode() != null) { 575 if (!url.endsWith("&")) { 576 url = url + "&"; 577 } 578 url = url + "country=" + postalCodeSearchCriteria.getCountryCode(); 579 } 580 if (postalCodeSearchCriteria.getCountryBias() != null) { 581 if (!url.endsWith("&")) { 582 url = url + "&"; 583 } 584 url = url + "countryBias=" 585 + postalCodeSearchCriteria.getCountryBias(); 586 } 587 if (postalCodeSearchCriteria.getMaxRows() > 0) { 588 url = url + "&maxRows=" + postalCodeSearchCriteria.getMaxRows(); 589 } 590 if (postalCodeSearchCriteria.getStartRow() > 0) { 591 url = url + "&startRow=" + postalCodeSearchCriteria.getStartRow(); 592 } 593 if (postalCodeSearchCriteria.isOROperator()) { 594 url = url + "&operator=OR"; 595 } 596 if (postalCodeSearchCriteria.isReduced() != null) { 597 url = url + "&isReduced=" 598 + postalCodeSearchCriteria.isReduced().toString(); 599 } 600 if (postalCodeSearchCriteria.getBoundingBox() != null) { 601 url = url + "&east=" 602 + postalCodeSearchCriteria.getBoundingBox().getEast(); 603 url = url + "&west=" 604 + postalCodeSearchCriteria.getBoundingBox().getWest(); 605 url = url + "&north=" 606 + postalCodeSearchCriteria.getBoundingBox().getNorth(); 607 url = url + "&south=" 608 + postalCodeSearchCriteria.getBoundingBox().getSouth(); 609 } 610 611 url = addUserName(url); 612 613 Element root = connectAndParse(url); 614 for (Object obj : root.getChildren("code")) { 615 Element codeElement = (Element) obj; 616 PostalCode code = getPostalCodeFromElement(codeElement); 617 postalCodes.add(code); 618 } 619 620 return postalCodes; 621 } 622 623 /** 624 * returns a list of postal codes 625 * 626 * @param postalCodeSearchCriteria 627 * @return 628 * @throws Exception 629 */ 630 public static List<PostalCode> findNearbyPostalCodes( 631 PostalCodeSearchCriteria postalCodeSearchCriteria) throws Exception { 632 633 List<PostalCode> postalCodes = new ArrayList<PostalCode>(); 634 635 String url = "/findNearbyPostalCodes?"; 636 if (postalCodeSearchCriteria.getPostalCode() != null) { 637 url = url 638 + "&postalcode=" 639 + URLEncoder.encode( 640 postalCodeSearchCriteria.getPostalCode(), "UTF8"); 641 } 642 if (postalCodeSearchCriteria.getPlaceName() != null) { 643 url = url 644 + "&placename=" 645 + URLEncoder.encode( 646 postalCodeSearchCriteria.getPlaceName(), "UTF8"); 647 } 648 if (postalCodeSearchCriteria.getCountryCode() != null) { 649 url = url + "&country=" + postalCodeSearchCriteria.getCountryCode(); 650 } 651 652 if (postalCodeSearchCriteria.getLatitude() != null) { 653 url = url + "&lat=" + postalCodeSearchCriteria.getLatitude(); 654 } 655 if (postalCodeSearchCriteria.getLongitude() != null) { 656 url = url + "&lng=" + postalCodeSearchCriteria.getLongitude(); 657 } 658 if (postalCodeSearchCriteria.getStyle() != null) { 659 url = url + "&style=" + postalCodeSearchCriteria.getStyle(); 660 } 661 if (postalCodeSearchCriteria.getMaxRows() > 0) { 662 url = url + "&maxRows=" + postalCodeSearchCriteria.getMaxRows(); 663 } 664 665 if (postalCodeSearchCriteria.getRadius() > 0) { 666 url = url + "&radius=" + postalCodeSearchCriteria.getRadius(); 667 } 668 url = addUserName(url); 669 670 Element root = connectAndParse(url); 671 for (Object obj : root.getChildren("code")) { 672 Element codeElement = (Element) obj; 673 PostalCode code = getPostalCodeFromElement(codeElement); 674 if (codeElement.getChildText("distance") != null) { 675 code.setDistance(Double.parseDouble(codeElement 676 .getChildText("distance"))); 677 } 678 postalCodes.add(code); 679 } 680 681 return postalCodes; 682 } 683 684 private static PostalCode getPostalCodeFromElement(Element codeElement) 685 throws ParseException { 686 PostalCode code = new PostalCode(); 687 code.setPostalCode(codeElement.getChildText("postalcode")); 688 code.setPlaceName(codeElement.getChildText("name")); 689 code.setCountryCode(codeElement.getChildText("countryCode")); 690 691 code.setLatitude(Double.parseDouble(codeElement.getChildText("lat"))); 692 code.setLongitude(Double.parseDouble(codeElement.getChildText("lng"))); 693 694 code.setAdminName1(codeElement.getChildText("adminName1")); 695 code.setAdminCode1(codeElement.getChildText("adminCode1")); 696 code.setAdminName2(codeElement.getChildText("adminName2")); 697 code.setAdminCode2(codeElement.getChildText("adminCode2")); 698 code.setAdminName3(codeElement.getChildText("adminName3")); 699 code.setAdminCode3(codeElement.getChildText("adminCode3")); 700 return code; 701 } 702 703 /** 704 * convenience method for 705 * {@link #findNearbyPlaceName(double,double,double,int)} 706 * 707 * @param latitude 708 * @param longitude 709 * @return 710 * @throws IOException 711 * @throws Exception 712 */ 713 public static List<Toponym> findNearbyPlaceName(double latitude, 714 double longitude) throws IOException, Exception { 715 return findNearbyPlaceName(latitude, longitude, 0, 0); 716 } 717 718 public static List<Toponym> findNearbyPlaceName(double latitude, 719 double longitude, double radius, int maxRows) throws IOException, 720 Exception { 721 List<Toponym> places = new ArrayList<Toponym>(); 722 723 String url = "/findNearbyPlaceName?"; 724 725 url = url + "&lat=" + latitude; 726 url = url + "&lng=" + longitude; 727 if (radius > 0) { 728 url = url + "&radius=" + radius; 729 } 730 if (maxRows > 0) { 731 url = url + "&maxRows=" + maxRows; 732 } 733 url = addUserName(url); 734 url = addDefaultStyle(url); 735 736 Element root = connectAndParse(url); 737 for (Object obj : root.getChildren("geoname")) { 738 Element toponymElement = (Element) obj; 739 Toponym toponym = getToponymFromElement(toponymElement); 740 places.add(toponym); 741 } 742 743 return places; 744 } 745 746 public static List<Toponym> findNearby(double latitude, double longitude, 747 FeatureClass featureClass, String[] featureCodes) 748 throws IOException, Exception { 749 return findNearby(latitude, longitude, 0, featureClass, featureCodes, 750 null, 0); 751 } 752 753 /* Overload function to allow backward compatibility */ 754 /** 755 * Based on the following inforamtion: Webservice Type : REST 756 * api.geonames.org/findNearbyWikipedia? Parameters : lang : language code 757 * (around 240 languages) (default = en) lat,lng, radius (in km), maxRows 758 * (default = 10) featureClass featureCode Example: 759 * http://api.geonames.org/findNearby?lat=47.3&lng=9 760 * 761 * @param: latitude 762 * @param: longitude 763 * @param: radius 764 * @param: feature Class 765 * @param: feature Codes 766 * @param: language 767 * @param: maxRows 768 * @return: list of wikipedia articles 769 * @throws: Exception 770 */ 771 public static List<Toponym> findNearby(double latitude, double longitude, 772 double radius, FeatureClass featureClass, String[] featureCodes, 773 String language, int maxRows) throws IOException, Exception { 774 List<Toponym> places = new ArrayList<Toponym>(); 775 776 String url = "/findNearby?"; 777 778 url += "&lat=" + latitude; 779 url += "&lng=" + longitude; 780 if (radius > 0) { 781 url = url + "&radius=" + radius; 782 } 783 if (maxRows > 0) { 784 url = url + "&maxRows=" + maxRows; 785 } 786 787 if (language != null) { 788 url = url + "&lang=" + language; 789 } 790 791 if (featureClass != null) { 792 url += "&featureClass=" + featureClass; 793 } 794 if (featureCodes != null && featureCodes.length > 0) { 795 for (String featureCode : featureCodes) { 796 url += "&featureCode=" + featureCode; 797 } 798 } 799 800 url = addUserName(url); 801 url = addDefaultStyle(url); 802 803 Element root = connectAndParse(url); 804 for (Object obj : root.getChildren("geoname")) { 805 Element toponymElement = (Element) obj; 806 Toponym toponym = getToponymFromElement(toponymElement); 807 places.add(toponym); 808 } 809 810 return places; 811 } 812 813 /** 814 * 815 * @param geoNameId 816 * @param language 817 * - optional 818 * @param style 819 * - optional 820 * @return the toponym for the geoNameId 821 * @throws IOException 822 * @throws Exception 823 */ 824 public static Toponym get(int geoNameId, String language, String style) 825 throws IOException, Exception { 826 String url = "/get?"; 827 828 url += "geonameId=" + geoNameId; 829 830 if (language != null) { 831 url = url + "&lang=" + language; 832 } 833 834 if (style != null) { 835 url = url + "&style=" + style; 836 } else { 837 url = addDefaultStyle(url); 838 } 839 url = addUserName(url); 840 841 Element root = connectAndParse(url); 842 Toponym toponym = getToponymFromElement(root); 843 return toponym; 844 } 845 846 public static Address findNearestAddress(double latitude, double longitude) 847 throws IOException, Exception { 848 849 String url = "/findNearestAddress?"; 850 851 url = url + "&lat=" + latitude; 852 url = url + "&lng=" + longitude; 853 url = addUserName(url); 854 855 Element root = connectAndParse(url); 856 for (Object obj : root.getChildren("address")) { 857 Element codeElement = (Element) obj; 858 Address address = new Address(); 859 address.setStreet(codeElement.getChildText("street")); 860 address.setStreetNumber(codeElement.getChildText("streetNumber")); 861 address.setMtfcc(codeElement.getChildText("mtfcc")); 862 863 address.setPostalCode(codeElement.getChildText("postalcode")); 864 address.setPlaceName(codeElement.getChildText("placename")); 865 address.setCountryCode(codeElement.getChildText("countryCode")); 866 867 address.setLatitude(Double.parseDouble(codeElement 868 .getChildText("lat"))); 869 address.setLongitude(Double.parseDouble(codeElement 870 .getChildText("lng"))); 871 872 address.setAdminName1(codeElement.getChildText("adminName1")); 873 address.setAdminCode1(codeElement.getChildText("adminCode1")); 874 address.setAdminName2(codeElement.getChildText("adminName2")); 875 address.setAdminCode2(codeElement.getChildText("adminCode2")); 876 877 address.setDistance(Double.parseDouble(codeElement 878 .getChildText("distance"))); 879 880 return address; 881 } 882 883 return null; 884 } 885 886 public static Intersection findNearestIntersection(double latitude, 887 double longitude) throws Exception { 888 return findNearestIntersection(latitude, longitude, 0); 889 } 890 891 public static Intersection findNearestIntersection(double latitude, 892 double longitude, double radius) throws Exception { 893 894 String url = "/findNearestIntersection?"; 895 896 url = url + "&lat=" + latitude; 897 url = url + "&lng=" + longitude; 898 if (radius > 0) { 899 url = url + "&radius=" + radius; 900 } 901 url = addUserName(url); 902 903 Element root = connectAndParse(url); 904 for (Object obj : root.getChildren("intersection")) { 905 Element e = (Element) obj; 906 Intersection intersection = new Intersection(); 907 intersection.setStreet1(e.getChildText("street1")); 908 intersection.setStreet2(e.getChildText("street2")); 909 intersection.setLatitude(Double.parseDouble(e.getChildText("lat"))); 910 intersection 911 .setLongitude(Double.parseDouble(e.getChildText("lng"))); 912 intersection.setDistance(Double.parseDouble(e 913 .getChildText("distance"))); 914 intersection.setPostalCode(e.getChildText("postalcode")); 915 intersection.setPlaceName(e.getChildText("placename")); 916 intersection.setCountryCode(e.getChildText("countryCode")); 917 intersection.setAdminName2(e.getChildText("adminName2")); 918 intersection.setAdminCode1(e.getChildText("adminCode1")); 919 intersection.setAdminName1(e.getChildText("adminName1")); 920 return intersection; 921 } 922 return null; 923 } 924 925 /** 926 * 927 * @see <a * href= 928 * "http://www.geonames.org/maps/reverse-geocoder.html#findNearbyStreets" 929 * > web service documentation</a> 930 * 931 * @param latitude 932 * @param longitude 933 * @param radius 934 * @return 935 * @throws Exception 936 */ 937 public static List<StreetSegment> findNearbyStreets(double latitude, 938 double longitude, double radius) throws Exception { 939 940 String url = "/findNearbyStreets?"; 941 942 url = url + "&lat=" + latitude; 943 url = url + "&lng=" + longitude; 944 if (radius > 0) { 945 url = url + "&radius=" + radius; 946 } 947 url = addUserName(url); 948 949 List<StreetSegment> segments = new ArrayList<StreetSegment>(); 950 951 Element root = connectAndParse(url); 952 for (Object obj : root.getChildren("streetSegment")) { 953 Element e = (Element) obj; 954 StreetSegment streetSegment = new StreetSegment(); 955 String line = e.getChildText("line"); 956 String[] points = line.split(","); 957 double[] latArray = new double[points.length]; 958 double[] lngArray = new double[points.length]; 959 for (int i = 0; i < points.length; i++) { 960 String[] coords = points[i].split(" "); 961 lngArray[i] = Double.parseDouble(coords[0]); 962 latArray[i] = Double.parseDouble(coords[1]); 963 } 964 965 streetSegment.setCfcc(e.getChildText("cfcc")); 966 streetSegment.setName(e.getChildText("name")); 967 streetSegment.setFraddl(e.getChildText("fraddl")); 968 streetSegment.setFraddr(e.getChildText("fraddr")); 969 streetSegment.setToaddl(e.getChildText("toaddl")); 970 streetSegment.setToaddr(e.getChildText("toaddr")); 971 streetSegment.setPostalCode(e.getChildText("postalcode")); 972 streetSegment.setPlaceName(e.getChildText("placename")); 973 streetSegment.setCountryCode(e.getChildText("countryCode")); 974 streetSegment.setAdminName2(e.getChildText("adminName2")); 975 streetSegment.setAdminCode1(e.getChildText("adminCode1")); 976 streetSegment.setAdminName1(e.getChildText("adminName1")); 977 segments.add(streetSegment); 978 } 979 return segments; 980 } 981 982 public static List<StreetSegment> findNearbyStreetsOSM(double latitude, 983 double longitude, double radius) throws Exception { 984 985 String url = "/findNearbyStreetsOSM?"; 986 987 url = url + "&lat=" + latitude; 988 url = url + "&lng=" + longitude; 989 if (radius > 0) { 990 url = url + "&radius=" + radius; 991 } 992 url = addUserName(url); 993 994 List<StreetSegment> segments = new ArrayList<StreetSegment>(); 995 996 Element root = connectAndParse(url); 997 for (Object obj : root.getChildren("streetSegment")) { 998 Element e = (Element) obj; 999 StreetSegment streetSegment = new StreetSegment(); 1000 String line = e.getChildText("line"); 1001 String[] points = line.split(","); 1002 double[] latArray = new double[points.length]; 1003 double[] lngArray = new double[points.length]; 1004 for (int i = 0; i < points.length; i++) { 1005 String[] coords = points[i].split(" "); 1006 lngArray[i] = Double.parseDouble(coords[0]); 1007 latArray[i] = Double.parseDouble(coords[1]); 1008 } 1009 1010 streetSegment.setName(e.getChildText("name")); 1011 segments.add(streetSegment); 1012 } 1013 return segments; 1014 } 1015 1016 /** 1017 * convenience method for {@link #search(ToponymSearchCriteria)} 1018 * 1019 * @see <a href="http://www.geonames.org/export/geonames-search.html">search 1020 * web service documentation</a> 1021 * 1022 * @param q 1023 * @param countryCode 1024 * @param name 1025 * @param featureCodes 1026 * @param startRow 1027 * @return 1028 * @throws Exception 1029 */ 1030 public static ToponymSearchResult search(String q, String countryCode, 1031 String name, String[] featureCodes, int startRow) throws Exception { 1032 return search(q, countryCode, name, featureCodes, startRow, null, null, 1033 null); 1034 } 1035 1036 /** 1037 * convenience method for {@link #search(ToponymSearchCriteria)} 1038 * 1039 * The string fields will be transparently utf8 encoded within the call. 1040 * 1041 * @see <a href="http://www.geonames.org/export/geonames-search.html">search 1042 * web service documentation</a> 1043 * 1044 * @param q 1045 * search over all fields 1046 * @param countryCode 1047 * @param name 1048 * search over name only 1049 * @param featureCodes 1050 * @param startRow 1051 * @param language 1052 * @param style 1053 * @param exactName 1054 * @return 1055 * @throws Exception 1056 */ 1057 public static ToponymSearchResult search(String q, String countryCode, 1058 String name, String[] featureCodes, int startRow, String language, 1059 Style style, String exactName) throws Exception { 1060 ToponymSearchCriteria searchCriteria = new ToponymSearchCriteria(); 1061 searchCriteria.setQ(q); 1062 searchCriteria.setCountryCode(countryCode); 1063 searchCriteria.setName(name); 1064 searchCriteria.setFeatureCodes(featureCodes); 1065 searchCriteria.setStartRow(startRow); 1066 searchCriteria.setLanguage(language); 1067 searchCriteria.setStyle(style); 1068 searchCriteria.setNameEquals(exactName); 1069 return search(searchCriteria); 1070 } 1071 1072 /** 1073 * full text search on the GeoNames database. 1074 * 1075 * This service gets the number of toponyms defined by the 'maxRows' 1076 * parameter. The parameter 'style' determines which fields are returned by 1077 * the service. 1078 * 1079 * @see <a href="http://www.geonames.org/export/geonames-search.html">search 1080 * web service documentation</a> 1081 * 1082 * <br> 1083 * 1084 * <pre> 1085 * ToponymSearchCriteria searchCriteria = new ToponymSearchCriteria(); 1086 * searchCriteria.setQ("z&uumlrich"); 1087 * ToponymSearchResult searchResult = WebService.search(searchCriteria); 1088 * for (Toponym toponym : searchResult.toponyms) { 1089 * System.out.println(toponym.getName() + " " + toponym.getCountryName()); 1090 * } 1091 * </pre> 1092 * 1093 * 1094 * @param searchCriteria 1095 * @return 1096 * @throws Exception 1097 */ 1098 public static ToponymSearchResult search( 1099 ToponymSearchCriteria searchCriteria) throws Exception { 1100 ToponymSearchResult searchResult = new ToponymSearchResult(); 1101 1102 String url = "/search?"; 1103 1104 if (searchCriteria.getQ() != null) { 1105 url = url + "q=" + URLEncoder.encode(searchCriteria.getQ(), "UTF8"); 1106 } 1107 if (searchCriteria.getNameEquals() != null) { 1108 url = url + "&name_equals=" 1109 + URLEncoder.encode(searchCriteria.getNameEquals(), "UTF8"); 1110 } 1111 if (searchCriteria.getNameStartsWith() != null) { 1112 url = url 1113 + "&name_startsWith=" 1114 + URLEncoder.encode(searchCriteria.getNameStartsWith(), 1115 "UTF8"); 1116 } 1117 1118 if (searchCriteria.getName() != null) { 1119 url = url + "&name=" 1120 + URLEncoder.encode(searchCriteria.getName(), "UTF8"); 1121 } 1122 1123 if (searchCriteria.getTag() != null) { 1124 url = url + "&tag=" 1125 + URLEncoder.encode(searchCriteria.getTag(), "UTF8"); 1126 } 1127 1128 if (searchCriteria.getCountryCode() != null) { 1129 url = url + "&country=" + searchCriteria.getCountryCode(); 1130 } 1131 if (searchCriteria.getCountryCodes() != null) { 1132 for (String countryCode : searchCriteria.getCountryCodes()) { 1133 url = url + "&country=" + countryCode; 1134 } 1135 } 1136 if (searchCriteria.getCountryBias() != null) { 1137 if (!url.endsWith("&")) { 1138 url = url + "&"; 1139 } 1140 url = url + "countryBias=" + searchCriteria.getCountryBias(); 1141 } 1142 if (searchCriteria.getContinentCode() != null) { 1143 url = url + "&continentCode=" + searchCriteria.getContinentCode(); 1144 } 1145 1146 if (searchCriteria.getAdminCode1() != null) { 1147 url = url + "&adminCode1=" 1148 + URLEncoder.encode(searchCriteria.getAdminCode1(), "UTF8"); 1149 } 1150 if (searchCriteria.getAdminCode2() != null) { 1151 url = url + "&adminCode2=" 1152 + URLEncoder.encode(searchCriteria.getAdminCode2(), "UTF8"); 1153 } 1154 if (searchCriteria.getAdminCode3() != null) { 1155 url = url + "&adminCode3=" 1156 + URLEncoder.encode(searchCriteria.getAdminCode3(), "UTF8"); 1157 } 1158 if (searchCriteria.getAdminCode4() != null) { 1159 url = url + "&adminCode4=" 1160 + URLEncoder.encode(searchCriteria.getAdminCode4(), "UTF8"); 1161 } 1162 1163 if (searchCriteria.getLanguage() != null) { 1164 url = url + "&lang=" + searchCriteria.getLanguage(); 1165 } 1166 1167 if (searchCriteria.getFeatureClass() != null) { 1168 url = url + "&featureClass=" + searchCriteria.getFeatureClass(); 1169 } 1170 1171 if (searchCriteria.getFeatureCodes() != null) { 1172 for (String featureCode : searchCriteria.getFeatureCodes()) { 1173 url = url + "&fcode=" + featureCode; 1174 } 1175 } 1176 if (searchCriteria.getMaxRows() > 0) { 1177 url = url + "&maxRows=" + searchCriteria.getMaxRows(); 1178 } 1179 if (searchCriteria.getStartRow() > 0) { 1180 url = url + "&startRow=" + searchCriteria.getStartRow(); 1181 } 1182 if (searchCriteria.getFuzzy() != 1.0) { 1183 url = url + "&fuzzy=" + searchCriteria.getFuzzy(); 1184 } 1185 1186 if (searchCriteria.getBoundingBox() != null) { 1187 url = url + "&east=" + searchCriteria.getBoundingBox().getEast(); 1188 url = url + "&west=" + searchCriteria.getBoundingBox().getWest(); 1189 url = url + "&north=" + searchCriteria.getBoundingBox().getNorth(); 1190 url = url + "&south=" + searchCriteria.getBoundingBox().getSouth(); 1191 } 1192 1193 if (searchCriteria.getStyle() != null) { 1194 url = url + "&style=" + searchCriteria.getStyle(); 1195 } else { 1196 url = addDefaultStyle(url); 1197 } 1198 url = addUserName(url); 1199 1200 Element root = connectAndParse(url); 1201 searchResult.totalResultsCount = Integer.parseInt(root 1202 .getChildText("totalResultsCount")); 1203 searchResult.setStyle(Style.valueOf(root.getAttributeValue("style"))); 1204 1205 for (Object obj : root.getChildren("geoname")) { 1206 Element toponymElement = (Element) obj; 1207 Toponym toponym = getToponymFromElement(toponymElement); 1208 toponym.setStyle(searchResult.getStyle()); 1209 searchResult.toponyms.add(toponym); 1210 } 1211 1212 return searchResult; 1213 } 1214 1215 /** 1216 * returns the children in the administrative hierarchy of a toponym. With 1217 * default maxRows. 1218 * 1219 * @param geonameId 1220 * @param language 1221 * @param style 1222 * @return 1223 * @throws Exception 1224 */ 1225 public static ToponymSearchResult children(int geonameId, String language, 1226 Style style) throws Exception { 1227 return children(geonameId, language, style, 0); 1228 } 1229 1230 /** 1231 * 1232 * @param geonameId 1233 * @param language 1234 * @param style 1235 * @param maxRows 1236 * @return 1237 * @throws Exception 1238 */ 1239 public static ToponymSearchResult children(int geonameId, String language, 1240 Style style, int maxRows) throws Exception { 1241 1242 ToponymSearchResult searchResult = new ToponymSearchResult(); 1243 1244 String url = "/children?"; 1245 1246 url = url + "geonameId=" + geonameId; 1247 1248 if (language != null) { 1249 url = url + "&lang=" + language; 1250 } 1251 if (maxRows != 0) { 1252 url += "&maxRows=" + maxRows; 1253 } 1254 1255 if (style != null) { 1256 url = url + "&style=" + style; 1257 } else { 1258 url = addDefaultStyle(url); 1259 } 1260 url = addUserName(url); 1261 1262 Element root = connectAndParse(url); 1263 searchResult.totalResultsCount = Integer.parseInt(root 1264 .getChildText("totalResultsCount")); 1265 searchResult.setStyle(Style.valueOf(root.getAttributeValue("style"))); 1266 1267 for (Object obj : root.getChildren("geoname")) { 1268 Element toponymElement = (Element) obj; 1269 Toponym toponym = getToponymFromElement(toponymElement); 1270 searchResult.toponyms.add(toponym); 1271 } 1272 1273 return searchResult; 1274 } 1275 1276 /** 1277 * returns the neighbours of a toponym. 1278 * 1279 * @param geonameId 1280 * @param language 1281 * @param style 1282 * @return 1283 * @throws Exception 1284 */ 1285 public static ToponymSearchResult neighbours(int geonameId, 1286 String language, Style style) throws Exception { 1287 ToponymSearchResult searchResult = new ToponymSearchResult(); 1288 1289 String url = "/neighbours?"; 1290 1291 url = url + "geonameId=" + geonameId; 1292 1293 if (language != null) { 1294 url = url + "&lang=" + language; 1295 } 1296 1297 if (style != null) { 1298 url = url + "&style=" + style; 1299 } else { 1300 url = addDefaultStyle(url); 1301 } 1302 url = addUserName(url); 1303 1304 Element root = connectAndParse(url); 1305 searchResult.totalResultsCount = Integer.parseInt(root 1306 .getChildText("totalResultsCount")); 1307 searchResult.setStyle(Style.valueOf(root.getAttributeValue("style"))); 1308 1309 for (Object obj : root.getChildren("geoname")) { 1310 Element toponymElement = (Element) obj; 1311 Toponym toponym = getToponymFromElement(toponymElement); 1312 searchResult.toponyms.add(toponym); 1313 } 1314 1315 return searchResult; 1316 } 1317 1318 /** 1319 * returns the hierarchy for a geonameId 1320 * 1321 * @see <a 1322 * href="http://www.geonames.org/export/place-hierarchy.html#hierarchy">Hierarchy 1323 * service description</a> 1324 * 1325 * @param geonameId 1326 * @param language 1327 * @param style 1328 * @return 1329 * @throws Exception 1330 */ 1331 public static List<Toponym> hierarchy(int geonameId, String language, 1332 Style style) throws Exception { 1333 1334 String url = "/hierarchy?"; 1335 1336 url = url + "geonameId=" + geonameId; 1337 1338 if (language != null) { 1339 url = url + "&lang=" + language; 1340 } 1341 1342 if (style != null) { 1343 url = url + "&style=" + style; 1344 } else { 1345 url = addDefaultStyle(url); 1346 } 1347 url = addUserName(url); 1348 1349 Element root = connectAndParse(url); 1350 List<Toponym> toponyms = new ArrayList<Toponym>(); 1351 for (Object obj : root.getChildren("geoname")) { 1352 Element toponymElement = (Element) obj; 1353 Toponym toponym = getToponymFromElement(toponymElement); 1354 toponyms.add(toponym); 1355 } 1356 1357 return toponyms; 1358 } 1359 1360 public static void saveTags(String[] tags, Toponym toponym, 1361 String username, String password) throws Exception { 1362 if (toponym.getGeoNameId() == 0) { 1363 throw new Error("no geonameid specified"); 1364 } 1365 1366 // FIXME proper url 1367 String url = "/servlet/geonames?srv=61"; 1368 1369 url = url + "&geonameId=" + toponym.getGeoNameId(); 1370 url = addUserName(url); 1371 1372 StringBuilder tagsCommaseparated = new StringBuilder(); 1373 for (String tag : tags) { 1374 tagsCommaseparated.append(tag + ","); 1375 } 1376 url = url + "&tag=" + tagsCommaseparated; 1377 1378 Element root = connectAndParse(url); 1379 } 1380 1381 /** 1382 * full text search on geolocated wikipedia articles. 1383 * 1384 * @param q 1385 * @param language 1386 * @return 1387 * @throws Exception 1388 */ 1389 public static List<WikipediaArticle> wikipediaSearch(String q, 1390 String language) throws Exception { 1391 List<WikipediaArticle> articles = new ArrayList<WikipediaArticle>(); 1392 1393 String url = "/wikipediaSearch?"; 1394 1395 url = url + "q=" + URLEncoder.encode(q, "UTF8"); 1396 1397 if (language != null) { 1398 url = url + "&lang=" + language; 1399 } 1400 url = addUserName(url); 1401 1402 Element root = connectAndParse(url); 1403 for (Object obj : root.getChildren("entry")) { 1404 Element wikipediaArticleElement = (Element) obj; 1405 WikipediaArticle wikipediaArticle = getWikipediaArticleFromElement(wikipediaArticleElement); 1406 articles.add(wikipediaArticle); 1407 } 1408 1409 return articles; 1410 } 1411 1412 /** 1413 * full text search on geolocated wikipedia articles. 1414 * 1415 * @param title 1416 * @param language 1417 * @return 1418 * @throws Exception 1419 */ 1420 public static List<WikipediaArticle> wikipediaSearchForTitle(String title, 1421 String language) throws Exception { 1422 List<WikipediaArticle> articles = new ArrayList<WikipediaArticle>(); 1423 1424 String url = "/wikipediaSearch?"; 1425 1426 url = url + "title=" + URLEncoder.encode(title, "UTF8"); 1427 1428 if (language != null) { 1429 url = url + "&lang=" + language; 1430 } 1431 url = addUserName(url); 1432 1433 Element root = connectAndParse(url); 1434 for (Object obj : root.getChildren("entry")) { 1435 Element wikipediaArticleElement = (Element) obj; 1436 WikipediaArticle wikipediaArticle = getWikipediaArticleFromElement(wikipediaArticleElement); 1437 articles.add(wikipediaArticle); 1438 } 1439 1440 return articles; 1441 } 1442 1443 public static List<WikipediaArticle> findNearbyWikipedia(double latitude, 1444 double longitude, String language) throws Exception { 1445 return findNearbyWikipedia(latitude, longitude, 0, language, 0); 1446 } 1447 1448 /* Overload function to allow backward compatibility */ 1449 /** 1450 * Based on the following inform: Webservice Type : REST 1451 * api.geonames.org/findNearbyWikipedia? Parameters : lang : language code 1452 * (around 240 languages) (default = en) lat,lng, radius (in km), maxRows 1453 * (default = 5) Example: 1454 * http://api.geonames.org/findNearbyWikipedia?lat=47&lng=9 1455 * 1456 * @param: latitude 1457 * @param: longitude 1458 * @param: radius 1459 * @param: language 1460 * @param: maxRows 1461 * @return: list of wikipedia articles 1462 * @throws: Exception 1463 */ 1464 public static List<WikipediaArticle> findNearbyWikipedia(double latitude, 1465 double longitude, double radius, String language, int maxRows) 1466 throws Exception { 1467 1468 List<WikipediaArticle> articles = new ArrayList<WikipediaArticle>(); 1469 1470 String url = "/findNearbyWikipedia?"; 1471 1472 url = url + "lat=" + latitude; 1473 url = url + "&lng=" + longitude; 1474 if (radius > 0) { 1475 url = url + "&radius=" + radius; 1476 } 1477 if (maxRows > 0) { 1478 url = url + "&maxRows=" + maxRows; 1479 } 1480 1481 if (language != null) { 1482 url = url + "&lang=" + language; 1483 } 1484 url = addUserName(url); 1485 1486 Element root = connectAndParse(url); 1487 for (Object obj : root.getChildren("entry")) { 1488 Element wikipediaArticleElement = (Element) obj; 1489 WikipediaArticle wikipediaArticle = getWikipediaArticleFromElement(wikipediaArticleElement); 1490 articles.add(wikipediaArticle); 1491 } 1492 1493 return articles; 1494 } 1495 1496 /** 1497 * GTOPO30 is a global digital elevation model (DEM) with a horizontal grid 1498 * spacing of 30 arc seconds (approximately 1 kilometer). GTOPO30 was 1499 * derived from several raster and vector sources of topographic 1500 * information. 1501 * 1502 * @param latitude 1503 * @param longitude 1504 * @return a single number giving the elevation in meters according to 1505 * gtopo30, ocean areas have been masked as "no data" and have been 1506 * assigned a value of -9999 1507 * @throws IOException 1508 * @throws GeoNamesException 1509 */ 1510 public static int gtopo30(double latitude, double longitude) 1511 throws IOException, GeoNamesException { 1512 String url = "/gtopo30?lat=" + latitude + "&lng=" + longitude; 1513 url = addUserName(url); 1514 BufferedReader in = new BufferedReader(new InputStreamReader( 1515 connect(url))); 1516 String gtopo30 = in.readLine(); 1517 in.close(); 1518 checkException(gtopo30); 1519 return Integer.parseInt(gtopo30); 1520 } 1521 1522 /** 1523 * Shuttle Radar Topography Mission (SRTM) elevation data. SRTM consisted of 1524 * a specially modified radar system that flew onboard the Space Shuttle 1525 * Endeavour during an 11-day mission in February of 2000. The dataset 1526 * covers land areas between 60 degrees north and 56 degrees south. This web 1527 * service is using SRTM3 data with data points located every 3-arc-second 1528 * (approximately 90 meters) on a latitude/longitude grid. 1529 * 1530 * @param latitude 1531 * @param longitude 1532 * @return elevation or -32768 if unknown 1533 * @throws IOException 1534 * @throws GeoNamesException 1535 */ 1536 public static int srtm3(double latitude, double longitude) 1537 throws IOException, GeoNamesException { 1538 String url = "/srtm3?lat=" + latitude + "&lng=" + longitude; 1539 url = addUserName(url); 1540 BufferedReader in = new BufferedReader(new InputStreamReader( 1541 connect(url))); 1542 String srtm3 = in.readLine(); 1543 in.close(); 1544 checkException(srtm3); 1545 return Integer.parseInt(srtm3); 1546 } 1547 1548 public static int[] srtm3(double[] latitude, double[] longitude) 1549 throws IOException { 1550 if (latitude.length != longitude.length) { 1551 throw new Error("number of lats and longs must be equal"); 1552 } 1553 int[] elevation = new int[latitude.length]; 1554 String lats = ""; 1555 String lngs = ""; 1556 for (int i = 0; i < elevation.length; i++) { 1557 lats += latitude[i] + ","; 1558 lngs += longitude[i] + ","; 1559 } 1560 String url = "/srtm3?lats=" + lats + "&lngs=" + lngs; 1561 url = addUserName(url); 1562 BufferedReader in = new BufferedReader(new InputStreamReader( 1563 connect(url))); 1564 for (int i = 0; i < elevation.length; i++) { 1565 String srtm3 = in.readLine(); 1566 elevation[i] = Integer.parseInt(srtm3); 1567 } 1568 in.close(); 1569 return elevation; 1570 } 1571 1572 public static int astergdem(double latitude, double longitude) 1573 throws IOException, GeoNamesException { 1574 String url = "/astergdem?lat=" + latitude + "&lng=" + longitude; 1575 url = addUserName(url); 1576 BufferedReader in = new BufferedReader(new InputStreamReader( 1577 connect(url))); 1578 String astergdem = in.readLine(); 1579 in.close(); 1580 checkException(astergdem); 1581 return Integer.parseInt(astergdem); 1582 } 1583 1584 public static int[] astergdem(double[] latitude, double[] longitude) 1585 throws IOException { 1586 if (latitude.length != longitude.length) { 1587 throw new Error("number of lats and longs must be equal"); 1588 } 1589 int[] elevation = new int[latitude.length]; 1590 String lats = ""; 1591 String lngs = ""; 1592 for (int i = 0; i < elevation.length; i++) { 1593 lats += latitude[i] + ","; 1594 lngs += longitude[i] + ","; 1595 } 1596 String url = "/astergdem?lats=" + lats + "&lngs=" + lngs; 1597 url = addUserName(url); 1598 BufferedReader in = new BufferedReader(new InputStreamReader( 1599 connect(url))); 1600 for (int i = 0; i < elevation.length; i++) { 1601 String astergdem = in.readLine(); 1602 elevation[i] = Integer.parseInt(astergdem); 1603 } 1604 in.close(); 1605 return elevation; 1606 } 1607 1608 /** 1609 * The iso country code of any given point. It is calling 1610 * {@link #countryCode(double, double, double)} with radius=0.0 1611 * 1612 * @param latitude 1613 * @param longitude 1614 * @return 1615 * @throws IOException 1616 * @throws GeoNamesException 1617 */ 1618 public static String countryCode(double latitude, double longitude) 1619 throws IOException, GeoNamesException { 1620 return countryCode(latitude, longitude, 0); 1621 } 1622 1623 /** 1624 * The iso country code of any given point with radius for coastal areas. 1625 * 1626 * @param latitude 1627 * @param longitude 1628 * @param radius 1629 * 1630 * @return iso country code for the given latitude/longitude 1631 * @throws IOException 1632 * @throws GeoNamesException 1633 */ 1634 public static String countryCode(double latitude, double longitude, 1635 double radius) throws IOException, GeoNamesException { 1636 String url = "/countryCode?lat=" + latitude + "&lng=" + longitude; 1637 if (radius != 0) { 1638 url += "&radius=" + radius; 1639 } 1640 url = addUserName(url); 1641 BufferedReader in = new BufferedReader(new InputStreamReader( 1642 connect(url))); 1643 String cc = in.readLine(); 1644 in.close(); 1645 if (cc != null && cc.length() == 2) { 1646 return cc; 1647 } 1648 if (cc == null || cc.length() == 0) { 1649 // nothing found return null 1650 return null; 1651 } 1652 // check whether we can parse an exception and throw it if we can 1653 checkException(cc); 1654 // something else was wrong, through generic exception 1655 throw new GeoNamesException("unhandled exception"); 1656 } 1657 1658 /** 1659 * get the timezone for a given location 1660 * 1661 * @param latitude 1662 * @param longitude 1663 * @return timezone at the given location 1664 * @throws IOException 1665 * @throws Exception 1666 */ 1667 public static Timezone timezone(double latitude, double longitude) 1668 throws IOException, Exception { 1669 1670 String url = "/timezone?"; 1671 double radius = 0; 1672 1673 url = url + "&lat=" + latitude; 1674 url = url + "&lng=" + longitude; 1675 if (radius > 0) { 1676 url = url + "&radius=" + radius; 1677 } 1678 url = addUserName(url); 1679 1680 Element root = connectAndParse(url); 1681 for (Object obj : root.getChildren("timezone")) { 1682 Element codeElement = (Element) obj; 1683 Timezone timezone = new Timezone(); 1684 timezone.setTimezoneId(codeElement.getChildText("timezoneId")); 1685 timezone.setCountryCode(codeElement.getChildText("countryCode")); 1686 1687 if (codeElement.getChildText("time") != null) { 1688 String minuteDateFmt = "yyyy-MM-dd HH:mm"; 1689 SimpleDateFormat df = null; 1690 if (codeElement.getChildText("time").length() == minuteDateFmt 1691 .length()) { 1692 df = new SimpleDateFormat(minuteDateFmt); 1693 } else { 1694 df = new SimpleDateFormat(DATEFMT); 1695 } 1696 timezone.setTime(df.parse(codeElement.getChildText("time"))); 1697 if (codeElement.getChildText("sunrise") != null) { 1698 timezone.setSunrise(df.parse(codeElement 1699 .getChildText("sunrise"))); 1700 } 1701 if (codeElement.getChildText("sunset") != null) { 1702 timezone.setSunset(df.parse(codeElement 1703 .getChildText("sunset"))); 1704 } 1705 timezone.setGmtOffset(Double.parseDouble(codeElement 1706 .getChildText("gmtOffset"))); 1707 timezone.setDstOffset(Double.parseDouble(codeElement 1708 .getChildText("dstOffset"))); 1709 } 1710 return timezone; 1711 } 1712 1713 return null; 1714 } 1715 1716 //FIXME implement and test 1717 public static String ocean(double latitude, double longitude) 1718 throws IOException, Exception { 1719 1720 String url = "/ocean?"; 1721 double radius = 0; 1722 1723 url = url + "&lat=" + latitude; 1724 url = url + "&lng=" + longitude; 1725 if (radius > 0) { 1726 url = url + "&radius=" + radius; 1727 } 1728 url = addUserName(url); 1729 1730 Element root = connectAndParse(url); 1731 for (Object obj : root.getChildren("ocean")) { 1732 Element oceanElement = (Element) obj; 1733 if (oceanElement != null) { 1734 return oceanElement.getChildText("name"); 1735 } 1736 } 1737 1738 return null; 1739 } 1740 1741 /** 1742 * 1743 * @param latitude 1744 * @param longitude 1745 * @return 1746 * @throws IOException 1747 * @throws Exception 1748 */ 1749 public static WeatherObservation findNearByWeather(double latitude, 1750 double longitude) throws IOException, Exception { 1751 1752 String url = "/findNearByWeatherXML?"; 1753 1754 url = url + "&lat=" + latitude; 1755 url = url + "&lng=" + longitude; 1756 url = addUserName(url); 1757 1758 Element root = connectAndParse(url); 1759 for (Object obj : root.getChildren("observation")) { 1760 Element weatherObservationElement = (Element) obj; 1761 WeatherObservation weatherObservation = getWeatherObservationFromElement(weatherObservationElement); 1762 return weatherObservation; 1763 } 1764 1765 return null; 1766 } 1767 1768 public static WeatherObservation weatherIcao(String icaoCode) 1769 throws IOException, Exception { 1770 1771 String url = "/weatherIcaoXML?"; 1772 1773 url = url + "&ICAO=" + icaoCode; 1774 url = addUserName(url); 1775 1776 Element root = connectAndParse(url); 1777 for (Object obj : root.getChildren("observation")) { 1778 Element weatherObservationElement = (Element) obj; 1779 WeatherObservation weatherObservation = getWeatherObservationFromElement(weatherObservationElement); 1780 return weatherObservation; 1781 } 1782 1783 return null; 1784 } 1785 1786 /** 1787 * @return the geoNamesServer, default is http://api.geonames.org 1788 */ 1789 public static String getGeoNamesServer() { 1790 return geoNamesServer; 1791 } 1792 1793 /** 1794 * @return the geoNamesServerFailover 1795 */ 1796 public static String getGeoNamesServerFailover() { 1797 return geoNamesServerFailover; 1798 } 1799 1800 /** 1801 * sets the server name for the GeoNames server to be used for the requests. 1802 * Default is api.geonames.org 1803 * 1804 * @param geoNamesServer 1805 * the geonamesServer to set 1806 */ 1807 public static void setGeoNamesServer(String pGeoNamesServer) { 1808 if (pGeoNamesServer == null) { 1809 throw new Error(); 1810 } 1811 pGeoNamesServer = pGeoNamesServer.trim().toLowerCase(); 1812 // add default http protocol if it is missing 1813 if (!pGeoNamesServer.startsWith("http://") 1814 && !pGeoNamesServer.startsWith("https://")) { 1815 pGeoNamesServer = "http://" + pGeoNamesServer; 1816 } 1817 WebService.geoNamesServer = pGeoNamesServer; 1818 } 1819 1820 /** 1821 * sets the default failover server for requests in case the main server is 1822 * not accessible. Default is api.geonames.org<br> 1823 * The failover server is only called if it is different from the main 1824 * server.<br> 1825 * The failover server is used for commercial GeoNames web service users. 1826 * 1827 * @param geoNamesServerFailover 1828 * the geoNamesServerFailover to set 1829 */ 1830 public static void setGeoNamesServerFailover(String geoNamesServerFailover) { 1831 if (geoNamesServerFailover != null) { 1832 geoNamesServerFailover = geoNamesServerFailover.trim() 1833 .toLowerCase(); 1834 if (!geoNamesServerFailover.startsWith("http://") 1835 && !geoNamesServerFailover.startsWith("https://")) { 1836 geoNamesServerFailover = "http://" + geoNamesServerFailover; 1837 } 1838 } 1839 WebService.geoNamesServerFailover = geoNamesServerFailover; 1840 } 1841 1842 /** 1843 * @return the proxy 1844 */ 1845 public static Proxy getProxy() { 1846 return proxy; 1847 } 1848 1849 /** 1850 * @param proxy 1851 * the proxy to set 1852 * 1853 * If you are behind a proxy and cannot change the java system 1854 * properties, you can use this method to set a proxy. You define 1855 * it like this: 1856 * 1857 * <pre> 1858 * <code> 1859 * java.net.SocketAddress sa = new java.net.InetSocketAddress("myproxyserver", 8080); 1860 * java.net.Proxy proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, sa); 1861 * </code> 1862 * </pre> 1863 */ 1864 public static void setProxy(Proxy proxy) { 1865 WebService.proxy = proxy; 1866 } 1867 1868 /** 1869 * @return the userName 1870 */ 1871 public static String getUserName() { 1872 return userName; 1873 } 1874 1875 /** 1876 * Sets the user name to be used for the requests. Needed to access the 1877 * commercial GeoNames web services. 1878 * 1879 * @param userName 1880 * the userName to set 1881 */ 1882 public static void setUserName(String userName) { 1883 WebService.userName = userName; 1884 } 1885 1886 /** 1887 * @return the token 1888 */ 1889 public static String getToken() { 1890 return token; 1891 } 1892 1893 /** 1894 * sets the token to be used to authenticate the requests. This is an 1895 * optional parameter for the commercial version of the GeoNames web 1896 * services. 1897 * 1898 * @param token 1899 * the token to set 1900 */ 1901 public static void setToken(String token) { 1902 WebService.token = token; 1903 } 1904 1905 /** 1906 * @return the defaultStyle 1907 */ 1908 public static Style getDefaultStyle() { 1909 return defaultStyle; 1910 } 1911 1912 /** 1913 * @param defaultStyle 1914 * the defaultStyle to set 1915 */ 1916 public static void setDefaultStyle(Style defaultStyle) { 1917 WebService.defaultStyle = defaultStyle; 1918 } 1919 1920 /** 1921 * @return the readTimeOut 1922 */ 1923 public static int getReadTimeOut() { 1924 return readTimeOut; 1925 } 1926 1927 /** 1928 * @param readTimeOut 1929 * the readTimeOut to set 1930 */ 1931 public static void setReadTimeOut(int readTimeOut) { 1932 WebService.readTimeOut = readTimeOut; 1933 } 1934 1935 /** 1936 * @return the connectTimeOut 1937 */ 1938 public static int getConnectTimeOut() { 1939 return connectTimeOut; 1940 } 1941 1942 /** 1943 * @param connectTimeOut 1944 * the connectTimeOut to set 1945 */ 1946 public static void setConnectTimeOut(int connectTimeOut) { 1947 WebService.connectTimeOut = connectTimeOut; 1948 } 1949 1950}