*
* rasterize_hash.cpp
*
* Copyright (C) 2021-2024 SuperMap Software Co., Ltd.
*
* Yukon is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; If not, see <http://www.gnu.org/licenses/>. *
*/
#include "postgres.h"
#include "fmgr.h"
#include <cmath>
#include "RasterizeHash.h"
* HashRasterize 函数修改自 rt_raster.c 中的 rt_raster_gdal_rasterize 函数
* 修改:
* 1. 因 _rti_rasterize_arg_t 中原有的指针部分已修改为基本类型,无需释放内存,移除本函数中的释放内存函数
* 2. 移除一些不需要的参数
* 3. 修改 arg 作为参数的函数调用
* 4. 移除 width 和 height 相关代码
* 5. 移除 skew_x 和 skew_y 相关代码
* 6. 移除因旋转产生的重新计算 extent 相关代码
* 7. 移除 scale_x scale_y 的相关代码,scale_x scale_y 在函数内计算
* 8. 移除 ul_xw,ul_yw 相关代码,由 global_extent 指定
*/
* Return a raster of the provided geometry
*
* @param wkb : WKB representation of the geometry to convert
* @param wkb_len : length of the WKB representation of the geometry
* @param srs : the geometry's coordinate system in OGC WKT
* @param global_extent 全局 hash 范围
* @param level hash 划分等级
* @param data : 栅格化后的原始数据
*/
void HashRasterize(
const unsigned char *wkb, uint32_t wkb_len,
const char *srs,
const Yk::YkRect2D global_extent,
const unsigned int level, RawHashData &data)
{
int i = 0;
int err = 0;
int _dim[2] = {0};
double _scale[2] = {0};
double _skew[2] = {0};
uint8_t noband = 0;
uint32_t numbands = 1;
rt_pixtype pixtype = (rt_pixtype)4;
double init = 0;
double nodata = 0;
uint8_t hasnodata = 1;
double value = 1;
int bandlist = 1;
OGRSpatialReferenceH *src_sr = nullptr;
char **options = nullptr;
options = (char **)palloc(sizeof(char *) * 1);
options[0] = palloc(sizeof(char *) * (strlen("ALL_TOUCHED=TRUE") + 1));
options[0] = "ALL_TOUCHED=TRUE";
options = (char **)repalloc(options, sizeof(char *) * 2);
options[1] = nullptr;
OGRErr ogrerr;
OGRGeometryH src_geom;
OGREnvelope src_env;
rt_envelope extent;
rt_envelope subextent;
OGRwkbGeometryType wkbtype = wkbUnknown;
int ul_user = 0;
CPLErr cplerr;
double _gt[6] = {0};
GDALDriverH _drv = nullptr;
int unload_drv = 0;
GDALDatasetH _ds = nullptr;
GDALRasterBandH _band = nullptr;
uint16_t _width = 0;
uint16_t _height = 0;
if (nullptr == wkb)
{
rterror("rasterize_hash: wkb cannot be nullptr.");
}
if (0 == wkb_len)
{
rterror("rasterize_hash: wkb_len cannot be 0.");
}
if (!global_extent.IsValid())
{
rterror("rasterize_hash: global_extent is invalid.");
}
if (nullptr != srs && strlen(srs))
{
src_sr = OSRNewSpatialReference(nullptr);
if (OSRSetFromUserInput(src_sr, srs) != OGRERR_NONE)
{
rterror("rasterize_hash: Could not create OSR spatial reference using the provided srs: %s", srs);
return;
}
}
ogrerr = OGR_G_CreateFromWkb((unsigned char *)wkb, src_sr, &src_geom, wkb_len);
delete wkb;
wkb = nullptr;
if (ogrerr != OGRERR_NONE)
{
rterror("rasterize_hash: Could not create OGR Geometry from WKB");
return;
}
if (OGR_G_IsEmpty(src_geom))
{
rtinfo("Geometry provided is empty. Returning empty raster");
OGR_G_DestroyGeometry(src_geom);
return;
}
_dim[0] = (std::pow(2, level));
_dim[1] = (std::pow(2, level));
_scale[0] = (global_extent.right - global_extent.left) / _dim[0];
_scale[1] = (global_extent.top - global_extent.bottom) / _dim[1];
OGR_G_GetEnvelope(src_geom, &src_env);
rt_util_from_ogr_envelope(src_env, &extent);
subextent.MinX = floor((extent.MinX - global_extent.left) / _scale[0]) * _scale[0] + global_extent.left;
subextent.MinY = floor((extent.MinY - global_extent.bottom) / _scale[1]) * _scale[1] + global_extent.bottom;
subextent.MaxX = ceil((extent.MaxX - global_extent.left) / _scale[0]) * _scale[0] + global_extent.left;
subextent.MaxY = ceil((extent.MaxY - global_extent.bottom) / _scale[1]) * _scale[1] + global_extent.bottom;
data.offsetx = floor(subextent.MinX / _scale[0] - global_extent.left / _scale[0] + 0.1);
data.offsety = floor(subextent.MinY / _scale[1] - global_extent.bottom / _scale[1] + 0.1);
RASTER_DEBUGF(3, "scale (x, y) = %f, %f", _scale[0], -1 * _scale[1]);
RASTER_DEBUGF(3, "dim (x, y) = %d, %d", _dim[0], _dim[1]);
if (!rt_util_gdal_driver_registered("MEM"))
{
RASTER_DEBUG(4, "Registering MEM driver");
GDALRegister_MEM();
unload_drv = 1;
}
_drv = GDALGetDriverByName("MEM");
if (nullptr == _drv)
{
rterror("rasterize_hash: Could not load the MEM GDAL driver");
OGR_G_DestroyGeometry(src_geom);
return;
}
if (unload_drv)
{
RASTER_DEBUG(4, "Deregistering MEM driver");
GDALDeregisterDriver(_drv);
}
_dim[0] = ceil((subextent.MaxX - subextent.MinX) / _scale[0]);
_dim[1] = ceil((subextent.MaxY - subextent.MinY) / _scale[1]);
data.width = _dim[0];
data.height = _dim[1];
* @brief 设置变形矩阵
* 如果 _gt[3] 为 miny 则 _gt[5] = _scale[1] raster 数据与图形方向相反
* 如果 _gt[3] 为 maxy 则 _gt[5] = - _scale[1], 数据与图形方向相同
*
*/
_gt[0] = subextent.MinX;
_gt[1] = _scale[0];
_gt[2] = 0;
_gt[3] = subextent.MinY;
_gt[4] = 0;
_gt[5] = _scale[1];
_ds = GDALCreate(_drv, "", _dim[0], _dim[1], 0, GDT_Byte, nullptr);
if (nullptr == _ds)
{
rterror("rasterize_hash: Could not create a GDALDataset to rasterize the geometry into");
OGR_G_DestroyGeometry(src_geom);
if (unload_drv)
GDALDestroyDriver(_drv);
return;
}
cplerr = GDALSetGeoTransform(_ds, _gt);
if (cplerr != CE_None)
{
rterror("rasterize_hash: Could not set geotransform on GDALDataset");
OGR_G_DestroyGeometry(src_geom);
GDALClose(_ds);
if (unload_drv)
GDALDestroyDriver(_drv);
return;
}
if (nullptr != src_sr)
{
char *_srs = nullptr;
OSRExportToWkt(src_sr, &_srs);
cplerr = GDALSetProjection(_ds, _srs);
CPLFree(_srs);
if (cplerr != CE_None)
{
rterror("rasterize_hash: Could not set projection on GDALDataset");
OGR_G_DestroyGeometry(src_geom);
GDALClose(_ds);
if (unload_drv)
GDALDestroyDriver(_drv);
return;
}
}
err = 0;
do
{
cplerr = GDALAddBand(_ds, GDT_Byte, nullptr);
if (cplerr != CE_None)
{
rterror("rasterize_hash: Could not add band to GDALDataset");
err = 1;
break;
}
_band = GDALGetRasterBand(_ds, i + 1);
if (nullptr == _band)
{
rterror("rasterize_hash: Could not get band %d from GDALDataset", i + 1);
err = 1;
break;
}
if (hasnodata)
{
RASTER_DEBUGF(4, "Setting NODATA value of band %d to %f", i, nodata[i]);
cplerr = GDALSetRasterNoDataValue(_band, nodata);
if (cplerr != CE_None)
{
rterror("rasterize_hash: Could not set nodata value");
err = 1;
break;
}
RASTER_DEBUGF(4, "NODATA value set to %f", GDALGetRasterNoDataValue(_band, nullptr));
}
cplerr = GDALFillRaster(_band, init, 0);
if (cplerr != CE_None)
{
rterror("rasterize_hash: Could not set initial value");
err = 1;
break;
}
} while (0);
if (err)
{
OGR_G_DestroyGeometry(src_geom);
GDALClose(_ds);
if (unload_drv)
GDALDestroyDriver(_drv);
return;
}
cplerr = GDALRasterizeGeometries(
_ds,
numbands, &bandlist,
1, &src_geom,
nullptr, nullptr,
&value,
options,
nullptr, nullptr);
if (cplerr != CE_None)
{
rterror("rasterize_hash: Could not rasterize geometry");
OGR_G_DestroyGeometry(src_geom);
GDALClose(_ds);
if (unload_drv)
GDALDestroyDriver(_drv);
return;
}
GDALFlushCache(_ds);
RASTER_DEBUG(3, "Converting GDAL dataset to raster");
RawFromGdalDataset(_ds, data);
OGR_G_DestroyGeometry(src_geom);
GDALClose(_ds);
if (unload_drv)
GDALDestroyDriver(_drv);
}
* @brief 从 GDALDatasetH 中获取原始的栅格化数据
*
* @param ds GDALDataset handler
* @param data 栅格化后的原始数据
*/
void RawFromGdalDataset(GDALDatasetH ds, RawHashData &data)
{
double gt[6] = {0};
CPLErr cplerr;
uint32_t width = 0;
uint32_t height = 0;
uint32_t numBands = 0;
int i = 0;
char *authname = nullptr;
char *authcode = nullptr;
GDALRasterBandH gdband = nullptr;
GDALDataType gdpixtype = GDT_Unknown;
rt_band band;
int32_t idx;
rt_pixtype pt = PT_END;
uint32_t ptlen = 0;
int hasnodata = 0;
int x;
int y;
int nXBlocks, nYBlocks;
int nXBlockSize, nYBlockSize;
int iXBlock, iYBlock;
int nXValid, nYValid;
int iY;
uint8_t *values = nullptr;
uint32_t valueslen = 0;
uint8_t *ptr = nullptr;
if (nullptr == ds)
{
rterror("RawFromGdalDataset: ds cannot be nullptr.");
}
width = GDALGetRasterXSize(ds);
height = GDALGetRasterYSize(ds);
RASTER_DEBUGF(3, "Raster dimensions (width x height): %d x %d", width, height);
data.val.resize(width * height);
cplerr = GDALGetGeoTransform(ds, gt);
if (GDALGetGeoTransform(ds, gt) != CE_None)
{
RASTER_DEBUG(4, "Using default geotransform matrix (0, 1, 0, 0, 0, -1)");
gt[0] = 0;
gt[1] = 1;
gt[2] = 0;
gt[3] = 0;
gt[4] = 0;
gt[5] = -1;
}
RASTER_DEBUGF(3, "Raster geotransform (%f, %f, %f, %f, %f, %f)",
gt[0], gt[1], gt[2], gt[3], gt[4], gt[5]);
numBands = GDALGetRasterCount(ds);
#if POSTGIS_DEBUG_LEVEL > 3
for (i = 1; i <= numBands; i++)
{
GDALRasterBandH _grb = nullptr;
double _min;
double _max;
double _mean;
double _stddev;
_grb = GDALGetRasterBand(ds, i);
GDALComputeRasterStatistics(_grb, FALSE, &_min, &_max, &_mean, &_stddev, nullptr, nullptr);
RASTER_DEBUGF(4, "GDAL Band %d stats: %f, %f, %f, %f", i, _min, _max, _mean, _stddev);
}
#endif
for (i = 1; i <= numBands; i++)
{
RASTER_DEBUGF(3, "Processing band %d of %d", i, numBands);
gdband = nullptr;
gdband = GDALGetRasterBand(ds, i);
if (nullptr == gdband)
{
rterror("RawFromGdalDataset: Could not get GDAL band");
return;
}
RASTER_DEBUGF(4, "gdband @ %p", gdband);
gdpixtype = GDALGetRasterDataType(gdband);
RASTER_DEBUGF(4, "gdpixtype, size = %s, %d", GDALGetDataTypeName(gdpixtype), GDALGetDataTypeSize(gdpixtype) / 8);
ptlen = GDALGetDataTypeSize(gdpixtype) / 8;
width = GDALGetRasterBandXSize(gdband);
height = GDALGetRasterBandYSize(gdband);
RASTER_DEBUGF(3, "GDAL band dimensions (width x height): %d x %d", width, height);
GDALGetBlockSize(gdband, &nXBlockSize, &nYBlockSize);
nXBlocks = (width + nXBlockSize - 1) / nXBlockSize;
nYBlocks = (height + nYBlockSize - 1) / nYBlockSize;
RASTER_DEBUGF(4, "(nXBlockSize, nYBlockSize) = (%d, %d)", nXBlockSize, nYBlockSize);
RASTER_DEBUGF(4, "(nXBlocks, nYBlocks) = (%d, %d)", nXBlocks, nYBlocks);
valueslen = ptlen * nXBlockSize * nYBlockSize;
for (iYBlock = 0; iYBlock < nYBlocks; iYBlock++)
{
for (iXBlock = 0; iXBlock < nXBlocks; iXBlock++)
{
x = iXBlock * nXBlockSize;
y = iYBlock * nYBlockSize;
RASTER_DEBUGF(4, "(iXBlock, iYBlock) = (%d, %d)", iXBlock, iYBlock);
RASTER_DEBUGF(4, "(x, y) = (%d, %d)", x, y);
if ((iXBlock + 1) * nXBlockSize > width)
nXValid = width - (iXBlock * nXBlockSize);
else
nXValid = nXBlockSize;
if ((iYBlock + 1) * nYBlockSize > height)
nYValid = height - (iYBlock * nYBlockSize);
else
nYValid = nYBlockSize;
RASTER_DEBUGF(4, "(nXValid, nYValid) = (%d, %d)", nXValid, nYValid);
cplerr = GDALRasterIO(
gdband, GF_Read,
x, y,
nXValid, nYValid,
&data.val[0] + valueslen * iYBlock, nXValid, nYValid,
gdpixtype,
0, 0);
if (cplerr != CE_None)
{
rterror("RawFromGdalDataset: Could not get data from GDAL raster");
return;
}
}
}
}
}
* @brief 根据坐标快速计算 geohash
*
* @param x 横坐标
* @param y 纵坐标
* @param level 划分等级
* @return uint64_t
*/
uint64_t HashCode(unsigned int x, unsigned int y, unsigned int level)
{
if ((x > (int)std::pow(2, level)) || y > (int)std::pow(2, level))
{
return 0;
}
std::bitset<64> code;
std::bitset<56> _x(x);
std::bitset<56> _y(y);
std::bitset<6> _level(level);
for (int i = 0, j = level - 1; i < 56 && j >= 0;)
{
code[61 - i] = _x[j];
code[61 - i - 1] = _y[j];
i += 2;
j--;
}
for (int i = 5; i >= 0; i--)
{
code[i] = _level[i];
}
return code.to_ulong();
}