4

I have data like this:

23.3445556 72.4535455 0.23434
23.3645556 72.4235455 0.53434
23.3245556 72.4635455 0.21434
23.3645556 72.2535455 0.25434

I want to make HashMap like this:

HashMap<23.34444,72.23455,0.2345566> demo = new HashMap()

Here 23.34444,72.23455 is a key and 0.2345566 is value.

This is because I want to traverse HashMap like this:

if(demo.latitude < 21.45454545 && demo.longitude > 72.3455)
    //get the value from hashMap   

long lat repn particular pixel on the map,each pixel have same value , i want to get avg value from particular area suppose x y and pixel will be upto 1 million

  • And i want to know does this is good way since daily it will get millions hit
Gaurav Singh
  • 343
  • 1
  • 3
  • 11

6 Answers6

3

You could use the Point class to start off.

https://docs.oracle.com/javase/7/docs/api/java/awt/Point.html

int xE6 = x*1e6
int yE6 = y*1e6
new Point(xE6, yE6)

But since this is awt specific and a misuse of the class, you will probably eventually want to create your own.

public final class LatLon {
    private double lat;
    private double lon;

    public LatLon(double lat, double lon) {
        this.lat = lat;
        this.lon = lon;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        LatLon latLon = (LatLon) o;

        if (Double.compare(latLon.lat, lat) != 0) return false;
        return Double.compare(latLon.lon, lon) == 0;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(lat);
        result = (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(lon);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    public double getLat() {
        return lat;
    }

    public void setLat(double lat) {
        this.lat = lat;
    }

    public double getLon() {
        return lon;
    }

    public void setLon(double lon) {
        this.lon = lon;
    }
}

(autogenerated using IntelliJ)

This can be used like

public static void main(String[] args) {
    HashMap<LatLon, Double> demo = new HashMap<LatLon, Double>();
    demo.put(new LatLon(23.3445556,72.4535455), 0.23434);
    demo.put(new LatLon(23.3645556,72.4235455), 0.53434);
    demo.put(new LatLon(23.3245556,72.4635455), 0.21434);
    demo.put(new LatLon(23.3645556,72.2535455), 0.25434);
    System.out.println(demo.get(new LatLon(23.3645556,72.2535455))); //0.25434
}

The problem with using this class as is, is that it uses doubles. You want some sort of precision, given by the decimal location.

doubles have strange math, and can give you accuracy errors so I heartily recommend using a library designed for geo-coordinates.

Especially given

if(demo.latitude<21.45454545 && demo.longitude >72.3455)

This sort of check is best served by some sort of purpose built collection for dealing with bounds checks and co-ordinates if you end up hitting performance problems.

Ryan Leach
  • 4,262
  • 5
  • 34
  • 71
  • The AWT point is for representing coordinates on screen, in pixels. Hence, the coordinates are `int`, not `double` as would be needed for lat/long. Also, `x*10^6` is probably not doing what you think it does. – tobias_k Jun 13 '17 at 07:57
  • I changed it to double, mainly because I'm too lazy to create an Int based answer that converts to and from intE6 location format for this question. – Ryan Leach Jun 13 '17 at 08:26
2

If it's a demo you're creating I would suggest creating an enum class with each coordinate you want to showcase as a separate enum object or as a key to the HashMap.

If that doesn't work for you I would create a "Coordinates" class and store the keys there. You would have to override the hashcode and equals method though or it might not behave like you want it to.

Example

public class Coordinates {
    double latitude, longitude;
}
...
HashMap<Coordinates, Double> demo = new HashMap<>(); /* Note: An object of Coordinates is the key. So, you first have to make an object of Coordinates class, put the latitude and longitude values and then put in the HashMap as key.*/
kiner_shah
  • 3,939
  • 7
  • 23
  • 37
Ted Cassirer
  • 364
  • 1
  • 10
  • 3
    that class is incomplete to be used in an HashMap – AxelH Jun 13 '17 at 07:59
  • What do you mean by "class is incomplete"? – kiner_shah Jun 13 '17 at 08:05
  • 1
    He means you also need to have a `hashCode` method in order for a class to correctly work as a `HashMap` key – PentaKon Jun 13 '17 at 08:09
  • 1
    they mentions it needing an equality and hashcode method, but doesn't provide one. They are absolutely necessary for correct hashmap behaviour, if you arn't basing off of reference equality. – Ryan Leach Jun 13 '17 at 08:09
  • 1
    Read [Why do I need to override the equals and hashCode methods in Java?](https://stackoverflow.com/questions/2265503/why-do-i-need-to-override-the-equals-and-hashcode-methods-in-java?noredirect=1&lq=1) – AxelH Jun 13 '17 at 08:10
2

I think you are approaching the problem the wrong way. Using a HashMap will not work correctly with greater than or lesser than comparisons. What would happen if you had 2 latlong keys that matched your comparison? What value do you choose?

I would probably solve your problem like this:

First, create a class that will contain both your "key" values and your "value" value

public class GeoValue {
  double lat;
  double lon;
  double value;
}

Then, add a comparison method to the class

public boolean lessThanLatGreaterThanLon(double lat, double lon) {
  return lat < this.lat && lon > this.lon;
}

Add all of those created objects to a Set type collection. If you use a HashSet, make sure that you also override .equals() and .hashCode methods for your GeoValue class.

To find the values you want you can use a filter method (if you're in Java8 or example)

final double lat = 3.5D;
final double lon = 4.5D;
Set<GeoValue> matchingValues = geoValues.stream()
    .filter(geo -> geo.lessThanLatGreaterThanLon(lat, lon))
    .collect(Collectors.toSet());

And you're ready to go.

PentaKon
  • 4,139
  • 5
  • 43
  • 80
2

HashMap won't be of use for your needs, because it's not meant for range queries, i.e. give me the entry whose key is closest to 12.0, or give me all entries between keys 10.0 and 20.0.

There are special-purpose structures that deal with geo points efficiently, i.e. R-tree or R* tree.

These kind of trees require you to index your data based on a geo-point like structure, usually a latitude/longitude pair, though they also allow to index data based on geo-shapes.

Creating a lat/lon pair object to be used as the key (as suggested in other answers) is only useful if you use a specialized structure that stores and indexes spatial data. Otherwise, having such pair will be pointless, because you won't be able to search points that are near a given location, or points that lie within a given rectangle, etc.


Now, if you don't want to go the R-tree's way and you can live with quite limited spatial queries, you might want to consider using the following structure:

TreeMap<Double, TreeMap<Double, Double>> demo = new TreeMap<>();

This is a TreeMap of TreeMaps, and the idea is to have the latitude as the key of the outer map and the longitude as the key of the inner maps. So you will always have to search first by latitude, then by longitude.

If this is OK for you, you can take advantage of some very useful methods of TreeMap, such as headMap, tailMap and subMap, to name the most relevant ones.

For example, if you want to find all the points within the rectangle determined by its upper left corner [-10.0, -10.0] and its lower right corner [10.0, 10.0], you could do it as follows:

// Get all points with latitude between -10.0 and 10.0
SortedMap<Double, TreeMap<Double, Double>> byLat = demo.subMap(-10.0, 10.0);

// Now print points from byLat submap with longitude between -10.0 and 10.0
byLat.entrySet().stream()
    .map(e -> e.getValue().subMap(-10.0, 10.0))
    .forEach(System.out::println);

Even for 1 million points, performance will be reasonable, though not the best one, because TreeMap is a general purpose Map implementation based on a Red/Black tree, which has O(log n) time complexity.


On the other hand, if you are willing to install some software, I recommend you use Elasticsearch with Geolocation. It has geo-point and geo-shaped specialized datatypes that will make your life easier. This search engine has excellent performance and scales horizontally up to thousands of nodes, so memory, lookup times, etc won't be a problem.

fps
  • 33,623
  • 8
  • 55
  • 110
0

You can generate a hashcode based on the longitude,latitude and then use that hashcode as key for save your values. That way it will be simpler instead of using them directly or converting them into a point as there is no use of the point at later point of time.

Bhanu Boppana
  • 134
  • 1
  • 2
  • 10
  • You are assuming you don't need them later. Since it is using both value in the condition, I would use an instance instead of a generate hashcode like this – AxelH Jun 13 '17 at 08:01
  • How is this simpler than making a `Point` class that uses the same logic in its `hashcode` method? Using the hash code directly, it is impossible to convert the keys from the map back to coordinates. – tobias_k Jun 13 '17 at 08:01
0

You can also make use of Point2D class available as part of java.awt. You will need to extend it and make a concrete class but it will give you equals/hashcode, etc. all built in. For integer coordinates you could just use the Point class from same library (no need to extend as well)

Kumar Vaibhav
  • 2,632
  • 8
  • 32
  • 54