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