Search code examples
javagisgeocoding

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.


Solution

  • 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:

    1. This assumes the shapefile you got is polygon data. Intersection on lines or points isn't going to give you what you want.
    2. First section opens the shapefile - nothing interesting
    3. you have to fetch the geometry property name for the given file
    4. 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.
    5. 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.