谷歌地图热图颜色的平均重量

时间:2018-01-27 06:44:38

标签: ios objective-c google-maps heatmap

Google Maps iOS SDK的热图(更具体地说是Google-Maps-iOS-Utils framework)主要通过计算该区域中点的密度来决定渲染区域的颜色。

但是,我想根据该区域中点的平均重量或强度来选择颜色。

根据我的理解,这种行为不是内置的(但谁知道 - 文档很糟糕)。确定颜色选择的文件是{em>我认为在/src/Heatmap/GMUHeatmapTileLayer.m这是一个相对较短的文件,但我对Objective-C不是很精通,所以我遇到了一些困难搞清楚什么是什么。我认为-tileForX:y:zoom:中的GMUHeatmapTileLayer.m是重要的功能,但我不确定,即使它是,我也不知道如何修改它。在此方法结束时,数据首先水平“卷积”,然后垂直“卷积”。我认为这是实际计算强度的地方。不幸的是,我不确切地知道它在做什么,我害怕改变事情,因为我吮吸obj-c。这就是这个方法的卷积部分:

- (UIImage *)tileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom {

  // ...

  // Convolve data.
  int lowerLimit = (int)data->_radius;
  int upperLimit = paddedTileSize - (int)data->_radius - 1;
  // Convolve horizontally first.
  float *intermediate = calloc(paddedTileSize * paddedTileSize, sizeof(float));
  for (int y = 0; y < paddedTileSize; y++) {
    for (int x = 0; x < paddedTileSize; x++) {
      float value = intensity[y * paddedTileSize + x];
      if (value != 0) {
        // convolve to x +/- radius bounded by the limit we care about.
        int start = MAX(lowerLimit, x - (int)data->_radius);
        int end = MIN(upperLimit, x + (int)data->_radius);
        for (int x2 = start; x2 <= end; x2++) {
          float scaledKernel = value * [data->_kernel[x2 - x + data->_radius] floatValue];
          // I THINK THIS IS WHERE I NEED TO MAKE THE CHANGE
          intermediate[y * paddedTileSize + x2] += scaledKernel;
          // ^
        }
      }
    }
  }
  free(intensity);
  // Convole vertically to get final intensity.
  float *finalIntensity = calloc(kGMUTileSize * kGMUTileSize, sizeof(float));
  for (int x = lowerLimit; x <= upperLimit; x++) {
    for (int y = 0; y < paddedTileSize; y++) {
      float value = intermediate[y * paddedTileSize + x];
      if (value != 0) {
        int start = MAX(lowerLimit, y - (int)data->_radius);
        int end = MIN(upperLimit, y + (int)data->_radius);
        for (int y2 = start; y2 <= end; y2++) {
          float scaledKernel = value * [data->_kernel[y2 - y + data->_radius] floatValue];
          // I THINK THIS IS WHERE I NEED TO MAKE THE CHANGE
          finalIntensity[(y2 - lowerLimit) * kGMUTileSize + x - lowerLimit] += scaledKernel;
          // ^
        }
      }
    }
  }
  free(intermediate);

  // ...

}

这是为每次迭代计算强度的方法,对吧?如果是这样,我怎么能改变它以达到我想要的效果(平均值,而不是总结颜色,我认为这与强度成正比)。

那么:如何通过修改框架来平均而不是求和强度?

2 个答案:

答案 0 :(得分:1)

我认为你走在正确的轨道上。要计算平均值,请将点总和除以点数。由于您已经计算了总和,我认为一个简单的解决方案是保存每个点的计数。如果我理解正确,这就是你必须要做的。

为总和分配内存时,还要为计数分配内存

// At this place
float *intermediate = calloc(paddedTileSize * paddedTileSize, sizeof(float));
// Add this line, calloc will initialize them to zero
int *counts = calloc(paddedTileSize * paddedTileSize, sizeof(int));

然后增加每个循环中的计数。

// Below this line (first loop)
intermediate[y * paddedTileSize + x2] += scaledKernel;
// Add this
counts[y * paddedTileSize + x2]++;

// And below this line (second loop)
finalIntensity[(y2 - lowerLimit) * kGMUTileSize + x - lowerLimit] += scaledKernel;
// Add this
counts[(y2 - lowerLimit) * kGMUTileSize + x - lowerLimit]++;

在两个循环之后,您应该有两个数组,一个是您的总和finalIntensity,另一个是您的计数counts。现在查看数值并计算平均值。

for (int y = 0; y < paddedTileSize; y++) {
    for (int x = 0; x < paddedTileSize; x++) {
        int n = y * paddedTileSize + x;
        if (counts[n] != 0)
            finalIntensity[n] = finalIntensity[n] / counts[n];
    }
}
free(counts);

finalIntensity现在应该包含您的平均值。

如果您愿意,并且其余代码使其成为可能,您可以跳过最后一个循环,而是在使用最终强度值时进行除法。只需将后续的finalIntensity[n]更改为counts[n] == 0 ? finalIntensity[n] : finalIntensity[n] / counts[n]

答案 1 :(得分:0)

我可能刚刚为java版本解决了同样的问题。

我的问题是拥有12个不同值的自定义渐变。 但是我的实际加权数据不一定包含从1到12的所有强度值。

问题是,最高强度值被映射到最高颜色。 此外,附近强度为1的10个数据点将获得与强度为12的单个点相同的颜色。

因此创建图块的功能是一个很好的起点:

爪哇:

public Tile getTile(int x, int y, int zoom) {
// ...
// Quantize points
    int dim = TILE_DIM + mRadius * 2;
    double[][] intensity = new double[dim][dim];
    int[][] count = new int[dim][dim];
    for (WeightedLatLng w : points) {
        Point p = w.getPoint();
        int bucketX = (int) ((p.x - minX) / bucketWidth);
        int bucketY = (int) ((p.y - minY) / bucketWidth);
        intensity[bucketX][bucketY] += w.getIntensity();
        count[bucketX][bucketY]++;
    }
    // Quantize wraparound points (taking xOffset into account)
    for (WeightedLatLng w : wrappedPoints) {
        Point p = w.getPoint();
        int bucketX = (int) ((p.x + xOffset - minX) / bucketWidth);
        int bucketY = (int) ((p.y - minY) / bucketWidth);
        intensity[bucketX][bucketY] += w.getIntensity();
        count[bucketX][bucketY]++;
    }
    for(int bx = 0; bx < dim; bx++)
        for (int by = 0; by < dim; by++)
            if (count[bx][by] != 0)
                intensity[bx][by] /= count[bx][by];
//...

我添加了一个计数器并计算每个强度的加法,之后我会检查每个强度并计算平均值。

对于C:

- (UIImage *)tileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom {
//...
// Quantize points.
    int paddedTileSize = kGMUTileSize + 2 * (int)data->_radius;
    float *intensity = calloc(paddedTileSize * paddedTileSize, sizeof(float));
    int *count = calloc(paddedTileSize * paddedTileSize, sizeof(int));
    for (GMUWeightedLatLng *item in points) {
        GQTPoint p = [item point];
        int x = (int)((p.x - minX) / bucketWidth);
        // Flip y axis as world space goes south to north, but tile content goes north to south.
        int y = (int)((maxY - p.y) / bucketWidth);
        // If the point is just on the edge of the query area, the bucketing could put it outside
        // bounds.
        if (x >= paddedTileSize) x = paddedTileSize - 1;
        if (y >= paddedTileSize) y = paddedTileSize - 1;
        intensity[y * paddedTileSize + x] += item.intensity;
        count[y * paddedTileSize + x] ++;
    }
    for (GMUWeightedLatLng *item in wrappedPoints) {
        GQTPoint p = [item point];
        int x = (int)((p.x + wrappedPointsOffset - minX) / bucketWidth);
        // Flip y axis as world space goes south to north, but tile content goes north to south.
        int y = (int)((maxY - p.y) / bucketWidth);
        // If the point is just on the edge of the query area, the bucketing could put it outside
        // bounds.
        if (x >= paddedTileSize) x = paddedTileSize - 1;
        if (y >= paddedTileSize) y = paddedTileSize - 1;
        // For wrapped points, additional shifting risks bucketing slipping just outside due to
        // numerical instability.
        if (x < 0) x = 0;
        intensity[y * paddedTileSize + x] += item.intensity;
        count[y * paddedTileSize + x] ++;
    }
    for(int i=0; i < paddedTileSize * paddedTileSize; i++)
        if (count[i] != 0)
            intensity[i] /= count[i];

接下来就是卷入其中。 我在那里做的是确保计算值不超过我的数据中的最大值。

爪哇:

// Convolve it ("smoothen" it out)
double[][] convolved = convolve(intensity, mKernel, mMaxAverage);

// the mMaxAverage gets set here:
public void setWeightedData(Collection<WeightedLatLng> data) {
// ...
    // Add points to quad tree
    for (WeightedLatLng l : mData) {
        mTree.add(l);
        mMaxAverage = Math.max(l.getIntensity(), mMaxAverage);
    }
// ...
// And finally the convolve method:
static double[][] convolve(double[][] grid, double[] kernel, double max) {
// ...
    intermediate[x2][y] += val * kernel[x2 - (x - radius)];
    if (intermediate[x2][y] > max) intermediate[x2][y] = max;
// ...
    outputGrid[x - radius][y2 - radius] += val * kernel[y2 - (y - radius)];
    if (outputGrid[x - radius][y2 - radius] > max ) outputGrid[x - radius][y2 - radius] = max;

对于C:

// To get the maximum average you could do that here:
- (void)setWeightedData:(NSArray<GMUWeightedLatLng *> *)weightedData {
    _weightedData = [weightedData copy];
    for (GMUWeightedLatLng *dataPoint in _weightedData)
        _maxAverage = Math.max(dataPoint.intensity, _maxAverage)
// ...
// And then simply in the convolve section
    intermediate[y * paddedTileSize + x2] += scaledKernel;
    if (intermediate[y * paddedTileSize + x2] > _maxAverage) 
        intermediate[y * paddedTileSize + x2] = _maxAverage;
// ...
   finalIntensity[(y2 - lowerLimit) * kGMUTileSize + x - lowerLimit] += scaledKernel;
   if (finalIntensity[(y2 - lowerLimit) * kGMUTileSize + x - lowerLimit] > _maxAverage)
       finalIntensity[(y2 - lowerLimit) * kGMUTileSize + x - lowerLimit] = _maxAverage;

最后着色

爪哇:

// The maximum intensity is simply the size of my gradient colors array (or the starting points) 
Bitmap bitmap = colorize(convolved, mColorMap, mGradient.mStartPoints.length);

对于C:

// Generate coloring
// ...
float max = [data->_maxIntensities[zoom] floatValue];
max = _gradient.startPoints.count;

我在Java中这样做了,它对我有用,但不确定C代码。

你必须玩半径,你甚至可以编辑内核。因为我发现当我有很多同质数据(即强度变化很小,或者一般来说很多数据)时,热图会退化为单色叠加,因为边缘上的渐变会变小小。

但希望无论如何这都有帮助。

// Erik