如何计算2个相邻H3 Cells之间的平均距离?
How to calculate the average distance between 2 neighboring H3 Cells?
我正在为 Java https://github.com/uber/h3-java
使用 Uber H3 库
我想知道分辨率为 15 的 2 个相邻 H3 单元之间的平均距离是多少(它们各自中心点之间的距离,或 2 x 中心点)。
根据我尝试计算的方式,我得到了 3 个截然不同的结果:
- 1.148 米
- 0.882 米
- 0.994 米
哪个是正确的?为什么我会得到如此不同的结果?
public static final long RES_15_HEXAGON = 644569124665188486L; // some random res 15 H3 index (as Long)
private final H3Core h3;
H3UtilsTest() throws IOException {
h3 = H3Core.newInstance();
}
@Test
void calculateDistanceBetweenNeighborsFromRealEdges() {
final long h3Index = RES_15_HEXAGON;
final GeoCoord centerPoint = h3.h3ToGeo(h3Index);
assertEquals(46.94862876826281, centerPoint.lat);
assertEquals(7.4404471879141205, centerPoint.lng);
assertEquals(15, h3.h3GetResolution(h3Index));
final List<GeoCoord> hexagon = this.h3.h3ToGeoBoundary(h3Index);
assertEquals(6, hexagon.size());
List<Double> edgeLengths = new ArrayList<>();
for (int i = 0; i < hexagon.size(); i++) {
edgeLengths.add(h3.pointDist(hexagon.get(i), hexagon.get((i + 1) % hexagon.size()), LengthUnit.m));
}
final double apothem = edgeLengths.stream().mapToDouble(Double::doubleValue).average().getAsDouble();
assertEquals(0.5739852101653261, apothem);
final double distanceBetweenNeighbors = 2 * apothem;
assertEquals(1.1479704203306522, distanceBetweenNeighbors);
}
@Test
void calculateDistanceBetweenNeighborsFromAverageEdges() {
final double averageEdgeLength = h3.edgeLength(15, LengthUnit.m);
assertEquals(0.509713273, averageEdgeLength);
assertEquals(1.019426546, 2 * averageEdgeLength);
final double apothem = averageEdgeLength * Math.sqrt(3) / 2;
assertEquals(0.4414246430641128, apothem);
final double distanceBetweenNeighbors = 2 * apothem;
assertEquals(0.8828492861282256, distanceBetweenNeighbors);
}
@Test
void calculateDistanceBetweenNeighborsFromNeighbors() {
final GeoCoord origin = h3.h3ToGeo(RES_15_HEXAGON);
final List<Long> neighbors = h3.kRing(RES_15_HEXAGON, 1);
assertEquals(7, neighbors.size()); // contains the center hexagon as well
neighbors.forEach(neighbor -> assertEquals(6, h3.h3ToGeoBoundary(neighbor).size())); // they are really 6-sided hexagons !
final List<Double> distances = neighbors.stream().filter(neighbor -> neighbor != RES_15_HEXAGON).map(neighbor -> h3.pointDist(origin, h3.h3ToGeo(neighbor), LengthUnit.m)).toList();
assertEquals(6, distances.size());
final Double distanceBetweenNeighbors = distances.stream().mapToDouble(Double::doubleValue).average().getAsDouble();
assertEquals(0.9941567117250641, distanceBetweenNeighbors);
}
我假设由于网格中“六边形”形状的变形,您对这些不同的方法会得到不同的答案。这是您选择的单元格及其邻居:
您可以看到六边形不是完全规则的 - 虽然 H3 试图尽量减少变形,但我们确实有在全球范围内变化的形状变形。单元格大小和形状会根据您选择的采样位置略有不同。
这里的“正确”答案在很大程度上取决于您的用例。我们可能可以计算出整个地球像元中心之间的平均距离,但您实际上可能更关心局部区域。或者平均值可能不是您真正需要的 - 您可能希望在 per-cell 的基础上进行计算。在大多数情况下,我发现最好是:
- 使用细胞中心之间的
pointDist
或 计算感兴趣细胞的实际距离
- 感兴趣区域内的样本,例如应用程序视口,并计算样本的平均值。
为了它的价值,我实现了一种创建更大样本的方法,灵感来自@nrabinowitz 的建议:
public static final long RES_15_HEXAGON = 644569124665188486L;
private final H3Core h3;
H3UtilsTest() throws IOException {
h3 = H3Core.newInstance();
}
@Test
void calculateDistanceBetweenNeighborsUsingLargerSample() {
final Set<Long> origins = new HashSet<>(Set.of(RES_15_HEXAGON));
final Set<Long> visitedOrigins = new HashSet<>();
final Set<Long> nextOrigins = new HashSet<>();
final List<Double> distances = new ArrayList<>();
final int nbIterations = 10;
for (int i = 0; i < nbIterations; i++) {
for (Long origin : origins) {
visitedOrigins.add(origin);
final Set<Long> destinations = new HashSet<>(h3.kRing(origin, 1));
destinations.removeAll(visitedOrigins);
for (Long destination : destinations) {
distances.add(h3.pointDist(h3.h3ToGeo(origin), h3.h3ToGeo(destination), LengthUnit.m));
}
destinations.removeAll(origins); // we don't need the hexagons (anymore) that are on the same ring as origins
nextOrigins.addAll(destinations);
}
origins.clear();
origins.addAll(nextOrigins);
nextOrigins.clear();
}
final double averageDistanceBetweenNeighbors = distances.stream().mapToDouble(Double::doubleValue).average().getAsDouble();
assertEquals(0.9942, averageDistanceBetweenNeighbors, 0.0001);
assertEquals(870, distances.size());
}
我正在为 Java https://github.com/uber/h3-java
使用 Uber H3 库我想知道分辨率为 15 的 2 个相邻 H3 单元之间的平均距离是多少(它们各自中心点之间的距离,或 2 x 中心点)。
根据我尝试计算的方式,我得到了 3 个截然不同的结果:
- 1.148 米
- 0.882 米
- 0.994 米
哪个是正确的?为什么我会得到如此不同的结果?
public static final long RES_15_HEXAGON = 644569124665188486L; // some random res 15 H3 index (as Long)
private final H3Core h3;
H3UtilsTest() throws IOException {
h3 = H3Core.newInstance();
}
@Test
void calculateDistanceBetweenNeighborsFromRealEdges() {
final long h3Index = RES_15_HEXAGON;
final GeoCoord centerPoint = h3.h3ToGeo(h3Index);
assertEquals(46.94862876826281, centerPoint.lat);
assertEquals(7.4404471879141205, centerPoint.lng);
assertEquals(15, h3.h3GetResolution(h3Index));
final List<GeoCoord> hexagon = this.h3.h3ToGeoBoundary(h3Index);
assertEquals(6, hexagon.size());
List<Double> edgeLengths = new ArrayList<>();
for (int i = 0; i < hexagon.size(); i++) {
edgeLengths.add(h3.pointDist(hexagon.get(i), hexagon.get((i + 1) % hexagon.size()), LengthUnit.m));
}
final double apothem = edgeLengths.stream().mapToDouble(Double::doubleValue).average().getAsDouble();
assertEquals(0.5739852101653261, apothem);
final double distanceBetweenNeighbors = 2 * apothem;
assertEquals(1.1479704203306522, distanceBetweenNeighbors);
}
@Test
void calculateDistanceBetweenNeighborsFromAverageEdges() {
final double averageEdgeLength = h3.edgeLength(15, LengthUnit.m);
assertEquals(0.509713273, averageEdgeLength);
assertEquals(1.019426546, 2 * averageEdgeLength);
final double apothem = averageEdgeLength * Math.sqrt(3) / 2;
assertEquals(0.4414246430641128, apothem);
final double distanceBetweenNeighbors = 2 * apothem;
assertEquals(0.8828492861282256, distanceBetweenNeighbors);
}
@Test
void calculateDistanceBetweenNeighborsFromNeighbors() {
final GeoCoord origin = h3.h3ToGeo(RES_15_HEXAGON);
final List<Long> neighbors = h3.kRing(RES_15_HEXAGON, 1);
assertEquals(7, neighbors.size()); // contains the center hexagon as well
neighbors.forEach(neighbor -> assertEquals(6, h3.h3ToGeoBoundary(neighbor).size())); // they are really 6-sided hexagons !
final List<Double> distances = neighbors.stream().filter(neighbor -> neighbor != RES_15_HEXAGON).map(neighbor -> h3.pointDist(origin, h3.h3ToGeo(neighbor), LengthUnit.m)).toList();
assertEquals(6, distances.size());
final Double distanceBetweenNeighbors = distances.stream().mapToDouble(Double::doubleValue).average().getAsDouble();
assertEquals(0.9941567117250641, distanceBetweenNeighbors);
}
我假设由于网格中“六边形”形状的变形,您对这些不同的方法会得到不同的答案。这是您选择的单元格及其邻居:
您可以看到六边形不是完全规则的 - 虽然 H3 试图尽量减少变形,但我们确实有在全球范围内变化的形状变形。单元格大小和形状会根据您选择的采样位置略有不同。
这里的“正确”答案在很大程度上取决于您的用例。我们可能可以计算出整个地球像元中心之间的平均距离,但您实际上可能更关心局部区域。或者平均值可能不是您真正需要的 - 您可能希望在 per-cell 的基础上进行计算。在大多数情况下,我发现最好是:
- 使用细胞中心之间的
pointDist
或 计算感兴趣细胞的实际距离
- 感兴趣区域内的样本,例如应用程序视口,并计算样本的平均值。
为了它的价值,我实现了一种创建更大样本的方法,灵感来自@nrabinowitz 的建议:
public static final long RES_15_HEXAGON = 644569124665188486L;
private final H3Core h3;
H3UtilsTest() throws IOException {
h3 = H3Core.newInstance();
}
@Test
void calculateDistanceBetweenNeighborsUsingLargerSample() {
final Set<Long> origins = new HashSet<>(Set.of(RES_15_HEXAGON));
final Set<Long> visitedOrigins = new HashSet<>();
final Set<Long> nextOrigins = new HashSet<>();
final List<Double> distances = new ArrayList<>();
final int nbIterations = 10;
for (int i = 0; i < nbIterations; i++) {
for (Long origin : origins) {
visitedOrigins.add(origin);
final Set<Long> destinations = new HashSet<>(h3.kRing(origin, 1));
destinations.removeAll(visitedOrigins);
for (Long destination : destinations) {
distances.add(h3.pointDist(h3.h3ToGeo(origin), h3.h3ToGeo(destination), LengthUnit.m));
}
destinations.removeAll(origins); // we don't need the hexagons (anymore) that are on the same ring as origins
nextOrigins.addAll(destinations);
}
origins.clear();
origins.addAll(nextOrigins);
nextOrigins.clear();
}
final double averageDistanceBetweenNeighbors = distances.stream().mapToDouble(Double::doubleValue).average().getAsDouble();
assertEquals(0.9942, averageDistanceBetweenNeighbors, 0.0001);
assertEquals(870, distances.size());
}