前面有使用Node.js读取存储在PostGIS数据库中栅格影像发布xyz服务的文章,这篇是Apache版本。性能还需要优化直接上代码:
cpp
#ifndef TILE_DATABASE_HANDLER_H
#define TILE_DATABASE_HANDLER_H
#include <libpq-fe.h>
#include <stdlib.h>
typedef struct {
PGconn* conn;
} TileDatabaseHandler;
// Create and initialize the TileDatabaseHandler
TileDatabaseHandler* createTileDatabaseHandler();
// Destroy the TileDatabaseHandler and free its resources
void destroyTileDatabaseHandler(TileDatabaseHandler* handler);
// Get the tile data from the database given z, x, y coordinates
char* getTile(TileDatabaseHandler* handler, int z, int x, int y, size_t* dataLength);
#endif // TILE_DATABASE_HANDLER_H
cpp
#include "TileDatabaseHandler.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// Function to create and initialize TileDatabaseHandler
TileDatabaseHandler* createTileDatabaseHandler() {
TileDatabaseHandler* handler = (TileDatabaseHandler*)malloc(sizeof(TileDatabaseHandler));
if (!handler) {
return NULL;
}
// Connection string
const char* conninfo = "host=192.168.101.201 port=4321 dbname=Tile user=postgres password=root";
// Establish connection to the database
handler->conn = PQconnectdb(conninfo);
// Check to ensure the connection was successful
if (PQstatus(handler->conn) != CONNECTION_OK) {
fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(handler->conn));
destroyTileDatabaseHandler(handler);
return NULL;
}
return handler;
}
// Function to destroy the TileDatabaseHandler and free its resources
void destroyTileDatabaseHandler(TileDatabaseHandler* handler) {
if (handler) {
if (handler->conn) {
PQfinish(handler->conn);
}
free(handler);
}
}
// Function to get tile data from the database given z, x, y coordinates
char* getTile(TileDatabaseHandler* handler, int z, int x, int y, size_t* dataLength) {
if (!handler || !handler->conn) return NULL;
const char* queryTemplate = "SELECT data FROM Tile WHERE z = $1 AND x = $2 AND y = $3";
// Allocate memory for query parameters
//const int paramCount = 3;
const char* params[3];
char zStr[12];
char xStr[22];
char yStr[22];
snprintf(zStr, sizeof(zStr), "%d", z - 1);
snprintf(xStr, sizeof(xStr), "%ld", (long)x);
snprintf(yStr, sizeof(yStr), "%ld", (long)y);
params[0] = zStr;
params[1] = xStr;
params[2] = yStr;
// Execute the query
PGresult* res = PQexecParams(handler->conn, queryTemplate, 3, NULL, params, NULL, NULL, 1);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, "SELECT failed: %s", PQerrorMessage(handler->conn));
PQclear(res);
return NULL;
}
// Check if we have any result
if (PQntuples(res) == 0) {
PQclear(res);
return NULL;
}
// Get the length of the data
*dataLength = PQgetlength(res, 0, 0);
if (*dataLength == 0) {
PQclear(res);
return NULL;
}
// Allocate memory for the tile data
char* tileData = (char*)malloc(*dataLength);
if (!tileData) {
PQclear(res);
return NULL;
}
// Copy the binary data to tileData buffer
memcpy(tileData, PQgetvalue(res, 0, 0), *dataLength);
// Clear the result to free memory
PQclear(res);
return tileData;
}
cpp
#include "httpd.h"
#include "http_log.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <apr_pools.h>
#include <apr_tables.h>
#include <apr_strings.h>
// Include the C header for the TileDatabaseHandler functions
#include "TileDatabaseHandler.h"
// A wrapper to handle database context in C code
typedef struct {
TileDatabaseHandler* dbHandler; // Database handler struct
char* tileDataBuffer; // Buffer to hold the tile data
size_t tileDataLength; // Length of the tile data
} tilehandler_context;
tilehandler_context* create_tilehandler_context() {
tilehandler_context* context = (tilehandler_context*)malloc(sizeof(tilehandler_context));
if (context) {
context->dbHandler = createTileDatabaseHandler();
context->tileDataBuffer = NULL;
context->tileDataLength = 0;
}
return context;
}
void destroy_tilehandler_context(tilehandler_context* context) {
if (context) {
if (context->tileDataBuffer) {
free(context->tileDataBuffer);
}
destroyTileDatabaseHandler(context->dbHandler);
free(context);
}
}
// Get tile data and its length
const char* get_tile_data(tilehandler_context* context, int z, int x, int y) {
if (!context) return NULL;
if (context->tileDataBuffer) {
free(context->tileDataBuffer);
context->tileDataBuffer = NULL;
}
// Assuming getTile is modified to return BLOB data and its length
context->tileDataBuffer = getTile(context->dbHandler, z, x, y, &(context->tileDataLength));
if (!context->tileDataBuffer || context->tileDataLength == 0) {
return NULL;
}
return context->tileDataBuffer;
}
/* The sample content handler */
static int tilehandler_module_handler(request_rec* r) {
if (strcmp(r->handler, "tilehandler_module")) {
return DECLINED;
}
if (r->method_number != M_GET) {
return HTTP_METHOD_NOT_ALLOWED;
}
int z, x, y;
// Extract coordinates from the URI, removing possible file extension
if (sscanf(r->uri, "/tiles/%d/%d/%d", &z, &x, &y) != 3) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to parse URI: %s", r->uri);
return HTTP_BAD_REQUEST;
}
// Create context
tilehandler_context* context = create_tilehandler_context();
if (!context) {
return HTTP_INTERNAL_SERVER_ERROR;
}
const char* tileData = get_tile_data(context, z, x, y);
if (tileData && context->tileDataLength > 0) {
// Set correct MIME type based on your image format
ap_set_content_type(r, "image/jpeg"); // Change to "image/png" if needed
// Set Content-Length header
apr_table_setn(r->headers_out, "Content-Length", apr_psprintf(r->pool, "%zu", context->tileDataLength));
// Write the image data
ap_rwrite(tileData, context->tileDataLength, r);
}
else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Tile not found for z=%d, x=%d, y=%d", z, x, y);
ap_set_content_type(r, "text/plain");
ap_rprintf(r, "Tile not found");
destroy_tilehandler_context(context);
return HTTP_NOT_FOUND;
}
destroy_tilehandler_context(context);
return OK;
}
static void tilehandler_module_register_hooks(apr_pool_t* p) {
ap_hook_handler(tilehandler_module_handler, NULL, NULL, APR_HOOK_MIDDLE);
}
/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA tilehandler_module = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
NULL, /* create per-server config structures */
NULL, /* merge per-server config structures */
NULL, /* table of config file commands */
tilehandler_module_register_hooks /* register hooks */
};