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);
// ...
}
这是为每次迭代计算强度的方法,对吧?如果是这样,我怎么能改变它以达到我想要的效果(平均值,而不是总结颜色,我认为这与强度成正比)。
那么:如何通过修改框架来平均而不是求和强度?
答案 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