在Java中将经度和纬度作为HashMap的键

4
我有这样的数据:
23.3445556 72.4535455 0.23434
23.3645556 72.4235455 0.53434
23.3245556 72.4635455 0.21434
23.3645556 72.2535455 0.25434

我希望能创建一个像这样的 HashMap
HashMap<23.34444,72.23455,0.2345566> demo = new HashMap()

这里的 23.34444,72.23455 是一个键,0.2345566 是对应的值。

原因是我想要像这样遍历 HashMap

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

每个像素在地图上都有其特定的经纬度表示,每个像素具有相同的值,我想从特定区域(假设为x y)获取平均值,并且该区域中的像素数量将达到100万个。

  • 我想知道这是否是一个好方法,因为每天都会有数百万次请求。

3
将此内容翻译为中文:将 23.34444,72.23455 转换为一个点类。 - ΦXocę 웃 Пepeúpa ツ
1
听起来你想要一个代表经纬度值的类... - Jon Skeet
请将经度和纬度值存储在浮点型数据库列中。这样可以确保精度,并且可以轻松地进行计算和查询。建议使用DOUBLE类型的列来存储这些值。 - user7294900
2
请注意,HashMap是为“相等性”比较而设计的,而不是您在末尾展示的那种比较。 - Jon Skeet
1
我认为你不能按照你的意图使用 HashMap。如果有两个键都符合你的 if 条件,那么会发生什么?你会选择哪个值? - PentaKon
显示剩余4条评论
6个回答

3
你可以使用Point类开始操作。 https://docs.oracle.com/javase/7/docs/api/java/awt/Point.html
int xE6 = x*1e6
int yE6 = y*1e6
new Point(xE6, yE6)

但是由于这是awt特定的且不正确使用该类,您最终可能会想要创建自己的类。

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;
    }
}

(使用IntelliJ自动生成)

这可以像这样使用

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
}

使用该类的问题在于它使用双精度浮点数,而您需要一定精度,由小数位数给出。
双精度浮点数具有奇怪的数学性质,并且可能会导致精度误差,因此我强烈建议使用专为地理坐标设计的库。
尤其是考虑到
如果(demo.latitude<21.45454545 && demo.longitude>72.3455)
这种检查最好使用某些专门用于处理边界检查和坐标的集合,以防止性能问题。

AWT点是用于表示屏幕上的像素坐标。因此,这些坐标是int类型的,而不是用于纬度/经度所需的double类型。另外,x*10^6可能不是您想要的操作。 - tobias_k
我将其更改为double,主要是因为我太懒了,不想创建一个基于Int的答案,该答案需要在intE6位置格式和int之间进行转换。 - Ryan Leach

2

如果你正在创建一个演示文稿,我建议创建一个枚举类,其中包含您想展示的每个坐标作为单独的枚举对象或作为HashMap的键。

如果这对您不起作用,我会创建一个“Coordinates”类并在那里存储键。然而,您必须重写hashCode和equals方法,否则它可能无法像您希望的那样运行。

例如

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.*/

3
该类不完整,不能在 HashMap 中使用。 - AxelH
“class is incomplete” 是什么意思? - kiner_shah
1
他的意思是,如果一个类要作为HashMap的键正确地工作,你还需要拥有一个hashCode方法。 - PentaKon
1
他们提到需要一个相等性和哈希码方法,但没有提供。如果您不是基于引用相等性,那么它们对于正确的哈希映射行为绝对必要。 - Ryan Leach

2

我认为你的方法不太正确。使用HashMap在大于或小于比较时无法正常工作。如果有两个符合比较条件的经纬度键,你会选择哪个值?

我可能会这样解决你的问题:

首先,创建一个类,该类将包含你的“键”值和“值”值。

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

然后,将一个比较方法添加到该类中。
public boolean lessThanLatGreaterThanLon(double lat, double lon) {
  return lat < this.lat && lon > this.lon;
}

将所有创建的对象添加到一个Set类型集合中。如果您使用HashSet,请确保您还覆盖了GeoValue类的.equals().hashCode方法。
要查找您想要的值,可以使用filter方法(如果您使用的是Java8或更高版本)。
final double lat = 3.5D;
final double lon = 4.5D;
Set<GeoValue> matchingValues = geoValues.stream()
    .filter(geo -> geo.lessThanLatGreaterThanLon(lat, lon))
    .collect(Collectors.toSet());

现在,你已经准备就绪了。


2

HashMap对您的需求没有用处,因为它不适用于范围查询,即给我最接近12.0的键值对,或者给我所有在10.020.0之间的键值对。

有一些专门处理地理点的数据结构,如R-treeR* tree

这些树需要您基于类似地理点的结构索引数据,通常是一个纬度/经度对,尽管它们也允许基于地理形状索引数据。

创建一个用作键的纬度/经度对对象(如其他答案中建议的)只有在使用存储和索引空间数据的专门结构时才有用。否则,拥有这样的键对将毫无意义,因为您将无法搜索靠近给定位置或位于给定矩形内的点等。


现在,如果您不想使用R树的方式,并且可以接受相当有限的空间查询,您可能希望考虑使用以下结构:
TreeMap<Double, TreeMap<Double, Double>> demo = new TreeMap<>();

这是一个由TreeMap组成的TreeMap,其思路是将纬度作为外部映射的键,将经度作为内部映射的键。所以您始终需要先按纬度搜索,然后再按经度搜索
如果这对您来说没问题,您可以利用一些非常有用的TreeMap方法,比如headMaptailMapsubMap等,其中最相关的方法。
例如,如果您想查找所有位于由其左上角[-10.0,-10.0]和其右下角[10.0,10.0]确定的矩形内的点,则可以执行以下操作:
// 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);

即使是100万个点,性能也会是合理的,尽管不是最好的,因为TreeMap是一个通用的Map实现,基于红黑树,其时间复杂度为O(log n)。
另一方面,如果您愿意安装一些软件,我建议您使用ElasticsearchGeolocation。它具有地理点地理形状专门的数据类型,可以让您的生活更轻松。这个搜索引擎拥有出色的性能,并且可以水平扩展到数千个节点,因此内存、查找时间等都不会成为问题。

0

您可以基于经度和纬度生成哈希码,然后使用该哈希码作为键来保存您的值。这样做会更简单,而不是直接使用它们或将它们转换为点,因为在以后的时间点上没有使用点。


你假设以后不需要它们。由于条件中使用了两个值,我会使用一个实例而不是生成哈希码。 - AxelH
这比创建一个使用相同逻辑的“Point”类并在其“hashcode”方法中使用它要简单吗?直接使用哈希码,无法将地图中的键转换回坐标。 - tobias_k

0
你也可以利用Java.awt的Point2D类。您需要扩展它并创建具体的类,但它将为您提供equals/hashcode等所有内置功能。对于整数坐标,您可以直接使用同一库中的Point类(无需进行扩展)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接