I'm new to the GeoTools library for Java and I'm simply trying to draw a polygon on a map. I'm using GPS coordinates for points, which draw just fine, but I just can't figure out how to draw the LineString between them to save my life.
I have checked all the tutorials on geotools.org and also this posting but no avail.
Should this be so complicated? Can anybody maybe post the code fragments required to draw a LineString? This is what I've tried last:
SimpleFeatureType lineType = DataUtilities.createType("LINE", "geom:LineString,name:String");
SimpleFeatureBuilder featureBuilderLines = new SimpleFeatureBuilder(lineType);
SimpleFeatureCollection collectionLines = FeatureCollections.newCollection();
LineString line = builder.createLineString(listOfPoints);
featureBuilderLines.add(line);
SimpleFeature featureLine = featureBuilderLines.buildFeature(null);
((DefaultFeatureCollection)collectionLines).add(featureLine);
Style lineStyle = SLD.createLineStyle(Color.RED, 2.0f);
map.addLayer(new FeatureLayer(collectionLines, lineStyle));
Thanks and advance and best regards
You seem to be mixing Geometry types, try something like:
import org.geotools.geometry.jts.JTSFactoryFinder;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.LineString;
public class TestLineBuilder {
public static void main(String[] args) {
com.vividsolutions.jts.geom.GeometryFactory gFac = JTSFactoryFinder.getGeometryFactory();
Coordinate[] coordinates = new Coordinate[2];
coordinates[0] = new Coordinate(1,3);
coordinates[1] = new Coordinate(3,8);
LineString line =gFac.createLineString(coordinates );
System.out.println(line);
}
}
which gives the right sort of answer for me.
Have been fighting with this. Finally got it to work for saving the map to an image (png) using a mixture of different snippets from the web. Showing the map via JMapFrame.showMap(map); leads to an Exception and crash. Anyway, I needed the image. The example for a line with two points is shown below. Adding a polyline with more points should be identical:
SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder();
b.setName( "LineFeature" );
//add a geometry property
b.setCRS( DefaultGeographicCRS.WGS84 ); // set crs first
b.add( "line", LineString.class ); // then add geometry
//build the type
final SimpleFeatureType TYPE = b.buildFeatureType();
SimpleFeatureBuilder featureBuilderLines = new SimpleFeatureBuilder(TYPE);
SimpleFeatureCollection collectionLines = new DefaultFeatureCollection("internal",TYPE);
GeometryFactory gFac = JTSFactoryFinder.getGeometryFactory(JTSFactoryFinder.EMPTY_HINTS);
Coordinate[] coordinates = new Coordinate[2];
double latStart = 44.9;
double lonStart = 14.9;
double latEnd = 12.1;
double lonEnd = 9.4;
coordinates[0] = new Coordinate(lonStart, latStart);
coordinates[1] = new Coordinate(lonEnd, latEnd);
LineString line = gFac.createLineString(coordinates );
featureBuilderLines.add(line);
SimpleFeature featureLine = featureBuilderLines.buildFeature(null);
collectionLines.add(featureLine);
float lineWidt = 2.0f;
Style lineStyle = SLD.createLineStyle(Color.red, lineWidth);
SimpleFeatureSource collectionFeatureSource = new CollectionFeatureSource(collectionLines);
map.addLayer(collectionFeatureSource, lineStyle);
Related
Am using hellochart library
https://github.com/lecho/hellocharts-android to show a linechart in my app.
When i try to generate the graph with a single value, it is just showing an empty graph (It is supposed to show a single dot in the graph indicating the value right?).
The problem also persists if the first and second y axis value is same (When there is just 2 values).
Any ideas why this weird problem occurs?
The code am using is given below
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<String> datelist = new ArrayList<String>();
List<Float> weightlist = new ArrayList<Float>();
datelist.add("12/Apr/2020");
weightlist.add(72f);
datelist.add("13/Apr/2020");
weightlist.add(72f);
LineChartView chart = findViewById(R.id.chart);
List<AxisValue> axisValues = new ArrayList<AxisValue>();
List<PointValue> values = new ArrayList<PointValue>();
for (int i = 0; i < datelist.size(); ++i) {
values.add(new PointValue(i, weightlist.get(i)));
axisValues.add(new AxisValue(i).setLabel(datelist.get(i)));
}
Line line = new Line(values).setColor(Color.BLUE).setCubic(false);
line.setHasLabels(true);
List<Line> lines = new ArrayList<Line>();
lines.add(line);
LineChartData data = new LineChartData();
data.setLines(lines);
data.setAxisXBottom(new Axis(axisValues));
data.setAxisYLeft(new Axis());
chart.setLineChartData(data);
}
}
This is the the solution i finally found, To set the viewport manually.
float ymax= maxyvalue +10f;
final Viewport v = new Viewport(chart.getMaximumViewport());
v.top =ymax; //max value
v.bottom = 0f; //min value
chart.setMaximumViewport(v);
chart.setCurrentViewport(v);
Hope it will help somebody in future.
I want to plot a line which connects 2 points on a map.
The code I am using:
public class Quickstart {
public static void main(String[] args) throws Exception {
// display a data store file chooser dialog for shapefiles
File file = JFileDataStoreChooser.showOpenFile("shp", null);
if (file == null) {
return;
}
FileDataStore store = FileDataStoreFinder.getDataStore(file);
SimpleFeatureSource featureSource = store.getFeatureSource();
GeometryFactory gf = JTSFactoryFinder.getGeometryFactory();
// ask for current and destination positions
double latitude, longitude, latitudeDest, longitudeDest;
Scanner reader = new Scanner(System.in);
reader.useLocale(Locale.US);
System.out.println("Enter reference longitude and latitude:\n");
longitude = reader.nextDouble();
latitude = reader.nextDouble();
System.out.println("Enter destination longitude and latitude:\n");
longitudeDest = reader.nextDouble();
latitudeDest = reader.nextDouble();
reader.close();
final String EPSG4326 = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\"," +
"\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\", " +
"0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]";
CoordinateReferenceSystem crs = CRS.parseWKT(EPSG4326);
Point start = gf.createPoint(new Coordinate(longitude, latitude));
Point end = gf.createPoint(new Coordinate(longitudeDest, latitudeDest));
GeodeticCalculator gc = new GeodeticCalculator(crs);
gc.setStartingPosition(JTS.toDirectPosition(start.getCoordinate(), crs));
gc.setDestinationPosition(JTS.toDirectPosition(end.getCoordinate(), crs));
// Calculate distance between points
double distance = gc.getOrthodromicDistance();
int totalmeters = (int) distance;
int km = totalmeters / 1000;
int meters = totalmeters - (km * 1000);
float remaining_cm = (float) (distance - totalmeters) * 10000;
remaining_cm = Math.round(remaining_cm);
float cm = remaining_cm / 100;
System.out.println("Distance = " + km + "km " + meters + "m " + cm + "cm");
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setName("TwoDistancesType");
builder.setCRS(DefaultGeographicCRS.WGS84);
builder.add("location", Point.class);
// build the type
final SimpleFeatureType TYPE = builder.buildFeatureType();
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
featureBuilder.add(start);
//featureBuilder.add(end);
SimpleFeature feature = featureBuilder.buildFeature(null);
DefaultFeatureCollection featureCollection = new DefaultFeatureCollection("internal", TYPE);
featureCollection.add(feature);
Style style = SLD.createSimpleStyle(TYPE, Color.red);
Layer layer = new FeatureLayer(featureCollection, style);
// Create a map content and add our shapefile to it
MapContent map = new MapContent();
map.setTitle("TEST");
map.addLayer(layer);
// Now display the map
JMapFrame.showMap(map);
}
}
I have 2 problems:
1) I can't add a second feature to featureBuilder.It doesn't allow it.It shows Can handle 1 attributes only, index is 1.
So, how can I plot a line then?
2) With the above code, I am receiving:
org.geotools.renderer.lite.StreamingRenderer fireErrorEvent SEVERE: The scale denominator must be positive
java.lang.IllegalArgumentException: The scale denominator must be positive
------- UPDATE ------------------------
After the solution that #Michael gave for the first question , now I no longer receive the error regarding the denominator, but I am receiving an empty map (white space).
----- UPDATE according to #iant suggestion ----------------
So, I tried this.Created a coordinates which holds the coordinates of the points (start and end) ,then created a linestring with those coordinates and added it to featurebuilder.
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setName("TwoDistancesType");
builder.setCRS(DefaultGeographicCRS.WGS84);
builder.add("line", LineString.class); //added a linestring class
final SimpleFeatureType TYPE = builder.buildFeatureType();
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
Coordinate[] coordinates = {start.getCoordinate(), end.getCoordinate()};
LineString line = gf.createLineString(coordinates);
featureBuilder.add(line);
and even though I am loading a map (countries.shp) it shows me an empty white space with a red line.
------ SOLUTION -------------
Ok, so the solution is (thanks to #iants comments):
Style style = SLD.createLineStyle(Color.red, 2.0f);
Layer layer = new FeatureLayer(featureCollection, style);
// Create style for the file
Style shpStyle = SLD.createSimpleStyle(TYPE, Color.blue);
Layer shpLayer = new FeatureLayer(featureSource, shpStyle);
// Create a map content and add our shapefile to it
MapContent map = new MapContent();
map.setTitle("TEST");
map.addLayer(layer);
map.addLayer(shpLayer);
and now you have a red line on a blue map!
You need to create a LineString from your points and then store that in your feature.
You should then get a correct scale but you might want to add some other data such as a coast line to the map first. The quick start tutorial can show you how to do that.
Disclaimer: I've never used geotools.
Looking at the source code of SimpleFeatureBuilder, add calls set which throws that error if:
if(index >= values.length)
throw new ArrayIndexOutOfBoundsException("Can handle "
+ values.length + " attributes only, index is " + index);
values is populated here:
values = new Object[featureType.getAttributeCount()];
so it's obvious that that problem is because your Type only has one property. Change it so it has two:
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setName("TwoDistancesType");
builder.setCRS(DefaultGeographicCRS.WGS84);
builder.add("start", Point.class);
builder.add("end", Point.class);
Point p = /*a point*/;
ReferencedEnvelope envelope = mapPane.getDisplayArea();
/*envelope.moveTo or something like that? */
mapPane.setDisplayArea(envelope);
How can I move the envelope so that the center position is the position of the given point? All I see are "expand"-methods.
The easiest way is to create a new envelope centred on your new point:
ReferencedEnvelope env = fr.getMapPane().getDisplayArea();
double x,y;//new centre
double w2 = env.getWidth()/2.0;
double h2 = env.getHeight()/2.0;
Coordinate c = new Coordinate(x,y);
ReferencedEnvelope renv = new ReferencedEnvelope(c.x-w2,c.x+w2,c.y-h2,c.y+h2,env.getCoordinateReferenceSystem());
I am trying to extract the bounding boxes of each word from the javacpp tesseract. This appears to be the bounding box call (my full code below):
boolean box = ri.BoundingBox(RIL_WORD, coord1, coord2, coord3, coord4)
RIL_WORD is the iterator level that can be adjusted for words, sentences, and paragraphs. The coordinates are IntPointers (included class with javacpp).
The api says this returns the bounding box coordinates but returns a boolean instead. SO at this point I know there is a bounding box but still cannot get the actual coordinates. Does anyone know how to get the bounding box rectangles out of java cpp tessaract? Thanks for the help. I have posted my working code for getting the individual words and the confidence level below because I had such a hard time finding examples.
public class TesseractOCR {
public void OCRText() {
BytePointer outText;
TessBaseAPI api = new TessBaseAPI();
// Initialize tesseract-ocr with English, without specifying tessdata path
if (api.Init(null, "eng") != 0) {
System.err.println("Could not initialize tesseract.");
System.exit(1);
}
// Open input image with leptonica library
org.bytedeco.javacpp.lept.PIX image = pixRead("testimage.png");
// Get OCR result
outText = api.GetUTF8Text();
System.out.println("OCR output:\n" + outText.getString());
final ResultIterator ri = api.GetIterator();
int x1 = 0;
int y1 = 0;
int x2 = 0;
int y2 = 0;
IntPointer coord1 = new IntPointer(x1);
IntPointer coord2 = new IntPointer(y1);
IntPointer coord3 = new IntPointer(x2);
IntPointer coord4 = new IntPointer(y2);
ri.Begin();
if (ri !=null) {
do {
BytePointer word = ri.GetUTF8Text(RIL_WORD);
float conf = ri.Confidence(RIL_WORD);
boolean box = ri.BoundingBox(RIL_WORD, coord1, coord2, coord3, coord4);
System.out.println(word.getString());
System.out.println(conf);
System.out.println(box);
} while (ri.Next(RIL_WORD));
}
api.End();
outText.deallocate();
pixDestroy(image);
}
}
Right now I have:
Polygon circle = geometryBuilder.circle(
myLong,
myLat,
radiusInMeters, 10);
And it creates (with lat=28.456306, long=-16.292034 and radius=500) a nonsense polygon with huge latitudes and longitudes, such as:
POLYGON ((483.678055 28.482505000000003, 388.1865521874737 -265.4101211462366, 138.1865521874737 -447.04575314757676, -170.8304421874737 -447.0457531475768, -420.8304421874737 -265.41012114623663, -516.321945 28.482504999999943, -420.83044218747375 322.3751311462365, -170.8304421874738 504.01076314757677, 138.18655218747358 504.0107631475768, 388.18655218747364 322.3751311462367, 483.678055 28.482505000000003))
I expected to have ten pairs of coordinates with lat's and long's nearby the center point I supplied.
Any help would be more than helpful. Thanks in advance!
EDIT
In addition to #iant 's answer, I had to create a Point as a Feature
//build the type
SimpleFeatureType TYPE = null;
try {
TYPE = DataUtilities.createType("", "Location", "locations:Point:srid=4326," + "id:Integer" // a
// number
// attribute
);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
com.vividsolutions.jts.geom.Point point = geometryFactory.createPoint(
new Coordinate(
currentDevicePosition.getLongitude(),
currentDevicePosition.getLatitude()
)
);
featureBuilder.add(point);
SimpleFeature feature = featureBuilder.buildFeature( "fid.1" ); // build the 1st feature
as explained in iant's Gist here: https://gitlab.com/snippets/17558 and here: http://docs.geotools.org/, oh, and also I was missing a dependency as explained here SchemaException in java
There are two solutions to this:
Convert the radius in meters to degrees and treat the problem as a planar problem
Convert the lat/lon point to meters, calculate the circle in a locally planar projection and reproject back to lat/lon.
For 1 you could do something like which will be fine for small radii near the equator:
GeodeticCalculator calc = new GeodeticCalculator(DefaultGeographicCRS.WGS84);
calc.setStartingGeographicPoint(point.getX(), point.getY());
calc.setDirection(0.0, 10000);
Point2D p2 = calc.getDestinationGeographicPoint();
calc.setDirection(90.0, 10000);
Point2D p3 = calc.getDestinationGeographicPoint();
double dy = p2.getY() - point.getY();
double dx = p3.getX() - point.getX();
double distance = (dy + dx) / 2.0;
Polygon p1 = (Polygon) point.buffer(distance);
I'll show some code for the second as it is more general (i.e. it works better and for a better range of radii).
First you need to find a local projection, GeoTools provides a "pseudo" projection AUTO42001,x,y which is a UTM projection centred at X,Y:
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)) {
double x = geom.getCoordinate().x;
double y = geom.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();
}
}
So now pGeom is our point in metres. Buffering it is now easy
Geometry out = bufferFeature(pGeom, distance.doubleValue(SI.METER));
then we project back to WGS84 (lat/lon) using the reverse transform we looked up earlier:
retGeom = JTS.transform(out, fromTransform);
There is then a little messing around to change the feature type to reflect the fact we are returning a polygon instead of a Point. The full code is in this gist.
When I run it I get the following output:
POINT (10.840378413128576 3.4152050343701745)
POLYGON ((10.84937634426605 3.4151876838951822, 10.849200076653755 3.413423962919184, 10.84868480171117 3.4117286878605766, 10.847850322146979 3.4101670058279794, 10.846728706726902 3.4087989300555464, 10.845363057862208 3.407677033830687, 10.843805855306746 3.406844430298736, 10.84211693959797 3.406333115754347, 10.840361212705258 3.4061627400701946, 10.838606144204721 3.4063398515107184, 10.836919178768184 3.4068576449605277, 10.835365144548726 3.4076962232621035, 10.834003762019957 3.408823361646906, 10.832887348980522 3.410195745914279, 10.832058809914859 3.411760636805914, 10.831549986992338 3.4134578966399034, 10.831380436105858 3.4152223003379722, 10.831556675029052 3.416986042039048, 10.832071932633442 3.4186813409639054, 10.832906408849936 3.4202430463705085, 10.834028035422469 3.4216111414662183, 10.835393708241908 3.422733050021835, 10.836950943907517 3.4235656570147763, 10.838639896841123 3.424076965623486, 10.840395659406198 3.4242473268789406, 10.842150756595839 3.4240701947133396, 10.843837739370569 3.4235523773972796, 10.845391776937724 3.4227137757216988, 10.846753148314034 3.4215866180136185, 10.847869537398722 3.4202142214154887, 10.848698043354238 3.4186493270628633, 10.849206829051935 3.4169520731645546, 10.84937634426605 3.4151876838951822))
double latitude = 40.689234d;
double longitude = -74.044598d;
double diameterInMeters = 2000d; //2km
GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
shapeFactory.setNumPoints(64); // adjustable
shapeFactory.setCentre(new Coordinate(latitude, longitude));
// Length in meters of 1° of latitude = always 111.32 km
shapeFactory.setWidth(diameterInMeters/111320d);
// Length in meters of 1° of longitude = 40075 km * cos( latitude ) / 360
shapeFactory.setHeight(diameterInMeters / (40075000 * Math.cos(Math.toRadians(latitude)) / 360));
Polygon circle = shapeFactory.createEllipse();
GeometricShapeFactory shapeFactory = new GeometricShapeFactory(); shapeFactory.setNumPoints(64);
adjustableshapeFactory.setCentre(new Coordinate(latitude, longitude));
// Length in meters of 1°