How to get the top of a dice in libgdx 3d? - java

I have some dice rolling animation which I originally had done in javascript using THREE & CANNON and I want to reimplent it in libgdx.
The animation part is done, but I have yet to figure out how to identify the top of the dice.
I am aware of at least 3 different methods to do that, so I am unsure which of these can actually done with libgdx / bullet.
(I am struggeling with this, the other approaches sound easier)
1.
Somehow do it the mathematical way by using the rotation of the dice.
I now the oginal rotation of the dice by getting it's quaternion,
and I can get the quaternions of the dice after it has been cast.
I also know some of the quaternions of the rotations it should end up with (at least somewhat as the cast won't be perfect).
Edit 3:
Currently I am counting 34 uniques (1: 4x, 2: 6x; 3: 5x, 4: 7x, 5: 7x, 6: 5x) out of a total of 64 possible rotations.
But there still seems to be some more ...
I calculated the 64 using the three axis and rotating each of them between 0 and 270°, in steps of 90°.
For example the following Quaternion's with a value of
( 0.5f,-0.5f, 0.5f,-0.5f) -> which should be 4
(-0.5f,-0.5f, 0.5f,-0.5f) -> which should be 5
are not covered by this approach.
Using the min distance between each coordinate seems to work better now.
The mathematical way is (mostly) working, I'd still like somme feedback if the other two options can be realised within libgdx.
The other options:
2.
Use raycasting to get the top of the dice.
In javasript the raycasting method delivered the mesh objects to me which were intersected and they had a property which let me identify which side it was. I haven't seen anything similiar in libgdx yet.
function identifyDiceTop(position) {
var raycaster = new THREE.Raycaster();
var vec = new THREE.Vector3(position.x, position.y, 5);
raycaster.set( vec, new THREE.Vector3(0, 0, -1));
var intersects = raycaster.intersectObjects( scene.children, true );
if (intersects.length > 0 && intersects[ 0 ].face != null) {
switch (intersects[ 0 ].face.materialIndex) {
// see which value is top
}
}
3.
Get the top side of the dice by finding the highest y-coordinate of a mesh of the dice object. Even simpler than above, but again can I even access the meshes somehow?
I am using the follwoing code to create the dice. But here I do not use any meshes directly, so I might need a different construction method?
MeshPartBuilder meshPartBuilder =
modelBuilder.part(
"box",
GL20.GL_TRIANGLES,
attr,
new Material(
TextureAttribute.createDiffuse(
region
)
)
);
meshPartBuilder.setUVRange(dice.side5);
meshPartBuilder.rect(-size,-size,-size,-size,size,-size,size,size,-size,size,-size,-size,0,0,-1);
meshPartBuilder.setUVRange(dice.side2);
meshPartBuilder.rect(-size,size,size, -size,-size,size,size,-size,size,size,size,size,0,0,1);
meshPartBuilder.setUVRange(dice.side6);
meshPartBuilder.rect(-size,-size,size, -size,-size,-size,size,-size,-size,size,-size,size,0,-1,0);
meshPartBuilder.setUVRange(dice.side1);
meshPartBuilder.rect(-size,size,-size, -size,size,size,size,size,size,size,size,-size,0,1,0);
meshPartBuilder.setUVRange(dice.side4);
meshPartBuilder.rect(-size,-size,size, -size,size,size,-size,size,-size,-size,-size,-size,-1,0,0);
meshPartBuilder.setUVRange(dice.side3);
meshPartBuilder.rect(size,-size,-size, size,size,-size,size,size,size,size,-size,size,1,0,0);
I hope somebody can give me a pointer.

Related

Infinite vertical world

I am created two bodies in Box2d, they are: Player and Platform;
I wanted to create game like Doodle Jump, but I don't know how to create "infinite world with generating platforms";
There is my code where I am creating Array:
buckets = new Array<Bucket>();
for(int i=1;i<BUCKET_COUNT;i++){
buckets.add(new Bucket(W/2,BUCKET_MARGIN*i, world));
}
And this code where I am "Trying" to change position of each platform when camera position is change:
for(Bucket bucket : buckets){
if(cam.position.y - (cam.viewportHeight/2) > bucket.getBody().getPosition().y + 22/PPM){
bucket.repos(W/2,bucket.getBody().getPosition().y + BUCKET_MARGIN);
}
}
It works! But it changes last platform position to very far bottom:
Regarding how to create an infinite world?
Use the world instance shift origin method. In the Box2D 2.3.2 C++ library code, this is the b2World::ShiftOrigin(const b2Vec2& newOrigin) method. Here's an excerpt of this method's declaration along with its documentation:
/// Shift the world origin. Useful for large worlds.
/// The body shift formula is: position -= newOrigin
/// #param newOrigin the new origin with respect to the old origin
void ShiftOrigin(const b2Vec2& newOrigin);
In Java, you should be able to find a similar interface method.
With shift origin you keep the viewport to the physics world centered on (or near) the physical world origin (of 0, 0). This is basically the practical means of accomplishing what Yevhen Danchenko suggested in the comments.
A reason for using this is that the implementation of floating-point arithmetic which Box2D relies on, is itself not infinitely wide ranging nor infinitely accurate. So shifting things helps keep things closer to the origin where the floating-point values are more accurate and keeps things from going off the range of practically usable values assuming that you'll only ever be showing a limited range of x and y values.

Small bug in Koch's Snowflake Implementation

So I'm programming a recursive program that is supposed to draw Koch's snowflake using OpenGL, and I've got the program basically working except one tiny issue. The deeper the recursion, the weirder 2 particular vertices get. Pictures at the bottom.
EDIT: I don't really care about the OpenGL aspect, I've got that part down. If you don't know OpenGL, all that the glVertex does is draw a line between the two vertices specified in the 2 method calls. Pretend its drawLine(v1,v2). Same difference.
I suspect that my method for finding points is to blame, but I can't find anything that looks incorrect.
I'm following the basically standard drawing method, here are the relevant code snips
(V is for vertex V1 is the bottom left corner, v2 is the bottom right corner, v3 is the top corner):
double dir = Math.PI;
recurse(V2,V1,n);
dir=Math.PI/3;
recurse(V1,V3,n);
dir= (5./3.)* Math.PI ;
recurse(V3,V2,n);
Recursive method:
public void recurse(Point2D v1, Point2D v2, int n){
double newLength = v1.distance(v2)/3.;
if(n == 0){
gl.glVertex2d(v1.getX(),v1.getY());
gl.glVertex2d(v2.getX(),v2.getY());
}else{
Point2D p1 = getPointViaRotation(v1, dir, newLength);
recurse(v1,p1,n-1);
dir+=(Math.PI/3.);
Point2D p2 = getPointViaRotation(p1,dir,newLength);
recurse(p1,p2,n-1);
dir-=(Math.PI*(2./3.));
Point2D p3 = getPointViaRotation(p2, dir, newLength);
recurse(p2,p3,n-1);
dir+=(Math.PI/3.);
recurse(p3,v2,n-1);
}
}
I really suspect my math is the problem, but this looks correct to me:
public static Point2D getPointViaRotation(Point2D p1, double rotation, double length){
double xLength = length * Math.cos(rotation);
double yLength = length * Math.sin(rotation);
return new Point2D.Double(xLength + p1.getX(), yLength + p1.getY());
}
N = 0 (All is well):
N = 1 (Perhaps a little bendy, maybe)
N = 5 (WAT)
I can't see any obvious problem code-wise. I do however have a theory about what happens.
It seems like all points in the graph are based on the locations of the points that came before it. As such, any rounding errors that occurs during this process eventually start accumulating, eventually ending with it going haywire and being way off.
What I would do for starters is calculating the start and end points of each segment before recursing, as to limit the impact of the rounding errors of the inner calls.
One thing about Koch's snowflake is, that the algorithm will lead to a rounding issue one time (it is recursive and all rounding errors add up). The trick is, to keep it going as long as possible. There're three things you can do:
If you want to get more detailed, the only way is to expand the possibilities of Double. You will need to use your own range of coordinates and transform them, every time you actually paint on the screen, to screen coordinates. Your own coordinates should zoom and show the last recursion step (the last triangle) in a coordination system of e.g. 100x100. Then calculate the three new triangles on top of that, transform into screen coordinates and paint.
The line dir=Math.PI/3; divides by 3 instead of (double) 3. Add the . after the 3
Make sure you use Point2D.Double anywhere. Your code should do so, but I would explicitely write it everywhere.
You won the game, when you still have a nice snowflake but get a Stackoverflow.
So, it turns out I am the dumbest man alive.
Thanks everyone for trying, I appreciate the help.
This code is meant to handle an equilateral triangle, its very specific about that (You can tell by the angles).
I put in a triangle with the height equal to the base (not equilateral). When I fixed the input triangle, everything works great.

JSyn, siren sound using oscillator fed/controlled/inputInto/daisy-chainedTo by another oscillator and a constant...and generating more than one sound

I have been trying to follow the example here, but it is not working, and I have not been able to find any other sources:
[ http://www.softsynth.com/jsyn/tutorial/osc_control.php ][1]
As far as I can tell, I have followed this sample code snippet exactly (except that I found out that AddUnit changed to Add sometime since that webpage was updated):
[...]make the frequency to waver slightly about a central frequency that is in a more useful range. We can do this by using an AddUnit to add the output of an oscillator to a constant value that we can set. We can also reduce the amplitude of the first oscillator to be within a smaller range.
AddUnit freqAdder = new AddUnit();
sineOsc1.output.connect( freqAdder.inputA ); // pass through adder
freqAdder.output.connect( sineOsc2.frequency ); // control second oscillator freq
freqAdder.inputB.set( 500.0 ); // add constant that will center us at 500 Hz
sineOsc1.amplitude.set( 100.0 ); // reduce offset to +/- 100 Hz
Thus the frequency of sineOsc2 will be sineOsc1.output plus inputB.
Can anybody see what is wrong with my code (below)? I already have a simple oscillator sound working. I just can't hear this second, more complicated sound, which is supposed to be siren-like.
It may be a problem with my coding of the siren sound, or it may just be a problem with my coding of generating two sounds. (Are 2 Synthesizers required? I have tried it with 1 and 2 Synthesizers.) (Are 2 lineOuts required? Other web sources say "no".)
Here is my code with 2 synthesizers and 1 output:
(Comments in quotes are from other sample code. I only understand a little of what those comments are getting at.)
import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.unitgen.Add;
import com.jsyn.unitgen.LineOut;
import com.jsyn.unitgen.SineOscillator;
[...]
com.jsyn.Synthesizer synthPCMSonification = JSyn.createSynthesizer();
com.jsyn.Synthesizer synthPCMAlarm = JSyn.createSynthesizer();
// "an instance of Synthesizer"
com.jsyn.unitgen.SineOscillator oscData = new SineOscillator();
SineOscillator oscAlarmWaverEnvelope = new SineOscillator();
SineOscillator oscAlarmComplete = new SineOscillator();
// "a unit"
com.jsyn.unitgen.LineOut oscsLineOut = new LineOut();
// "a unit"
[...]
// "start synthesis engine"
synthPCMSonification.start();
synthPCMAlarm.start();
// "build unit generators"
synthPCMSonification.add(oscData);
//synthPCM.add(oscAlarmWaverEnvelope); //TODO: Figure out if need line
synthPCMAlarm.add(oscAlarmComplete);
synthPCMSonification.add(oscsLineOut);
synthPCMAlarm.add(oscsLineOut);
oscData.frequency.set(LOWEST_FREQUENCY_C);
oscData.amplitude.set(volSonification);
//create a frequency adder for a siren-like alarm
com.jsyn.unitgen.Add oscAlarmFreqAdder = new Add(); //used to be AddUnit
//set the alarm centre frequency
alarmCentreFreq = (LOWEST_FREQUENCY_C
* Math.pow(2, OCTAVES_SPANNED_C + 1));
//This formula centres the alarm one octave
//above the threshold's sonification freqency
alarmWaverFreq = alarmCentreFreq / 10;
//This sets the waver at one tenth of the centre freq
//Unfortunately, the waver appears to need to be the
//same amount above and below the centre
//(linear, vice perceptually-linear (exponential))
System.out.println(alarmCentreFreq + "-Hz alarm centre frequency");
oscAlarmFreqAdder.inputB.set(alarmCentreFreq);
//set the alarm waver envelope
//(alarm will range between centre-waver and centre+waver)
oscAlarmWaverEnvelope.frequency.set(alarmCentreFreq / 10);
//"pass through adder" (??)
oscAlarmWaverEnvelope.output.connect(oscAlarmFreqAdder.inputA);
//(entered this with by starting to type, then hitting [Ctrl]+[Space]!)
//"control the 2nd oscillator frequency" (?)
oscAlarmFreqAdder.output.connect(oscAlarmComplete.frequency);
//set alarm volume
oscAlarmComplete.amplitude.set(volAlarm);
// "connect unit generators"
// connect oscillator to both channels of stereo player
oscAlarmComplete.output.connect(0, oscsLineOut.input, 0);
oscAlarmComplete.output.connect(0, oscsLineOut.input, 1);
// "startUnitGenerators"
// "start execution of units. JSyn 'pulls' data so the only unit
// you have to start() is the last one, in this case our LineOut"
oscsLineOut.start();
How many people out there know and use JSyn? How about meta-oscillators?
If you have ever connected different JSyn parts together, or even just got it to output more than one sound at once, you know more than I do...
There are a number of things that could be improved here.
1) You created two synthesizers:
com.jsyn.Synthesizer synthPCMSonification = JSyn.createSynthesizer();
com.jsyn.Synthesizer synthPCMAlarm = JSyn.createSynthesizer();
That is only needed if you are running some synthesis in non-real-time or at a different sample rate. I highly recommend only using one synthesizer. Connecting units across synthesizers or running the same unit on both synthesizers will cause problems. I suspect that is the main error.
You can have multiple LineOut units in one synth. Or you can mix automatically by connecting multiple units to the LineOut.
2) I recommend starting with just one oscillator connected to a LineOut. After you can get that to make sound, add the modulation.
3) You can get exponential frequency (pitch) modulation using the optimized PowerOfTwo unit.
http://www.softsynth.com/jsyn/docs/javadocs/com/jsyn/unitgen/PowerOfTwo.html
Connect the LFO to a PowerOfTwo unit. Then use a Multiply unit to scale the center frequency. An LFO that goes from +1.0 to -1.0 will scale the frequency up and down an octave.
4) The tutorial uses the old JSyn API. I need to update it. Note that in the new JSyn API you rarely need an Add unit because the input ports will automatically sum any connected inputs.
5) StackOverflow is great but you can get support from the JSyn community of over 600 people by signing up for the JSyn mail list.
http://www.softsynth.com/jsyn/support/index.php
(Note: I really wanted to just add this to the answer #philburk gave, as his answer certainly helped, but my requests to add this to his answer were rejected, so I have to give this as a separate answer. I am torn on whether I should move the 'accept' to this answer or not, though, even though this is the actual fix.)
The code in the question can be fixed by adding (or changing to, in the case of the frequency line) the following lines of code:
synthPCM.add(oscAlarmWaverEnvelope) //(this is the line I already suspected I needed)
oscAlarmWaverEnvelope.frequency.set(4.0);
oscAlarmWaverEnvelope.amplitude.set(alarmCentreFreq / 10);
...meaning:
An oscillator still needs to be connected to the synthesizer even if it will not be heard directly (and a line like this was missing from the sample code, probably because the sample code assumed a person had some previous sample code from the tutorial in it).
An envelope oscillator should be set to a very low frequency in order for its effect to be heard.
An envelope oscillator should be given an amplitude, and this needs to be on the order of the frequency being altered in order for its effect to be heard.

Random geographic coordinates (on land, avoid ocean)

Any clever ideas on how to generate random coordinates (latitude / longitude) of places on Earth? Latitude / Longitude. Precision to 5 points and avoid bodies of water.
double minLat = -90.00;
double maxLat = 90.00;
double latitude = minLat + (double)(Math.random() * ((maxLat - minLat) + 1));
double minLon = 0.00;
double maxLon = 180.00;
double longitude = minLon + (double)(Math.random() * ((maxLon - minLon) + 1));
DecimalFormat df = new DecimalFormat("#.#####");
log.info("latitude:longitude --> " + df.format(latitude) + "," + df.format(longitude));
Maybe i'm living in a dream world and the water topic is unavoidable ... but hopefully there's a nicer, cleaner and more efficient way to do this?
EDIT
Some fantastic answers/ideas -- however, at scale, let's say I need to generate 25,000 coordinates. Going to an external service provider may not be the best option due to latency, cost and a few other factors.
To deal with the body of water problem is going to be largely a data issue, e.g. do you just want to miss the oceans or do you need to also miss small streams. Either you need to use a service with the quality of data that you need, or, you need to obtain the data yourself and run it locally. From your edit, it sounds like you want to go the local data route, so I'll focus on a way to do that.
One method is to obtain a shapefile for either land areas or water areas. You can then generate a random point and determine if it intersects a land area (or alternatively, does not intersect a water area).
To get started, you might get some low resolution data here and then get higher resolution data here for when you want to get better answers on coast lines or with lakes/rivers/etc. You mentioned that you want precision in your points to 5 decimal places, which is a little over 1m. Do be aware that if you get data to match that precision, you will have one giant data set. And, if you want really good data, be prepared to pay for it.
Once you have your shape data, you need some tools to help you determine the intersection of your random points. Geotools is a great place to start and probably will work for your needs. You will also end up looking at opengis code (docs under geotools site - not sure if they consumed them or what) and JTS for the geometry handling. Using this you can quickly open the shapefile and start doing some intersection queries.
File f = new File ( "world.shp" );
ShapefileDataStore dataStore = new ShapefileDataStore ( f.toURI ().toURL () );
FeatureSource<SimpleFeatureType, SimpleFeature> featureSource =
dataStore.getFeatureSource ();
String geomAttrName = featureSource.getSchema ()
.getGeometryDescriptor ().getLocalName ();
ResourceInfo resourceInfo = featureSource.getInfo ();
CoordinateReferenceSystem crs = resourceInfo.getCRS ();
Hints hints = GeoTools.getDefaultHints ();
hints.put ( Hints.JTS_SRID, 4326 );
hints.put ( Hints.CRS, crs );
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2 ( hints );
GeometryFactory gf = JTSFactoryFinder.getGeometryFactory ( hints );
Coordinate land = new Coordinate ( -122.0087, 47.54650 );
Point pointLand = gf.createPoint ( land );
Coordinate water = new Coordinate ( 0, 0 );
Point pointWater = gf.createPoint ( water );
Intersects filter = ff.intersects ( ff.property ( geomAttrName ),
ff.literal ( pointLand ) );
FeatureCollection<SimpleFeatureType, SimpleFeature> features = featureSource
.getFeatures ( filter );
filter = ff.intersects ( ff.property ( geomAttrName ),
ff.literal ( pointWater ) );
features = featureSource.getFeatures ( filter );
Quick explanations:
This assumes the shapefile you got is polygon data. Intersection on lines or points isn't going to give you what you want.
First section opens the shapefile - nothing interesting
you have to fetch the geometry property name for the given file
coordinate system stuff - you specified lat/long in your post but GIS can be quite a bit more complicated. In general, the data I pointed you at is geographic, wgs84, and, that is what I setup here. However, if this is not the case for you then you need to be sure you are dealing with your data in the correct coordinate system. If that all sounds like gibberish, google around for a tutorial on GIS/coordinate systems/datum/ellipsoid.
generating the coordinate geometries and the filters are pretty self-explanatory. The resulting set of features will either be empty, meaning the coordinate is in the water if your data is land cover, or not empty, meaning the opposite.
Note: if you do this with a really random set of points, you are going to hit water pretty often and it could take you a while to get to 25k points. You may want to try to scope your point generation better than truly random (like remove big chunks of the Atlantic/Pacific/Indian oceans).
Also, you may find that your intersection queries are too slow. If so, you may want to look into creating a quadtree index (qix) with a tool like GDAL. I don't recall which index types are supported by geotools, though.
This has being asked a long time ago and I now have the similar need. There are two possibilities I am looking into:
1. Define the surface ranges for the random generator.
Here it's important to identify the level of precision you are going for. The easiest way would be to have a very relaxed and approximate approach. In this case you can divide the world map into "boxes":
Each box has it's own range of lat lon. Then you first randomise to get a random box, then you randomise to get a random lat and random long within the boundaries of that box.
Precisions is of course not the best at all here... Though it depends:) If you do your homework well and define a lot of boxes covering most complex surface shapes - you might be quite ok with the precision.
2. List item
Some API to return continent name from coordinates OR address OR country OR district = something that WATER doesn't have. Google Maps API's can help here. I didn't research this one deeper, but I think it's possible, though you will have to run the check on each generated pair of coordinates and rerun IF it's wrong. So you can get a bit stuck if random generator keeps throwing you in the ocean.
Also - some water does belong to countries, districts...so yeah, not very precise.
For my needs - I am going with "boxes" because I also want to control exact areas from which the random coordinates are taken and don't mind if it lands on a lake or river, just not open ocean:)
Download a truckload of KML files containing land-only locations.
Extract all coordinates from them this might help here.
Pick them at random.
Definitely you should have a map as a resource. You can take it here: http://www.naturalearthdata.com/
Then I would prepare 1bit black and white bitmap resource with 1s marking land and 0x marking water.
The size of bitmap depends on your required precision. If you need 5 degrees then your bitmap will be 360/5 x 180/5 = 72x36 pixels = 2592 bits.
Then I would load this bitmap in Java, generate random integer withing range above, read bit, and regenerate if it was zero.
P.S. Also you can dig here http://geotools.org/ for some ready made solutions.
To get a nice even distribution over latitudes and longitudes you should do something like this to get the right angles:
double longitude = Math.random() * Math.PI * 2;
double latitude = Math.acos(Math.random() * 2 - 1);
As for avoiding bodies of water, do you have the data for where water is already? Well, just resample until you get a hit! If you don't have this data already then it seems some other people have some better suggestions than I would for that...
Hope this helps, cheers.
There is another way to approach this using the Google Earth Api. I know it is javascript, but I thought it was a novel way to solve the problem.
Anyhow, I have put together a full working solution here - notice it works for rivers too: http://www.msa.mmu.ac.uk/~fraser/ge/coord/
The basic idea I have used is implement the hiTest method of the GEView object in the Google Earth Api.
Take a look at the following example of the hitest from Google.
http://earth-api-samples.googlecode.com/svn/trunk/examples/hittest.html
The hitTest method is supplied a random point on the screen in (pixel coordinates) for which it returns a GEHitTestResult object that contains information about the geographic location corresponding to the point. If one uses the GEPlugin.HIT_TEST_TERRAIN mode with the method one can limit results only to land (terrain) as long as we screen the results to points with an altitude > 1m
This is the function I use that implements the hitTest:
var hitTestTerrain = function()
{
var x = getRandomInt(0, 200); // same pixel size as the map3d div height
var y = getRandomInt(0, 200); // ditto for width
var result = ge.getView().hitTest(x, ge.UNITS_PIXELS, y, ge.UNITS_PIXELS, ge.HIT_TEST_TERRAIN);
var success = result && (result.getAltitude() > 1);
return { success: success, result: result };
};
Obviously you also want to have random results from anywhere on the globe (not just random points visible from a single viewpoint). To do this I move the earth view after each successful hitTestTerrain call. This is achieved using a small helper function.
var flyTo = function(lat, lng, rng)
{
lookAt.setLatitude(lat);
lookAt.setLongitude(lng);
lookAt.setRange(rng);
ge.getView().setAbstractView(lookAt);
};
Finally here is a stripped down version of the main code block that calls these two methods.
var getRandomLandCoordinates = function()
{
var test = hitTestTerrain();
if (test.success)
{
coords[coords.length] = { lat: test.result.getLatitude(), lng: test.result.getLongitude() };
}
if (coords.length <= number)
{
getRandomLandCoordinates();
}
else
{
displayResults();
}
};
So, the earth moves randomly to a postition
The other functions in there are just helpers to generate the random x,y and random lat,lng numbers, to output the results and also to toggle the controls etc.
I have tested the code quite a bit and the results are not 100% perfect, tweaking the altitude to something higher, like 50m solves this but obviously it is diminishing the area of possible selected coordinates.
Obviously you could adapt the idea to suit you needs. Maybe running the code multiple times to populate a database or something.
As a plan B, maybe you can pick a random country and then pick a random coordinate inside of this country. To be fair when picking a country, you can use its area as weight.
There is a library here and you can use its .random() method to get a random coordinate. Then you can use GeoNames WebServices to determine whether it is on land or not. They have a list of webservices and you'll just have to use the right one. GeoNames is free and reliable.
Go there http://wiki.openstreetmap.org/
Try to use API: http://wiki.openstreetmap.org/wiki/Databases_and_data_access_APIs
I guess you could use a world map, define a few points on it to delimit most of water bodies as you say and use a polygon.contains method to validate the coordinates.
A faster algorithm would be to use this map, take some random point and check the color beneath, if it's blue, then water... when you have the coordinates, you convert them to lat/long.
You might also do the blue green thing , and then store all the green points for later look up. This has the benifit of being "step wise" refinable. As you figure out a better way to make your list of points you can just point your random graber at a more and more acurate group of points.
Maybe a service provider has an answer to your question already: e.g. https://www.google.com/enterprise/marketplace/viewListing?productListingId=3030+17310026046429031496&pli=1
Elevation api? http://code.google.com/apis/maps/documentation/elevation/ above sea level or below? (no dutch points for you!)
Generating is easy, the Problem is that they should not be on water. I would import the "Open Streetmap" for example here http://ftp.ecki-netz.de/osm/ and import it to an Database (verry easy data Structure). I would suggest PostgreSQL, it comes with some geo functions http://www.postgresql.org/docs/8.2/static/functions-geometry.html . For that you have to save the points in a "polygon"-column, then you can check with the "&&" operator if it is in an Water polygon. For the attributes of an OpenStreetmap Way-Entry you should have a look at http://wiki.openstreetmap.org/wiki/Category:En:Keys
Supplementary to what bsimic said about digging into GeoNames' Webservices, here is a shortcut:
they have a dedicated WebService for requesting an ocean name.
(I am aware the of OP's constraint to not using public web services due to the amount of requests. Nevertheless I stumbled upon this with the same basic question and consider this helpful.)
Go to http://www.geonames.org/export/web-services.html#astergdem and have a look at "Ocean / reverse geocoding". It is available as XML and JSON. Create a free user account to prevent daily limits on the demo account.
Request example on ocean area (Baltic Sea, JSON-URL):
http://api.geonames.org/oceanJSON?lat=54.049889&lng=10.851388&username=demo
results in
{
"ocean": {
"distance": "0",
"name": "Baltic Sea"
}
}
while some coordinates on land result in
{
"status": {
"message": "we are afraid we could not find an ocean for latitude and longitude :53.0,9.0",
"value": 15
}
}
Do the random points have to be uniformly distributed all over the world? If you could settle for a seemingly uniform distribution, you can do this:
Open your favorite map service, draw a rectangle inside the United States, Russia, China, Western Europe and definitely the northern part of Africa - making sure there are no big lakes or Caspian seas inside the rectangles. Take the corner coordinates of each rectangle, and then select coordinates at random inside those rectangles.
You are guaranteed non of these points will be on any sea or lake. You might find an occasional river, but I'm not sure how many geoservices are going to be accurate enough for that anyway.
This is an extremely interesting question, from both a theoretical and practical perspective. The most suitable solution will largely depend on your exact requirements. Do you need to account for every body of water, or just the major seas and oceans? How critical are accuracy and correctness; Will identifying sea as land or vice-versa be a catastrophic failure?
I think machine learning techniques would be an excellent solution to this problem, provided that you don't mind the (hopefully small) probability that a point of water is incorrectly classified as land. If that's not an issue, then this approach should have a number of advantages against other techniques.
Using a bitmap is a nice solution, simple and elegant. It can be produced to a specified accuracy and the classification is guaranteed to be correct (Or a least as correct as you made the bitmap). But its practicality is dependent on how accurate you need the solution to be. You mention that you want the coordinate accuracy to 5 decimal places (which would be equivalent to mapping the whole surface of the planet to about the nearest metre). Using 1 bit per element, the bitmap would weigh in at ~73.6 terabytes!
We don't need to store all of this data though; We only need to know where the coastlines are. Just by knowing where a point is in relation to the coast, we can determine whether it is on land or sea. As a rough estimate, the CIA world factbook reports that there are 22498km of coastline on Earth. If we were to store coordiates for every metre of coastline, using a 32 bit word for each latitude and longitude, this would take less than 1.35GB to store. It's still a lot if this is for a trivial application, but a few orders of magnitude less than using a bitmap. If having such a high degree of accuracy isn't neccessary though, these numbers would drop considerably. Reducing the mapping to only the nearest kilometre would make the bitmap just ~75GB and the coordinates for the world's coastline could fit on a floppy disk.
What I propose is to use a clustering algorithm to decide whether a point is on land or not. We would first need a suitably large number of coordinates that we already know to be on either land or sea. Existing GIS databases would be suitable for this. Then we can analyse the points to determine clusters of land and sea. The decision boundary between the clusters should fall on the coastlines, and all points not determining the decision boundary can be removed. This process can be iterated to give a progressively more accurate boundary.
Only the points determining the decision boundary/the coastline need to be stored, and by using a simple distance metric we can quickly and easily decide if a set of coordinates are on land or sea. A large amount of resources would be required to train the system, but once complete the classifier would require very little space or time.
Assuming Atlantis isn't in the database, you could randomly select cities. This also provides a more realistic distribution of points if you intend to mimic human activity:
https://simplemaps.com/data/world-cities
There's only 7,300 cities in the free version.

Graphics2D - Math plot - Ploygon - how to get all plot points

I've just tried to write "line" code to visualize a simple math;
Here it is
Ploygon polygon=new Ploygon();
int x,y;
ploygon.addPoint(0,0);
polygon.addPoint(width,height);
g.drawPolygon(polygon);
The code gives y=x effect;
OK... it is quite simple code; But the thing I am interested to get is points each N pixels during the statement period as {x0,y0}{0,0} and {x1,y1} {width,height} and that is the problem :(
The polygon xpoints array is not handy because it may contain just the same points which were added when addPoint(x,y) method was invoked; so in my case there just two added points which are connected by Polygon but what about all the rest points which stay between these points {x0,y0}{0,0} and {x1,y1} {width,height} ? How to get them?
For example. Coming back to the previous snippet how to find out what point x,y value is when (height%N)=0 etc?
Is there the most optimal way?
Thanks
What you have to realise here is that you are no longer working with pixels/coordinates per se, but you are working with vectors. You'd get much the same image from a polygon contained the coordinates (-500,-500) and (500,500) which is drawn onto a Graphics object which represents the (clipped) area from (0,0) in the bottom left to (100,100) in the bottom right. (ignoring for now that the actual coordinate system of Graphics has an inverted y-axis).
Therefore you have to solve this in a more back-to-basic's Math way rather than a “read the pixels” way. Unless you just want to determine if a given point is in the shape (for which the Shape interface offers a built-in method), you would be looking at calculating the slope of a line and determining functions which represent your line. For instance continuing from the example you have two points (-500,-500) and (500,500) which gives a slope of 1000/1000 = 1. So you could rewrite that function in terms of your x-coordinates as f(x) = -500 + (x + 500). Then if you want to know if the point (100,200) is on that line all you need to do is calculate f(100) and see that it isn't.
Getting back to your example, finding points which match a predicate (height%N =0), we'd be looking for f(x) == 0 mod N and so 'all' you'd need to do is solve the equation for x.

Categories

Resources