package com.revolsys.swing.map.layer.geonames;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.measure.unit.SI;
import com.revolsys.datatype.DataTypes;
import com.revolsys.geometry.cs.GeographicCoordinateSystem;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.impl.PointDoubleXY;
import com.revolsys.geometry.model.impl.PointDoubleXYZ;
import com.revolsys.io.PathName;
import com.revolsys.record.Record;
import com.revolsys.record.io.format.json.JsonParser;
import com.revolsys.record.schema.RecordDefinition;
import com.revolsys.record.schema.RecordDefinitionImpl;
import com.revolsys.util.UrlUtil;
public class GeoNamesService {
public static final RecordDefinition NAME_RECORD_DEFINITION;
public static final RecordDefinition WIKIPEDIA_RECORD_DEFINITION;
static {
final RecordDefinitionImpl recordDefinition = new RecordDefinitionImpl(
PathName.newPathName("/geoname.org/name"));
recordDefinition.addField("geonameId", DataTypes.STRING, false);
recordDefinition.addField("name", DataTypes.STRING, false);
recordDefinition.addField("fcode", DataTypes.STRING, false);
recordDefinition.addField("fcodeName", DataTypes.STRING, false);
recordDefinition.addField("fcl", DataTypes.STRING, false);
recordDefinition.addField("fclName", DataTypes.STRING, false);
recordDefinition.addField("adminName1", DataTypes.STRING, false);
recordDefinition.addField("adminName2", DataTypes.STRING, false);
recordDefinition.addField("adminName3", DataTypes.STRING, false);
recordDefinition.addField("adminName4", DataTypes.STRING, false);
recordDefinition.addField("adminCode1", DataTypes.STRING, false);
recordDefinition.addField("population", DataTypes.INTEGER, false);
recordDefinition.addField("countryCode", DataTypes.STRING, false);
recordDefinition.addField("countryName", DataTypes.STRING, false);
recordDefinition.addField("timeZoneId", DataTypes.STRING, false);
recordDefinition.addField("geometry", DataTypes.POINT, false);
NAME_RECORD_DEFINITION = recordDefinition;
final RecordDefinitionImpl wikipediaRecordDefinition = new RecordDefinitionImpl(
PathName.newPathName("/geoname.org/wikipedia"));
wikipediaRecordDefinition.addField("summary", DataTypes.STRING, false);
wikipediaRecordDefinition.addField("title", DataTypes.STRING, false);
wikipediaRecordDefinition.addField("wikipediaUrl", DataTypes.STRING, false);
wikipediaRecordDefinition.addField("countryCode", DataTypes.STRING, false);
wikipediaRecordDefinition.addField("feature", DataTypes.STRING, false);
wikipediaRecordDefinition.addField("thumbnailImg", DataTypes.STRING, false);
wikipediaRecordDefinition.addField("lang", DataTypes.STRING, false);
wikipediaRecordDefinition.addField("population", DataTypes.INTEGER, false);
wikipediaRecordDefinition.addField("geometry", DataTypes.POINT, false);
WIKIPEDIA_RECORD_DEFINITION = wikipediaRecordDefinition;
}
private URL findNearbyBoundingBoxJsonUrl;
private URL searchJsonUrl;
private URL wikipediaBoundingBoxJsonUrl;
public GeoNamesService() {
final String url = "Http://ws.geonames.org";
try {
init(new URL(url));
} catch (final MalformedURLException e) {
throw new IllegalArgumentException("Invalid URL:" + url);
}
}
public GeoNamesService(final URL url) {
init(url);
}
public List<Record> getNames(final BoundingBox boundingBox) {
final GeometryFactory geometryFactory = GeometryFactory.floating3(4326);
final GeographicCoordinateSystem cs = (GeographicCoordinateSystem)geometryFactory
.getCoordinateSystem();
final BoundingBox geographicBoundingBox = boundingBox.convert(geometryFactory);
final Map<String, Object> params = new HashMap<>();
final double radius = cs.getDatum().getSpheroid().getSemiMajorAxis();
final double height = geographicBoundingBox.getHeight();
final double width = geographicBoundingBox.getWidth();
final double diagonal = Math.sqrt(width * width + height * height);
final double radiusKm = cs.getUnit().getConverterTo(SI.RADIAN).convert(diagonal) * radius
/ 1000;
params.put("lat", geographicBoundingBox.getCentreY());
params.put("lng", geographicBoundingBox.getCentreY());
params.put("radius", radiusKm);
params.put("maxRows", "50");
final String searchUrlString = UrlUtil.getUrl(this.findNearbyBoundingBoxJsonUrl, params);
try {
final URL searchUrl = new URL(searchUrlString);
final Map<String, Object> result = JsonParser.getMap(searchUrl.openStream());
return mapToObjects(NAME_RECORD_DEFINITION, result);
} catch (final IOException e) {
throw new IllegalArgumentException("Unable to connect to URL:" + searchUrlString);
}
}
public List<Record> getWikipediaArticles(final BoundingBox boundingBox) {
final BoundingBox geographicBoundingBox = boundingBox.convert(GeometryFactory.floating3(4326));
final Map<String, Object> params = new HashMap<>();
params.put("north", geographicBoundingBox.getMaxY());
params.put("east", geographicBoundingBox.getMaxX());
params.put("south", geographicBoundingBox.getMinY());
params.put("west", geographicBoundingBox.getMinX());
params.put("maxRows", "50");
final String searchUrlString = UrlUtil.getUrl(this.wikipediaBoundingBoxJsonUrl, params);
try {
final URL searchUrl = new URL(searchUrlString);
final Map<String, Object> result = JsonParser.getMap(searchUrl.openStream());
return mapToObjects(WIKIPEDIA_RECORD_DEFINITION, result);
} catch (final IOException e) {
throw new IllegalArgumentException("Unable to connect to URL:" + searchUrlString);
}
}
private void init(final URL url) {
try {
this.searchJsonUrl = new URL(url.toString() + "/searchJSON");
this.wikipediaBoundingBoxJsonUrl = new URL(url.toString() + "/wikipediaBoundingBoxJSON");
this.findNearbyBoundingBoxJsonUrl = new URL(url.toString() + "/findNearbyJSON");
} catch (final MalformedURLException e) {
throw new IllegalArgumentException("Invalid URL:" + url);
}
}
private List<Record> mapToObjects(final RecordDefinition recordDefinition,
final Map<String, Object> result) {
final List<Record> results = new ArrayList<>();
final List<Map<String, Object>> names = (List<Map<String, Object>>)result.get("geonames");
for (final Map<String, Object> name : names) {
final Record record = recordDefinition.newRecord();
for (final String fieldName : recordDefinition.getFieldNames()) {
final Object value = name.get(fieldName);
if (value != null) {
record.setValue(fieldName, value);
}
}
final double lat = ((Number)name.get("lat")).doubleValue();
final double lon = ((Number)name.get("lng")).doubleValue();
Point coordinate = new PointDoubleXY(lon, lat);
final Number elevation = (Number)name.get("elevation");
if (elevation == null) {
coordinate = new PointDoubleXY(lon, lat);
} else {
coordinate = new PointDoubleXYZ(lon, lat, elevation.doubleValue());
}
record.setGeometryValue(GeometryFactory.DEFAULT_3D.point(coordinate));
results.add(record);
}
return results;
}
public List<Record> searchByName(final String name) {
return searchByName(name, null);
}
public List<Record> searchByName(final String name, final String countryCode) {
final Map<String, String> params = new HashMap<>();
params.put("name", name);
params.put("style", "FULL");
params.put("type", "json");
final String searchUrlString = UrlUtil.getUrl(this.searchJsonUrl, params);
try {
final URL searchUrl = new URL(searchUrlString);
final Map<String, Object> result = JsonParser.getMap(searchUrl.openStream());
return mapToObjects(NAME_RECORD_DEFINITION, result);
} catch (final IOException e) {
throw new IllegalArgumentException("Unable to connect to URL:" + searchUrlString);
}
}
}