I want to reproject an geospatial image using GDAL compiled for Android. I am currently using the swig bindings but am considering going jni/ndk.
I have successfully warped an image with the AutoCreateWarpedVRT function, but I would like to use more options (e.g. cropping the output). Currently the below is my attempt at warping using the Warp. It produces an output raster that is not warped at all, and also is not applying the -te options.
The documentation for the GDAL swig bindings is very sparse (link) and I suspect that I did not get the WarpOptions right.
Any suggestions on how to make the WarpOptions(Vector) arguments work (or any of the ???Options(Vector) for that sake) is appreciated.
Dataset src = gdal.Open("input_raster.jpg");
src.SetProjection(src_wkt);
// Set reference points
double west = 451000;
double east = 501005;
double south = 6214995;
double north = 6257000;
int width = src.getRasterXSize();
int height = src.getRasterYSize();
GCP[] gcps = { new GCP(0, 0, west, north),
new GCP(0, height, west, south),
new GCP(width, 0, east, north),
new GCP(width, height, east, south)};
src.SetGCPs(gcps, dst_wkt);
// Try to warp
Dataset[] src_array = {src};
WarpOptions warpOptions = new WarpOptions(
new Vector(Arrays.asList("s_srs EPSG:32632", "t_srs EPSG:3857", "te 1000 820"))
);
Dataset warp = gdal.Warp("warp.vrt", src_array, warpOptions);
Dataset warp_png = driverPNG.CreateCopy("warp_raster.png", warp);
//Produces a raster output without errors, but does not apply the warp.
src.delete();
warp.delete();
warp_png.delete();
According to this discussion the syntax should be:
Vector<String> options = new Vector<>();
options.add("-s_srs");
options.add("EPSG:32632");
options.add("-t_srs");
options.add("EPSG:3857");
Seems great, but throws a runtime error WarpOptions.java:17
if (cPtr == 0)
throw new RuntimeException();
So, apart from the error I think the solution is correct. The error may be platform specific.
Related
I am trying to run an AnyLogic model developed a few years ago and I do not have access to the person who coded it. I have very limited knowledge of Java. The model is built based on data being read in from an XML file. The first step is to read the layout data from an XML file and draw the layout. It used to work fine but with AnyLogic version changes, it stopped working. I get the following error:
" (RectangularNode) : The creation of space markup element isn't finished. Please use initialize() function."
The code that gets highlighted as the source of the error is:
((Agent)temp).setLocation(shapeLayoutObj);
What is interesting is that couple RectangularNodes do get drawn before this error occurs. The surrounding code is given below. The above statement identified in the console window as the error source is close to the bottom of the code provided below.
/* Layout drawing */
//Initializing objects
int scale=30;
Layout layout= CMSDDocument.getDataSection().getLayout();
List<LayoutObject> layoutObj =CMSDDocument.getDataSection().getLayoutObject();
double xLoc, yLoc, width, depth;
double xOffset = 50.0;
double yOffset = 100.0;
String layoutName;
width = layout.getBoundary().getWidth().doubleValue()*scale;
depth = layout.getBoundary().getDepth().doubleValue()*scale;
RectangularNode shapeCellArea = new RectangularNode(this, SHAPE_DRAW_2D3D, true, xOffset, yOffset, 0.0, width, depth, 0, null, Color.red, 2.0, LINE_STYLE_SOLID, POSITION_CHOICE_RANDOM, new Attractor(0,0,0));
ShapeText text = new ShapeText(SHAPE_DRAW_2D3D,true,xOffset+width/2,yOffset-20,0.0,0.0,red,"SHOP FLOOR",new Font ("Calibri", Font.BOLD , 15),TextAlignment.ALIGNMENT_CENTER);
presentation.add(shapeCellArea);
presentation.add(text);
LinkedHashMap<String,RectangularNode> floorLayout=new LinkedHashMap<String,RectangularNode>();
boolean found;
int j;
//For every layout description
for(int i= 0; i<layout.getPlacement().size();i++){
//Getting the name and the description
layoutName = layout.getPlacement().get(i).getLayoutElementIdentifier();
xLoc = layout.getPlacement().get(i).getLocation().getX().doubleValue()*scale + xOffset;
yLoc = layout.getPlacement().get(i).getLocation().getY().doubleValue()*scale + yOffset;
found = false;
j = 0;
//Looking for the layout object with tthe same name as the layout description
while (!found && j<layoutObj.size()){
if (layoutName.equals(layoutObj.get(j).getIdentifier())) {
//Getting layou values
width = layoutObj.get(j).getBoundary().getWidth().doubleValue()*scale;
depth = layoutObj.get(j).getBoundary().getDepth().doubleValue()*scale;
RectangularNode shapeLayoutObj=null;
//Configure the shape
if(layoutName.contains("Mc")){
shapeLayoutObj = new RectangularNode(this, SHAPE_DRAW_2D3D, true, xLoc, yLoc, 0.0, width, depth, 0, null, Color.black, 2.0, LINE_STYLE_DASHED, POSITION_CHOICE_BY_ATTRACTORS,new Attractor(width/2,depth/2,0));
}
else{
shapeLayoutObj = new RectangularNode(this, SHAPE_DRAW_2D3D, true, xLoc, yLoc, 0.0, width, depth, 0, null, Color.black, 2.0, LINE_STYLE_DASHED, POSITION_CHOICE_RANDOM);
}
text = new ShapeText(SHAPE_DRAW_2D3D,true,xLoc+width/2,yLoc-15,0.0,0.0,black,layoutName,new Font ("Calibri", Font.PLAIN , 11),TextAlignment.ALIGNMENT_CENTER);
presentation.add(shapeLayoutObj);
presentation.add(text);
//Associate the shape with a machine or a delay
Iterator<ArrayList<Machine>> cell=machineList.iterator();
boolean foundAgent=false;
while(cell.hasNext()&&!foundAgent){
Iterator<Machine> machines=cell.next().iterator();
while(machines.hasNext()&&!foundAgent){
Machine temp=((Machine)machines.next());
if(temp.name.equals(layoutName)){
foundAgent=true;
((Agent)temp).setLocation(shapeLayoutObj);
ShapeButton button = new ShapeButton(Main.this, true, xLoc,yLoc+depth+5,40, 20.0,controlDefault, controlDefault,
true,new Font("Dialog", 0, 11 ),"Go to" ) {
public void action(){
getExperimentHost().setPresentable(temp);
}
};
presentation.add(button);
}
}
}
I understand from past exchanges that AnyLogic switched from Java Swing (in AnyLogic7 when the model was developed) to a Web-based UI and wonder if this error is due to that switch. In AnyLogic help file, RectangularNode function is identified as "deprecated in version 8.4, will be removed in the next release". I am now using AnyLogic 8 Personal Learning Edition 8.5.2 and hence I figure that this function has been removed. Wish the error message said that! Wish also that the help file suggested the new function corresponding to the deprecated function. It is also confusing that "Rectangular Node" continues to show up in the current version as a space markup element on the process modeling library palette. Guess the underlying code has changed for that element.
I looked at AnyLogic help and best I could figure that "ShapeRectangle" may be the new function that I should use to replace "RectangularNode" function but the "ShapeRectangle" function doesn't allow defining attractors. And the "ShapeRectangle" function appears to correspond to Rectangle element in the Presentation Palette rather than to the Rectangular Node in Process Modeling Library.
Kindly point me in the right direction. Should I be replacing all instances of RectangularNode or is the error something else? If I should be replacing the RectangularNode function, what function should I be using instead?
A helpful colleague pointed out that:
"
It looks like only the RectangularNode constructors with ShapeDraw parameters are deprecated. The basic one is not.
RectangularNode's parent class AreaNode has a method to add attractors.
This would lead me to think:
a) you should not replace RectangularNode. ShapeRectangle is about shape but you can't just use a shape as a node.
b) AreaNode, the superclass of RectangularNode , has methods to add attractors so you can use those."
Based on the advice and some trial and error, I used the following statements to replace the deprecated version of RectangularNode. The compiler no longer shows the message about the RectangularNode being deprecated.
// Replacement for above statement added below by SJ on 7/3/20
RectangularNode shapeCellArea = new RectangularNode(this);
shapeCellArea.setPos(xOffset, yOffset, 0.0);
shapeCellArea.setLineColor(Color.red);
shapeCellArea.setLineStyle(LINE_STYLE_SOLID);
shapeCellArea.setLineWidth(2.0);
shapeCellArea.setPositionChoiceMode(POSITION_CHOICE_RANDOM);
shapeCellArea.setSize(width,depth);
shapeCellArea.addAttractor(new Attractor(0,0,0));
// ShapeDrawMode SHAPE_DRAW_2D3D is default mode so don't have to set that.
// end of code added by SJ on 7/3/20
Unfortunately the above changes did not remove the error. Still trying to figure out the error message and will post it as a new question.
---------- The above progress update was posted on July 4, 2020. Additional information below added on July 17, 2020 ------------
With some onlines searches and guided by the answer to another question by #Florian at How can i create Path space markup element in Anylogic via Code, I have addressed the problem by adding all the nodes to a network, adding the network to a level, and then initializing the level. The updated code is below.
Network netwkSJ = new Network(this, "mynetwork");
....
LinkedHashMap<String,RectangularNode> floorLayout=new LinkedHashMap<String,RectangularNode>();
// the loop in the original question here
// added the following statement within the loop
.......
netwkSJ.add(shapeLayoutObj);
floorLayout.put(layoutName, shapeLayoutObj);
// Commented out the statement that was generating the error in the loop
// and moved it within the new loop below
//((Agent)temp).setLocation(shapeLayoutObj); // statement giving error
.......
Level level = new Level(this, "myLevel", SHAPE_DRAW_2D3D, 0);
level.add(netwkSJ);
level.initialize(); // cannot be changed after initialization!
Set set = floorLayout.entrySet();
// iterating through elements of floorLayout to link machines to corresponding layout locations
floorLayout.forEach((Name,node) -> {
//Associate the node with a machine
Iterator<ArrayList<Machine>> cell=machineList.iterator();
boolean foundAgent=false;
while(cell.hasNext()&&!foundAgent){
Iterator<Machine> machines=cell.next().iterator();
while(machines.hasNext()&&!foundAgent){
Machine temp=((Machine)machines.next());
if(temp.name.equals(Name)){
foundAgent=true;
((Agent)temp).setLocation(node);
// System.out.println(Name + " -> " );
}
};
};
});
I am working on a fluid simulation application using SWT and want to draw the final calculated density of fluid back to UI. Now I am thinking about using SWT canvas and GC, but GC seems only for drawing shapes and lines, without the ability to draw colored pixels
There are many simulation app in the website but none is implemented by SWT. Below is the expected result for this application:
Use GC.drawPoint to draw a single pixel, the colour will be that set with GC.setForeground.
Jonah's answer is the way to do it if you want to draw everything yourself.
I'm just posting this as an alternative:
There's a library called "JHC (Java Heat Map Control)" that you could use to draw your data.
It would look something like this:
The data is provided in the form of an int[][].
// Define scales
JHCScale<String> xScale = new JHCScale.String(new String[] { "X1", "X2", "X3" });
JHCScale<String> yScale = new JHCScale.String(new String[] { "Y1", "Y2", "Y3" });
// Configure
JHCLayout layout = new JHCLayout(false, 20, true, 20);
JHCConfiguration config = new JHCConfiguration("x-label", "y-label",
JHCGradient.GRADIENT_HEAT, layout);
// Create array
int[][] array = {{0,1,0}, {1,2,1}, {0,1,0}};
// Create data object
JHCData data = JHCData.create(array, Orientation.ROW, xScale, yScale);
// Create JHC widget
JHC jhc = new JHC(shell, SWT.NONE);
jhc.setData(data, config);
I'm using Java3d (VERSION 1.6) and am trying to read all polygons from any object.
I loaded one object using following code:
private BranchGroup loadObj(String p) {
BranchGroup objRoot = new BranchGroup();
TransformGroup tg = new TransformGroup();
Transform3D t3d = new Transform3D();
t3d.setScale(0.3);
Matrix4d matrix = new Matrix4d();
t3d.get(matrix);
try
{
Scene s = null;
ObjectFile f = new ObjectFile ();
String basepath = new File(p).getAbsolutePath();
System.out.println(basepath);
f.setBasePath(basepath);
f.setFlags (0);
s = f.load (s1);
s.getSceneGroup().setBoundsAutoCompute(true);
tg.addChild (s.getSceneGroup ());
objRoot.addChild(tg);
bounds.add(objRoot.getBounds());
objRoot.compile();
}
Now I like to read the computed polygons from that BranchGroup or Scene Object and put each in a class of mainly an array of Point3d's. With that class I build some algorithms to search for specific points and stuff. So how would I get these polygons?
The reason I need it is because I'm trying to "walk" over an uneven surface. I can't use BoundingBoxes or spheres, for that is not precise enough. I would appreciate a different solution as well!
EDIT:
With the help of gouessej I got so far:
try
{
Scene s = null;
ObjectFile f = new ObjectFile ();
String basepath = new File(p).getAbsolutePath();
System.out.println(basepath);
f.setBasePath(basepath);
f.setFlags (ObjectFile.TRIANGULATE);
String s1 = p;
s = f.load (s1);
BranchGroup branch = s.getSceneGroup();
branch.setBoundsAutoCompute(true);
Shape3D shape = (Shape3D)branch.getChild(0);
Geometry g = shape.getGeometry();
TriangleArray ta = (TriangleArray)shape.getGeometry();
System.out.println(ta.getVertexCount()); // Prints around 95.000, sounds about right
System.out.println(ta.getVertexFormat()); // prints 387
double[] coords = ta.getCoordRefDouble(); // line: 526; Here it throws the exception
System.out.println(Arrays.toString(coords));
tg.addChild (branch);
objRoot.addChild(tg);
bounds.add(objRoot.getBounds());
System.out.println();
objRoot.compile();
}
But on the line ta.getCoordRefDouble(), it throws me an Exception:
Exception in thread "main" java.lang.IllegalStateException: GeometryArray: cannot access individual array references in INTERLEAVED mode
at javax.media.j3d.GeometryArray.getCoordRefDouble(GeometryArray.java:5755)
at com.object.simpleTest.Test1.loadObj(Test1.java:526)
at com.object.simpleTest.Test1.<init>(Test1.java:428)
at com.object.simpleTest.Test1.main(Test1.java:686)
What does it mean and how to fix it?
At first, Java 3D is NOT dead as you can see here (please edit your question).
Secondly, you can look at the Java documentation of the class ObjectFile. I advise you to use the flag "TRIANGULATE" to be sure to get a polygon array containing only convex polygons to ease your computations.
The branch group of your Scene object contains one Shape3D object. This Shape3D object contains a Geometry object, it stores your polygons. The source code of ObjectFile is here. Look at this line.
Edit.: You can get the BranchGroup of your scene by calling Scene.getSceneGroup(). You can see that the group is added into the scene here. Call Group.getAllChildren(), loop on all children, use instanceof to check whether a child is an instance of Shape3D. For each Shape3D, call getGeometry() or getAllGeometries(). The geometry should be a GeometryArray, maybe a TriangleArray. getCoordRefBuffer() might not work exactly in the same way in Java 3D 1.6 because we removed J3DBuffer, use getCoordRefDouble(), getCoordRefFloat() or any variant of getCoordinate() or getCoordinates(). Please ensure that you use Java 3D 1.6 so that we are talking about the same code and the same version. Older versions are obsolete and unmaintained.
Edit.2: Rather call getInterleavedVertices() as its name implies if the vertices are interleaved. Keep in mind that it might contain the normals too (in first position), not only the vertex coordinates (in second position):
nx ny nz vx vy vz
I'm trying to detect the positions of billiards balls on a table from an image taken at a perspective angle. I'm using the getPerspectiveTransform() method to find the transformation matrix and I want to apply that to only the circles I detect using HoughCircles. I'm trying to go from a rather large trapezoidal shape to a smaller rectangular shape. I don't want to do the transformation on the image first and then find the HoughCircles because the image gets too warped for houghcircles to provide useful results.
Here's my code:
CvMat mmat = cvCreateMat(3,3,CV_32FC1);
double srcX1 = 462;
double srcX2 = 978;
double srcX3 = 1440;
double srcX4 = 0;
double srcY = 241;
double srcHeight = 772;
double dstX = 56.8;
double dstY = 33.5;
double dstWidth = 262.4;
double dstHeight = 447.3;
CvSeq seq = cvHoughCircles(newGray, circles, CV_HOUGH_GRADIENT, 2.1d, (double)newGray.height()/40, 85d, 65d, 5, 50);
JavaCV.getPerspectiveTransform(new double[]{srcX1, srcY, srcX2,srcY, srcX3, srcHeight, srcX4, srcHeight},
new double[]{dstX, dstY, dstWidth, dstY, dstWidth, dstHeight, dstX, dstHeight}, mmat);
cvWarpPerspective(seq, seq, mmat);
for(int j=0; j<seq.total(); j++){
CvPoint3D32f point = new CvPoint3D32f(cvGetSeqElem(seq, j));
float xyr[] = {point.x(),point.y(),point.z()};
CvPoint center = new CvPoint(Math.round(xyr[0]), Math.round(xyr[1]));
int radius = Math.round(xyr[2]);
cvCircle(gray, center, 3, CvScalar.GREEN, -1, 8, 0);
cvCircle(gray, center, radius, CvScalar.BLUE, 3, 8, 0);
}
The problem is I get this error on the warpPerspective() method:
error: (-215) seq->total > 0 && CV_ELEM_SIZE(seq->flags) == seq->elem_size in function cv::Mat cv::cvarrToMat(const CvArr*, bool, bool, int)
Also I guess it's worth mentioning that I'm using JavaCV, in case the method calls look a bit different than what you're used to. Thanks for any help.
Answer:
the problem with what you want to do (besides the obvious, opencv wont let you) is that the radius cant really be warped correctly. AFAIK the xy coordinates are pretty easy to calculate x'=((m00x+m01y+m02)/(m20x+m21y+m22)) y'=((m10x+m11y+m12)/(m20x+m21y_m22)) when m is the transformation matrix. the radius you can hack by transforming all the points of the original circle and then find the max distance between x'y' and those points (atleast if the radius in the warped image is expected to cover all those points)
btw, mIJx = m(i,j)*x (just to clarify)
End Answer.
Everything i write is according to the c++ version, i've never used JavaCV but from what i could see its just a wrapper that calls the native c++ lib.
CvSeq is a sequance data structure that behaves like a linked list.
the assert your application crushes at is
CV_Assert(seq->total > 0 && CV_ELEM_SIZE(seq->flags) == seq->elem_size);
which means that either your seq instance is empty (total is the number of elements in the sequence) or somehow the inner seq flags are corrupted.
I'd recommend that you'd check the total member of your CvSeq, and the cvHoughCircles call.
all of this occurs before the actual implementation of cvWarpPerspective (its the first line in the implementation, that only converts your CvSeq to cv::Mat).. so its not the warping but what you're doing before that.
anyway, to understand whats wrong with cvHoughCircles we'll need more info about the creation of newGray and circles.
here is an example i've found on the javaCV page (Link)
IplImage gray = cvCreateImage( cvSize( img.width, img.height ), IPL_DEPTH_8U, 1 );
cvCvtColor( img, gray, CV_RGB2GRAY );
// smooth it, otherwise a lot of false circles may be detected
cvSmooth(gray,gray,CV_GAUSSIAN,9,9,2,2);
CvMemStorage circles = CvMemStorage.create();
CvSeq seq = cvHoughCircles(gray, circles.getPointer(), CV_HOUGH_GRADIENT,
2, img.height/4, 100, 100, 0, 0);
for(int i=0; i<seq.total; i++){
float xyr[] = cvGetSeqElem(seq,i).getFloatArray(0, 3);
CvPoint center = new CvPoint(Math.round(xyr[0]), Math.round(xyr[1]));
int radius = Math.round(xyr[2]);
cvCircle(img, center.byValue(), 3, CvScalar.GREEN, -1, 8, 0);
cvCircle(img, center.byValue(), radius, CvScalar.BLUE, 3, 8, 0);
}
from what i've seen in the implementation of cvHoughCircles, the answer is saved in the circles buff and at the end they create from it the CvSeq to return, so if you've allocated the circles buff wrong, it wont work.
EDIT:
as you can see, the CvSeq instance in case of the return from cvHoughCircles is a list of point-values, that is probably why the assertion failed. you cannot convert this CvSeq into a cv::Mat.. because its just not a cv::Mat. to get only the circles returned from cvHoughCircles in an cv::Mat instance, you'll need to create a new cv::Mat instance and than draw onto it all the circles in the CvSeq - as seen in the provided example above.
than the warping will work (you'll have a cv::Mat instance, and that is what the function expect - a cv::Mat as the only element in the CvSeq)
END EDIT
here is the c++ reference for CvSeq
and if you want to fiddle with the source code than
cvarrToMat is in matrix.cpp
CV_ELEM_SIZE is in types_c.h
cvWarpPerspective is in imgwarp.cpp
cvHoughCircles is in hough.cpp
I hope that will help.
BTW, your next error will probably be:
cv::warpPerspective in the C++ OpencCv asserts that dst.data != src.data
thus
cvWarpPerspective(seq, seq, mmat);
wont work cause your source mat and destination mat referencing the same data.
Not all the functions in OpenCV (and image processing in general) work in-situ (because there is no in-situ algorithm or because its slower then the other version eg. transpose of an n*n mat will work in-situ, but n*m where n!=m will be harder to do in-situ and might be slower)
you cant assume the using the src matrix as the dst will work.
I know there are many comparisons of java plotting libraries out there, but I'm not finding what I need. I just want a mind-numbingly simple toolkit that creates images of scatterplots from a set of coordinates. No GUI, no interaction, no fancy display, just a basic XY coordinate system with points.
It wouldn't be the end of the world to use something that offers a lot more functionality than I need, but I'd rather not. Do you know of anything like what I'm looking for?
Have you looked at JFreeChart? While it can do some very advanced things, it also does the simple as well. Shown below is a screenshot of its scatter plot capability.
(source: jfree.org)
I looked around at what existed, and realized that jcckit is technically pretty good, but just lacks a simple wrapper around it to make it easy to use.
So I forked it and made a really simple wrapper. Here's how to use:
import static easyjcckit.QuickPlot.*;
double[] xaxis = new double[]{0,1,2,3,4,5};
double[] yvalues = new double[]{0,1,4,9,16,25};
scatter( xaxis, yvalues ); // create a plot using xaxis and yvalues
double[] yvalues2 = new double[]{0,1,2,3,4,5};
addScatter( xaxis, yvalues2 ); // create a second plot on top of first
System.out.println("Press enter to exit");
System.in.read();
As well as scatter plots, you can freely add lines to the same axes if you wish using 'addPlot' and 'plot'.
Here is the code: https://bitbucket.org/hughperkins/easyjcckit
You an use a custom JPanel to draw your data(not tested, but you get the idea...)
private List<Point2D> data=(...);
JPanel pane=new JPanel()
{
protected paintComponent(Graphics2D g)
{
super.paintComponent(g);
int minx=(...),miny=(...),maxx=(...),maxy=(...);
for(Point2D p: data)
{
int x=((p.getX()-minx)/(maxx-minx))*this.getWidth();
int y=((p.getY()-miny)/(maxy-miny))*this.getHeight();
g.drawLine(x-5,y,x+5,y);
g.drawLine(x,y-5,x,y+5);
}
}
pane.setOpaque(true);
(...)
anotherComponent.add(pane);
(...)
}
Also you could check Simple Java Plot. Minimal example (no options):
Plot plot = Plot.plot(null).
// setting data
series(null, Plot.data().
xy(1, 2).
xy(3, 4), null);
// saving sample_minimal.png
plot.save("sample_minimal", "png");