From a set of coordinates that define a route, I want to draw a Geometry that mimicks a theoretical highway of that track, given an arbitrary number of meters wide (e.g. 20)
I don't know if GeoTools provides tools for constructing a Geometry with such inputs, so my initial idea is to split the track coordinates (several thousands) in pairs (coord0, coord1), (coord1, coord2), ...., (coordN, coordN-1) and, with each pair, build a rectangle assuming that the two points are the midpoints of a 20m wide segment (as in Knowing two points of a rectangle, how can I figure out the other two?), and joining all the resulting geometries.
Maybe it's overkill but I haven't found a cheaper way to do this
Any ideas would be greatly appreciated!
The easy way to do this is to use a 20m buffer around a line created from your points. So some code like this to create a line from the points (:
String[] wkt = {
"Point (-0.13666168754467312 50.81919869153657743)",
"Point (-0.13622277073931291 50.82205165077141373)",
"Point (-0.13545466632993253 50.82512406840893959)",
"Point (-0.13457683271921211 50.82687973563037787)",
"Point (-0.13413791591385191 50.82907431965718104)",
"Point (-0.13951464677951447 50.8294035072611976)",
"Point (-0.14346489802775639 50.83082998687861931)",
"Point (-0.14697623247063807 50.83072025767727808)",
"Point (-0.15004865010815954 50.83390240451614517)",
"Point (-0.15740050659794308 50.8349996965295432)",
"Point (-0.16486209228906662 50.83741373895902171)",
"Point (-0.17276259478555042 50.83894994777778464)",
"Point (-0.18549118214099652 50.8387304893751022)"
};
//build line
WKTReader2 reader = new WKTReader2();
GeometryFactory gf = new GeometryFactory();
Coordinate[] points = new Coordinate[wkt.length];
int i=0;
for(String w:wkt) {
Point p;
try {
p = (Point) reader.read(w);
points[i++]=p.getCoordinate();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
LineString line = gf.createLineString(points);
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(schema);
builder.set("locations", line);
SimpleFeature feature = builder.buildFeature("1");
And then a BufferLine method like:
public SimpleFeature bufferFeature(SimpleFeature feature, Measure<Double, Length> distance) {
// extract the geometry
GeometryAttribute gProp = feature.getDefaultGeometryProperty();
CoordinateReferenceSystem origCRS = gProp.getDescriptor().getCoordinateReferenceSystem();
Geometry geom = (Geometry) feature.getDefaultGeometry();
Geometry pGeom = geom;
MathTransform toTransform, fromTransform = null;
// reproject the geometry to a local projection
if (!(origCRS instanceof ProjectedCRS)) {
Point c = geom.getCentroid();
double x = c.getCoordinate().x;
double y = c.getCoordinate().y;
String code = "AUTO:42001," + x + "," + y;
// System.out.println(code);
CoordinateReferenceSystem auto;
try {
auto = CRS.decode(code);
toTransform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto);
fromTransform = CRS.findMathTransform(auto, DefaultGeographicCRS.WGS84);
pGeom = JTS.transform(geom, toTransform);
} catch (MismatchedDimensionException | TransformException | FactoryException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// buffer
Geometry out = buffer(pGeom, distance.doubleValue(SI.METER));
Geometry retGeom = out;
// reproject the geometry to the original projection
if (!(origCRS instanceof ProjectedCRS)) {
try {
retGeom = JTS.transform(out, fromTransform);
} catch (MismatchedDimensionException | TransformException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// return a new feature containing the geom
SimpleFeatureType schema = feature.getFeatureType();
SimpleFeatureTypeBuilder ftBuilder = new SimpleFeatureTypeBuilder();
ftBuilder.setCRS(origCRS);
// ftBuilder.setDefaultGeometry("buffer");
ftBuilder.addAll(schema.getAttributeDescriptors());
ftBuilder.setName(schema.getName());
SimpleFeatureType nSchema = ftBuilder.buildFeatureType();
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(nSchema);
List<Object> atts = feature.getAttributes();
for (int i = 0; i < atts.size(); i++) {
if (atts.get(i) instanceof Geometry) {
atts.set(i, retGeom);
}
}
SimpleFeature nFeature = builder.buildFeature(null, atts.toArray());
return nFeature;
}
/**
* create a buffer around the geometry, assumes the geometry is in the same
* units as the distance variable.
*
* #param geom
* a projected geometry.
* #param dist
* a distance for the buffer in the same units as the projection.
* #return
*/
private Geometry buffer(Geometry geom, double dist) {
Geometry buffer = geom.buffer(dist);
return buffer;
}
The tricky part is reprojecting into a locally flat CRS so that you can use metres for the buffer size. If you know of a locally good projection you could just use that (in this case we could have used OSGB (EPSG:27700) for better results).
This gives the following map:
Related
I'm using android studio to create a heat map on google maps. I have a database consisting of the following information:
longitude latitude Electricity Energy Consumption
1 -77.08527 38.7347905 4.742112594
2 -19.03592 34.8081915 4.742112594
3 -74.04591 12.8815925 5.278542493
4 -32.05547 25.9549935 12.270006486
5 -49.06596 76.0283945 4.742112594
6 -63.08492 20.1017955 4.742112594
Is there any way to take these coordinates and magnitude and plot a density map using Google Maps?
I've done a bit of research, and the google api does allow the creation of the heatmap but it only allows a dataset containing coordiantes. How would I reflect the energy consumption in certain areas?
This is the link to the site that tells you how to create a heatmap: https://developers.google.com/maps/documentation/android-api/utility/heatmap
I just need a push in the right direction to be able to implement this.
There is the following method that I thought I could use but I didnt quite understand it and was hoping maybe someone could explain how to use it, and if its possible to use this to implement my particular scenario:
This is the code from the site to implement the heatmap which only take into account the coordinates:
List<LatLng> list = null;
// Get the data: latitude/longitude positions of police stations.
try {
list = readItems(R.raw.police_stations);
} catch (JSONException e) {
Toast.makeText(this, "Problem reading list of locations.", Toast.LENGTH_LONG).show();
}
// Create a heat map tile provider, passing it the latlngs of the police stations.
mProvider = new HeatmapTileProvider.Builder()
.data(list)
.build();
// Add a tile overlay to the map, using the heat map tile provider.
mOverlay = mMap.addTileOverlay(new TileOverlayOptions().tileProvider(mProvider));
}
private ArrayList<LatLng> readItems(int resource) throws JSONException {
ArrayList<LatLng> list = new ArrayList<LatLng>();
InputStream inputStream = getResources().openRawResource(resource);
String json = new Scanner(inputStream).useDelimiter("\\A").next();
JSONArray array = new JSONArray(json);
for (int i = 0; i < array.length(); i++) {
JSONObject object = array.getJSONObject(i);
double lat = object.getDouble("lat");
double lng = object.getDouble("lng");
list.add(new LatLng(lat, lng));
}
return list;
}
This is the code from the site to change the dataset:
ArrayList<WeightedLatLng> data = new ArrayList<WeightedLatLng>();
mProvider.setData(data);
mOverlay.clearTileCache();
Instead of LatLng, you can use WeightedLatLng.
You code should look like:
List<WeightedLatLng> list = null;
// Get the data: latitude/longitude positions of police stations.
try {
list = readItems(R.raw.police_stations);
} catch (JSONException e) {
Toast.makeText(this, "Problem reading list of locations.", Toast.LENGTH_LONG).show();
}
// Create a heat map tile provider, passing it the latlngs of the police stations.
mProvider = new HeatmapTileProvider.Builder()
.weightedData(list)
.build();
// Add a tile overlay to the map, using the heat map tile provider.
mOverlay = mMap.addTileOverlay(new
TileOverlayOptions().tileProvider(mProvider));
}
private List<WeightedLatLng> readItems(int resource) throws JSONException {
List<WeightedLatLng> list = new ArrayList<WeightedLatLng>();
InputStream inputStream = getResources().openRawResource(resource);
String json = new Scanner(inputStream).useDelimiter("\\A").next();
JSONArray array = new JSONArray(json);
for (int i = 0; i < array.length(); i++) {
JSONObject object = array.getJSONObject(i);
double lat = object.getDouble("lat");
double lng = object.getDouble("lng");
double magnitude = object.getDouble("mag"
list.add(new WeightedLatLng(lat, lng, magnitude));
}
return list;
}
I need to create a GeoJSON stream, which contains data about roads. The roads are like polygons, except they are open (the start and end points are different).
I tried to use the following code to achieve that goal:
protected void addPolyLine(final double[] coords,
final GeometryBuilder builder,
final SimpleFeatureBuilder fbuilder,
final List<SimpleFeature> features,
final String id) {
final double[] modcoords = new double[coords.length+2];
for (int i=0; i < coords.length; i++) {
modcoords[i] = coords[i];
}
modcoords[modcoords.length-2] = coords[0];
modcoords[modcoords.length-1] = coords[1];
final double[] holeStart = new double[] {coords[0], coords[1],
coords[coords.length-2], coords[coords.length - 1]};
final LinearRing shell = builder.linearRing(modcoords);
final LinearRing hole = builder.linearRing(holeStart);
final Polygon polygon = builder.polygon(shell, hole);
fbuilder.add(polygon);
final SimpleFeature feature = fbuilder.buildFeature(id);
features.add(feature);
}
It doesn't work - I get the error
java.lang.IllegalArgumentException: Invalid number of points in LinearRing (found 3 - must be 0 or >= 4)
at com.vividsolutions.jts.geom.LinearRing.validateConstruction(LinearRing.java:114)
at com.vividsolutions.jts.geom.LinearRing.<init>(LinearRing.java:106)
at com.vividsolutions.jts.geom.GeometryFactory.createLinearRing(GeometryFactory.java:341)
at org.geotools.geometry.jts.GeometryBuilder.linearRing(GeometryBuilder.java:199)
When I use builder.lineString(coords), the resulting GeoJSON contains no coordinates as it should.
How can I create a polyline (a line, which connects several point and is not closed) using GeoTools 12-RC1?
Update 1 (05.07.2015 21:22 MSK): This is how I define feature types for points, polygons and lines. Points and polygons work fine, lines don't.
private final static SimpleFeatureType POINT =
createType("Test", "Location:Point");
private final static SimpleFeatureType POLYGON =
createType("Test2", "Location:Polygon");
private final BuildingsReader buildingsReader =
new DefaultBuildingsReader();
private final static SimpleFeatureType LINE =
createType("Test3", "Line");
private static SimpleFeatureType createType(final String x1, final String x2) {
try
{
return DataUtilities.createType(x1, x2);
}
catch (final SchemaException exception) {
exception.printStackTrace();
}
return null;
}
You need to do something with createLineString like:
line=builder.createLineString(Arrays.asList(coords));
fbuilder.add(line);
final SimpleFeature feature = fbuilder.buildFeature(id);
features.add(feature);
You will need to modify your feature type to expect a line instead of a polygon.
I have buildings' information stored in citygml file. I am trying to extract the building's polygon geometry using the citygml4j library. I have looked at the FeatureWalker class, but I am unable to get the polygon geometry.
How do I go about doing this? Here is my code:
CityGMLContext ctx = new CityGMLContext();
CityGMLBuilder builder = ctx.createCityGMLBuilder();
CityGMLInputFactory in = builder.createCityGMLInputFactory();
CityGMLReader reader = in.createCityGMLReader(new File("/home/vishal/NWW/sampleData/LOD2_Building_v100.gml"));
while(reader.hasNext())
{
CityGML citygml = reader.nextFeature();
System.out.println("Found class:" + citygml.getCityGMLClass() + "\nVersion"+citygml.getCityGMLModule().getVersion());
//Counting the no of buildings
CityModel citymodel = new CityModel();
if(citygml.getCityGMLClass() == CityGMLClass.CITY_MODEL)
{
citymodel = (CityModel)citygml;
// Counting the no of buildings
int count=0;
for(CityObjectMember cityObjectMember : citymodel.getCityObjectMember())
{
AbstractCityObject cityobject = cityObjectMember.getCityObject();
if(cityobject.getCityGMLClass() == CityGMLClass.BUILDING)
{
++count;
}
}
System.out.println("Building count"+count);
}
FeatureWalker walker = new FeatureWalker(){
public void visit(Building building){
System.out.println(building.getId());
//MultiSurface multisurface = boundrysurface.getLod2MultiSurface().getMultiSurface();
//System.out.println(multisurface.getSurfaceMember().get(0));
List<BoundarySurfaceProperty> list = building.getBoundedBySurface();
System.out.println(list);
System.out.println(list.get(0).getBoundarySurface());
//HOW TO GET THE POLYGON AND ITS COORDINATES??
}
};
citymodel.accept(walker);
PS: If you have any other resources/tutorials on citygml4j library, kindly let me know.
Thanks,
You can search for the AbstractBoundarySurfaces directly, like this:
FeatureWalker walker = new FeatureWalker() {
#Override
public void visit(AbstractBoundarySurface boundarySurface) {
MultiSurfaceProperty lod2MultiSurface = boundarySurface.getLod2MultiSurface();
if (lod2MultiSurface != null) {
MultiSurface multiSurface = lod2MultiSurface.getMultiSurface();
if (multiSurface == null) {
// Do something!
}
List<SurfaceProperty> surfaceMember = multiSurface.getSurfaceMember();
for (SurfaceProperty surfaceProperty : surfaceMember) {
AbstractSurface abstractSurface = surfaceProperty.getObject();
if (abstractSurface instanceof Polygon) {
Polygon polygon = (Polygon) abstractSurface;
// Do something with polygon!
}
// Check for other subtypes of AbstractSurface
}
}
// Process LOD3 and LOD4
super.visit(boundarySurface);
}
};
building.accept(walker);
Then you can traverse down the tree and look for the polygons.
I am very new to GeoTools. I would like to create a hex grid and save it to a SHP file. But something goes wrong along the way (the saved SHP file is empty). In the debug mode I found that the gird is correctly created and contains a bunch of polygons that make sense. Writing those to a shape file proves to be difficult. I followed the tutorial on GeoTools' website, but that does not quite do it yet. I suspect TYPE to be incorrectly defined, but could not find out how to define it correctly.
Any help of how to store the grid into a SHP file is highly appreciated.
ReferencedEnvelope gridBounds = new ReferencedEnvelope(xMin, xMax, yMin, yMax, DefaultGeographicCRS.WGS84);
// length of each hexagon edge
double sideLen = 0.5;
// max distance between vertices
double vertexSpacing = sideLen / 20;
SimpleFeatureSource grid = Grids.createHexagonalGrid(gridBounds, sideLen, vertexSpacing);
/*
* We use the DataUtilities class to create a FeatureType that will describe the data in our
* shapefile.
*
* See also the createFeatureType method below for another, more flexible approach.
*/
final SimpleFeatureType TYPE = createFeatureType();
/*
* Get an output file name and create the new shapefile
*/
File newFile = new File("D:/test/shape.shp");
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<String, Serializable>();
params.put("url", newFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
newDataStore.createSchema(TYPE);
/*
* You can comment out this line if you are using the createFeatureType method (at end of
* class file) rather than DataUtilities.createType
*/
newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);
/*
* Write the features to the shapefile
*/
Transaction transaction = new DefaultTransaction("create");
String typeName = newDataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);
if (featureSource instanceof SimpleFeatureStore) {
SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
featureStore.setTransaction(transaction);
try {
featureStore.addFeatures(grid.getFeatures());
transaction.commit();
} catch (Exception problem) {
problem.printStackTrace();
transaction.rollback();
} finally {
transaction.close();
}
} else {
System.out.println(typeName + " does not support read/write access");
}
private static SimpleFeatureType createFeatureType() {
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setName("Location");
builder.setCRS(DefaultGeographicCRS.WGS84); // <- Coordinate reference system
// add attributes in order
builder.add("Polygon", Polygon.class);
builder.length(15).add("Name", String.class); // <- 15 chars width for name field
// build the type
final SimpleFeatureType LOCATION = builder.buildFeatureType();
return LOCATION;
}
I am exporting a set of points to KML using the GeoTools KML encoder.
It works fine, but my points are expressed as lat, lon, alt, and they are exported as such, but Google Earth presents them clamped to the surface.
I read here that I need to set the altitudeMode attribute. How would I do that with the GeoTools encoder?
Here's my code:
/**
* Converts the given point array to KML.
* #param points The array of points to be converted.
* #param os The target output stream where the resulting KML is to be
* written. This method does not close the stream.
*/
public static void toKml(Point[] points, OutputStream os) {
SimpleFeatureTypeBuilder sftb = new SimpleFeatureTypeBuilder();
sftb.setName("points");
sftb.add("geomtery", Point.class, DefaultGeographicCRS.WGS84_3D);
SimpleFeatureType type = sftb.buildFeatureType();
DefaultFeatureCollection features = new DefaultFeatureCollection();
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type);
for (int i = 0; i < points.length; i++) {
builder.add(points[i]);
features.add(builder.buildFeature(Integer.toString(i)));
}
Encoder encoder = new Encoder(new KMLConfiguration());
encoder.setIndenting(true);
try {
encoder.encode(features, KML.kml, os);
} catch (IOException e) {
throw new RuntimeException("While converting " +
Arrays.toString(points) + " to KML.", e);
}
}