port to mapnik 3
This commit is contained in:
parent
6b05f19c38
commit
e3cc70bee7
7 changed files with 1402 additions and 0 deletions
63
plugins/input/sqlserver/build.py
Normal file
63
plugins/input/sqlserver/build.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#
|
||||||
|
# This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 Artem Pavlenko
|
||||||
|
#
|
||||||
|
# Mapnik is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library 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
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
Import ('plugin_base')
|
||||||
|
Import ('env')
|
||||||
|
|
||||||
|
PLUGIN_NAME = 'sqlserver'
|
||||||
|
|
||||||
|
plugin_env = plugin_base.Clone()
|
||||||
|
|
||||||
|
plugin_sources = Split(
|
||||||
|
"""
|
||||||
|
%(PLUGIN_NAME)s_datasource.cpp
|
||||||
|
%(PLUGIN_NAME)s_featureset.cpp
|
||||||
|
%(PLUGIN_NAME)s_geometry_parser.cpp
|
||||||
|
""" % locals()
|
||||||
|
)
|
||||||
|
|
||||||
|
libraries = [ 'odbc' ]
|
||||||
|
libraries.append('boost_system%s' % env['BOOST_APPEND'])
|
||||||
|
libraries.append(env['ICU_LIB_NAME'])
|
||||||
|
|
||||||
|
if env['PLUGIN_LINKING'] == 'shared':
|
||||||
|
libraries.append('mapnik')
|
||||||
|
|
||||||
|
TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
|
||||||
|
SHLIBPREFIX='',
|
||||||
|
SHLIBSUFFIX='.input',
|
||||||
|
source=plugin_sources,
|
||||||
|
LIBS=libraries,
|
||||||
|
LINKFLAGS=env['CUSTOM_LDFLAGS'])
|
||||||
|
|
||||||
|
# if the plugin links to libmapnik ensure it is built first
|
||||||
|
Depends(TARGET, env.subst('../../../src/%s' % env['MAPNIK_LIB_NAME']))
|
||||||
|
|
||||||
|
if 'uninstall' not in COMMAND_LINE_TARGETS:
|
||||||
|
env.Install(env['MAPNIK_INPUT_PLUGINS_DEST'], TARGET)
|
||||||
|
env.Alias('install', env['MAPNIK_INPUT_PLUGINS_DEST'])
|
||||||
|
|
||||||
|
plugin_obj = {
|
||||||
|
'LIBS': libraries,
|
||||||
|
'SOURCES': plugin_sources,
|
||||||
|
}
|
||||||
|
|
||||||
|
Return('plugin_obj')
|
464
plugins/input/sqlserver/sqlserver_datasource.cpp
Normal file
464
plugins/input/sqlserver/sqlserver_datasource.cpp
Normal file
|
@ -0,0 +1,464 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 we-do-IT
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "sqlserver_datasource.hpp"
|
||||||
|
#include "sqlserver_featureset.hpp"
|
||||||
|
#include "sqlserver_geometry_parser.hpp"
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/debug.hpp>
|
||||||
|
#include <mapnik/boolean.hpp>
|
||||||
|
#include <mapnik/sql_utils.hpp>
|
||||||
|
#include <mapnik/timer.hpp>
|
||||||
|
#include <mapnik/value_types.hpp>
|
||||||
|
#include <mapnik/geometry_envelope.hpp>
|
||||||
|
|
||||||
|
// boost
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/tokenizer.hpp>
|
||||||
|
#include <boost/make_shared.hpp>
|
||||||
|
|
||||||
|
// stl
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
// sql server (odbc)
|
||||||
|
#include <sqlext.h>
|
||||||
|
#include <msodbcsql.h>
|
||||||
|
|
||||||
|
using mapnik::datasource;
|
||||||
|
using mapnik::parameters;
|
||||||
|
using mapnik::query;
|
||||||
|
using mapnik::featureset_ptr;
|
||||||
|
using mapnik::layer_descriptor;
|
||||||
|
using mapnik::attribute_descriptor;
|
||||||
|
using mapnik::datasource_exception;
|
||||||
|
using mapnik::box2d;
|
||||||
|
using mapnik::coord2d;
|
||||||
|
|
||||||
|
DATASOURCE_PLUGIN(sqlserver_datasource)
|
||||||
|
|
||||||
|
// an exception class to wrap gathering the odbc error
|
||||||
|
sqlserver_datasource_exception::sqlserver_datasource_exception(std::string const& message)
|
||||||
|
: mapnik::datasource_exception("SQL Server Plugin: "+message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlserver_datasource_exception::sqlserver_datasource_exception(std::string const& message, SQLSMALLINT HandleType, SQLHANDLE Handle)
|
||||||
|
: mapnik::datasource_exception("SQL Server Plugin: "+message+": "+sql_diagnostics(HandleType, Handle)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlserver_datasource_exception::~sqlserver_datasource_exception() throw () {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sqlserver_datasource_exception::sql_diagnostics(SQLSMALLINT HandleType, SQLHANDLE Handle) {
|
||||||
|
// Get the status records.
|
||||||
|
std::ostringstream s;
|
||||||
|
SQLCHAR SqlState[6];
|
||||||
|
SQLINTEGER NativeError;
|
||||||
|
SQLCHAR Msg[SQL_MAX_MESSAGE_LENGTH];
|
||||||
|
SQLSMALLINT MsgLen;
|
||||||
|
SQLSMALLINT i = 1;
|
||||||
|
while (SQLGetDiagRec(HandleType, Handle, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen) == SQL_SUCCESS) {
|
||||||
|
s << "[" << SqlState << "] ";
|
||||||
|
s << Msg;
|
||||||
|
s << " (" << NativeError << ") ";
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return s.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// datasource
|
||||||
|
sqlserver_datasource::sqlserver_datasource(parameters const& params) :
|
||||||
|
datasource (params),
|
||||||
|
type_(datasource::Vector),
|
||||||
|
fields_(*params.get<std::string>("fields", "*")),
|
||||||
|
geometry_field_(*params.get<std::string>("geometry_field", "")),
|
||||||
|
is_geometry_(true),
|
||||||
|
extent_initialized_(false),
|
||||||
|
desc_(*params.get<std::string>("type"), *params.get<std::string>("encoding", "utf-8")),
|
||||||
|
henv_(0),
|
||||||
|
hdbc_(0)
|
||||||
|
{
|
||||||
|
#ifdef MAPNIK_STATS
|
||||||
|
mapnik::progress_timer __stats__(std::clog, "sqlserver_datasource::init");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// they must supply a table/view name or a subquery
|
||||||
|
if (params.get<std::string>("table")) {
|
||||||
|
table_ = *params.get<std::string>("table");
|
||||||
|
} else {
|
||||||
|
throw sqlserver_datasource_exception("no <table> parameter specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
// they may supply an extent (and srid) to prevent querying for it
|
||||||
|
boost::optional<std::string> ext = params.get<std::string>("extent");
|
||||||
|
if (ext) {
|
||||||
|
extent_initialized_ = extent_.from_string(*ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<mapnik::value_integer> srid = params.get<mapnik::value_integer>("srid");
|
||||||
|
if (srid) {
|
||||||
|
srid_ = *srid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ext && !srid) {
|
||||||
|
throw sqlserver_datasource_exception("must specify <srid> parameter if <extent> parameter specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
// the driver refers to an entry in odbcinst.ini http://www.unixodbc.org/odbcinst.html
|
||||||
|
std::string driver("ODBC Driver 11 for SQL Server"); // default for ms driver
|
||||||
|
if (params.get<std::string>("driver")) {
|
||||||
|
driver = *params.get<std::string>("driver");
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the connection string
|
||||||
|
std::ostringstream s;
|
||||||
|
s << "Driver={" << driver << "};";
|
||||||
|
if (params.get<std::string>("server")) {
|
||||||
|
s << "Server=" << *params.get<std::string>("server") << ";";
|
||||||
|
}
|
||||||
|
if (params.get<std::string>("database")) {
|
||||||
|
s << "Database=" << *params.get<std::string>("database") << ";";
|
||||||
|
}
|
||||||
|
if (params.get<std::string>("user")) {
|
||||||
|
s << "Uid=" << *params.get<std::string>("user") << ";";
|
||||||
|
}
|
||||||
|
if (params.get<std::string>("password")) {
|
||||||
|
s << "Pwd=" << *params.get<std::string>("password") << ";";
|
||||||
|
}
|
||||||
|
if (params.get<std::string>("trusted")) {
|
||||||
|
s << "Trusted_Connection=" << *params.get<std::string>("trusted") << ";";
|
||||||
|
}
|
||||||
|
std::string InConnectionString = s.str();
|
||||||
|
MAPNIK_LOG_DEBUG(sqlserver) << "sqlserver_datasource: connection string: " << InConnectionString;
|
||||||
|
|
||||||
|
// everything returns one of these
|
||||||
|
SQLRETURN retcode;
|
||||||
|
|
||||||
|
// allocate environment handle
|
||||||
|
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv_);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not allocate environment handle"); // no diagnostics available
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the ODBC version environment attribute
|
||||||
|
retcode = SQLSetEnvAttr(henv_, SQL_ATTR_ODBC_VERSION, (SQLPOINTER*)SQL_OV_ODBC3, 0);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not set ODBC version environment value", SQL_HANDLE_ENV, henv_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate connection handle
|
||||||
|
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv_, &hdbc_);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not allocate connection handle", SQL_HANDLE_ENV, henv_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set login timeout to 5 seconds
|
||||||
|
retcode = SQLSetConnectAttr(hdbc_, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not set connection timeout", SQL_HANDLE_DBC, hdbc_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect to data source
|
||||||
|
SQLCHAR OutConnectionString[1024];
|
||||||
|
SQLSMALLINT OutConnectionStringLength;
|
||||||
|
retcode = SQLDriverConnect(hdbc_,
|
||||||
|
NULL,
|
||||||
|
(SQLCHAR*)InConnectionString.c_str(),
|
||||||
|
InConnectionString.length(),
|
||||||
|
OutConnectionString,
|
||||||
|
1024,
|
||||||
|
&OutConnectionStringLength,
|
||||||
|
SQL_DRIVER_NOPROMPT);
|
||||||
|
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not connect", SQL_HANDLE_DBC, hdbc_);
|
||||||
|
}
|
||||||
|
MAPNIK_LOG_DEBUG(sqlserver) << "sqlserver_datasource: connected: " << OutConnectionString;
|
||||||
|
|
||||||
|
// get columns description
|
||||||
|
#ifdef MAPNIK_STATS
|
||||||
|
mapnik::progress_timer __stats__(std::clog, "sqlserver_datasource::get_column_description");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// table parameter can be a table/view name or a subquery
|
||||||
|
std::ostringstream stmt;
|
||||||
|
if (table_.find_first_of(" \t") == std::string::npos) {
|
||||||
|
// no whitespace in table_; assume a table/view name
|
||||||
|
stmt << "SELECT TOP(1) " << fields_ << " FROM " << table_;
|
||||||
|
} else {
|
||||||
|
// whitespace in table_; assume a valid query
|
||||||
|
stmt << table_;
|
||||||
|
}
|
||||||
|
MAPNIK_LOG_DEBUG(sqlserver) << "sqlserver_datasource: " << stmt.str();
|
||||||
|
|
||||||
|
// allocate statement handle
|
||||||
|
SQLHANDLE hstmt=0;
|
||||||
|
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc_, &hstmt);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not allocate statement", SQL_HANDLE_DBC, hdbc_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare statement
|
||||||
|
retcode = SQLPrepare(hstmt, (SQLCHAR*)stmt.str().c_str(), SQL_NTS);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not prepare statement", SQL_HANDLE_STMT, hstmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// find out how many columns in result set
|
||||||
|
SQLSMALLINT n=0;
|
||||||
|
retcode = SQLNumResultCols(hstmt, &n);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not get number of result columns", SQL_HANDLE_STMT, hstmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get name,type for each column
|
||||||
|
for (int i=1; i<=n; i++) {
|
||||||
|
SQLCHAR ColumnName[255]; // max is currently 128 in sql server
|
||||||
|
SQLSMALLINT NameLength;
|
||||||
|
SQLSMALLINT DataType;
|
||||||
|
SQLULEN ColumnSize;
|
||||||
|
SQLSMALLINT DecimalDigits;
|
||||||
|
SQLSMALLINT Nullable;
|
||||||
|
retcode = SQLDescribeCol(hstmt, i, ColumnName, sizeof(ColumnName), &NameLength, &DataType, &ColumnSize, &DecimalDigits, &Nullable);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not describe column", SQL_HANDLE_STMT, hstmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLCHAR TypeName[255];
|
||||||
|
SQLSMALLINT ReturnedLength;
|
||||||
|
char geometry[] = {'g','\0','e','\0','o','\0','m','\0','e','\0','t','\0','r','\0','y','\0','\0','\0'}; // geometry
|
||||||
|
char geography[] = {'g','\0','e','\0','o','\0','g','\0','r','\0','a','\0','p','\0','h','\0','y','\0','\0','\0'}; // geography
|
||||||
|
switch (DataType) {
|
||||||
|
case SQL_CHAR:
|
||||||
|
case SQL_VARCHAR:
|
||||||
|
case -9: // NVARCHAR
|
||||||
|
desc_.add_descriptor(attribute_descriptor((char *)ColumnName,mapnik::sqlserver::String));
|
||||||
|
//MAPNIK_LOG_DEBUG(sqlserver) << "found string column: " << (char*)ColumnName;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQL_INTEGER:
|
||||||
|
case SQL_SMALLINT:
|
||||||
|
desc_.add_descriptor(attribute_descriptor((char *)ColumnName,mapnik::sqlserver::Integer));
|
||||||
|
//MAPNIK_LOG_DEBUG(sqlserver) << "found integer column: " << (char*)ColumnName;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQL_NUMERIC:
|
||||||
|
case SQL_DECIMAL:
|
||||||
|
case SQL_FLOAT:
|
||||||
|
case SQL_REAL:
|
||||||
|
case SQL_DOUBLE:
|
||||||
|
desc_.add_descriptor(attribute_descriptor((char *)ColumnName,mapnik::sqlserver::Double));
|
||||||
|
//MAPNIK_LOG_DEBUG(sqlserver) << "found double column: " << (char*)ColumnName;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQL_DATETIME:
|
||||||
|
case SQL_TYPE_DATE:
|
||||||
|
case SQL_TYPE_TIME:
|
||||||
|
case SQL_TYPE_TIMESTAMP:
|
||||||
|
desc_.add_descriptor(attribute_descriptor((char *)ColumnName,mapnik::sqlserver::String));
|
||||||
|
//MAPNIK_LOG_DEBUG(sqlserver) << "found string column: " << (char*)ColumnName;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQL_SS_UDT:
|
||||||
|
// check if it is a geometry type
|
||||||
|
retcode = SQLColAttribute(hstmt, i, SQL_CA_SS_UDT_TYPE_NAME, &TypeName, sizeof(TypeName), &ReturnedLength, NULL);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not get column attribute", SQL_HANDLE_STMT, hstmt);
|
||||||
|
}
|
||||||
|
// on linux the type name is returned as a weird string with null bytes as every second character
|
||||||
|
if (strcmp((char *)TypeName, "geometry") == 0 || std::memcmp((char*)TypeName, geometry, ReturnedLength) == 0) {
|
||||||
|
//MAPNIK_LOG_DEBUG(sqlserver) << "found geometry column: " << (char*)ColumnName;
|
||||||
|
geometry_field_ = (const char *)ColumnName;
|
||||||
|
is_geometry_ = true;
|
||||||
|
desc_.add_descriptor(attribute_descriptor((char *)ColumnName,mapnik::sqlserver::Geometry));
|
||||||
|
}
|
||||||
|
if (strcmp((char *)TypeName, "geography") == 0 || std::memcmp((char*)TypeName, geography, ReturnedLength) == 0) {
|
||||||
|
//MAPNIK_LOG_DEBUG(sqlserver) << "found geography column: " << (char*)ColumnName;
|
||||||
|
geometry_field_ = (const char *)ColumnName;
|
||||||
|
is_geometry_ = false;
|
||||||
|
desc_.add_descriptor(attribute_descriptor((char *)ColumnName,mapnik::sqlserver::Geography));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
MAPNIK_LOG_WARN(sqlserver) << "sqlserver_datasource: unknown/unsupported datatype in column: " << ColumnName << " (" << DataType << ")";
|
||||||
|
desc_.add_descriptor(attribute_descriptor((char *)ColumnName,mapnik::sqlserver::Unknown));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// free handle
|
||||||
|
retcode = SQLFreeStmt(hstmt, SQL_CLOSE);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not free statement handle", SQL_HANDLE_STMT, hstmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// final check
|
||||||
|
if (geometry_field_ == "") {
|
||||||
|
MAPNIK_LOG_WARN(sqlserver) << "sqlserver_datasource: no geometry column found or specified";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlserver_datasource::~sqlserver_datasource()
|
||||||
|
{
|
||||||
|
if (hdbc_) {
|
||||||
|
(void)SQLDisconnect(hdbc_);
|
||||||
|
(void)SQLFreeHandle(SQL_HANDLE_DBC, hdbc_);
|
||||||
|
hdbc_ = 0;
|
||||||
|
}
|
||||||
|
if (henv_) {
|
||||||
|
(void)SQLFreeHandle(SQL_HANDLE_ENV, henv_);
|
||||||
|
henv_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * sqlserver_datasource::name()
|
||||||
|
{
|
||||||
|
return "sqlserver";
|
||||||
|
}
|
||||||
|
|
||||||
|
mapnik::datasource::datasource_t sqlserver_datasource::type() const
|
||||||
|
{
|
||||||
|
return type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
box2d<double> sqlserver_datasource::envelope() const
|
||||||
|
{
|
||||||
|
if (extent_initialized_) return extent_;
|
||||||
|
|
||||||
|
SQLRETURN retcode;
|
||||||
|
|
||||||
|
// allocate statement handle
|
||||||
|
SQLHANDLE hstmt=0;
|
||||||
|
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc_, &hstmt);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not allocate statement", SQL_HANDLE_DBC, hdbc_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// table parameter can be a table/view or a subquery
|
||||||
|
// iff a subquery, need to wrap in ()
|
||||||
|
std::ostringstream stmt;
|
||||||
|
stmt << "SELECT geometry::EnvelopeAggregate(" << geometry_field_ << ") FROM ";
|
||||||
|
if (table_.find_first_of(" \t") == std::string::npos) {
|
||||||
|
// no whitespace in table; assume a table/view name
|
||||||
|
stmt << table_;
|
||||||
|
} else {
|
||||||
|
// whitespace in table; assume a subquery
|
||||||
|
stmt << "(" << table_ << ") T";
|
||||||
|
}
|
||||||
|
MAPNIK_LOG_DEBUG(sqlserver) << "sqlserver_datasource: " << stmt.str();
|
||||||
|
|
||||||
|
// execute statement
|
||||||
|
retcode = SQLExecDirect(hstmt, (SQLCHAR*)stmt.str().c_str(), SQL_NTS);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not execute statement", SQL_HANDLE_STMT, hstmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch first result (will only be one row)
|
||||||
|
retcode = SQLFetch(hstmt);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not fetch result", SQL_HANDLE_STMT, hstmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the row data
|
||||||
|
SQLUSMALLINT ColumnNum = 1;
|
||||||
|
SQLCHAR BinaryPtr[1024]; // envelope is a 5 point polygon; usually only 112 bytes
|
||||||
|
SQLLEN BinaryLenOrInd;
|
||||||
|
retcode = SQLGetData(hstmt, ColumnNum, SQL_C_BINARY, BinaryPtr, sizeof(BinaryPtr), &BinaryLenOrInd);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not get data", SQL_HANDLE_STMT, hstmt);
|
||||||
|
}
|
||||||
|
//MAPNIK_LOG_DEBUG(sqlserver) << "sqlserver_datasource: envelope returned " << BinaryLenOrInd << " bytes";
|
||||||
|
|
||||||
|
sqlserver_geometry_parser geometry_parser(is_geometry_ ? Geometry : Geography);
|
||||||
|
mapnik::geometry::geometry<double> geom = geometry_parser.parse(BinaryPtr, BinaryLenOrInd);
|
||||||
|
extent_ = mapnik::geometry::envelope(geom);
|
||||||
|
extent_initialized_ = true;
|
||||||
|
srid_ = geometry_parser.get_srs_id(); // get the srid of the extents; assume that is same for whole table
|
||||||
|
|
||||||
|
// free handle
|
||||||
|
retcode = SQLFreeStmt(hstmt, SQL_CLOSE);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not free statement handle", SQL_HANDLE_STMT, hstmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return extent_;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<mapnik::datasource_geometry_t> sqlserver_datasource::get_geometry_type() const
|
||||||
|
{
|
||||||
|
return boost::optional<mapnik::datasource_geometry_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
layer_descriptor sqlserver_datasource::get_descriptor() const
|
||||||
|
{
|
||||||
|
return desc_;
|
||||||
|
}
|
||||||
|
|
||||||
|
featureset_ptr sqlserver_datasource::features(query const& q) const
|
||||||
|
{
|
||||||
|
#ifdef MAPNIK_STATS
|
||||||
|
mapnik::progress_timer __stats__(std::clog, "sqlserver_datasource::features");
|
||||||
|
#endif
|
||||||
|
box2d<double> const& box = q.get_bbox();
|
||||||
|
return features_in_box(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
featureset_ptr sqlserver_datasource::features_at_point(coord2d const& pt, double tol) const
|
||||||
|
{
|
||||||
|
#ifdef MAPNIK_STATS
|
||||||
|
mapnik::progress_timer __stats__(std::clog, "sqlserver_datasource::features_at_point");
|
||||||
|
#endif
|
||||||
|
box2d<double> box(pt.x - tol, pt.y - tol, pt.x + tol, pt.y + tol);
|
||||||
|
return features_in_box(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
featureset_ptr sqlserver_datasource::features_in_box(box2d<double> const& box) const {
|
||||||
|
std::string sql; // = table_; //populate_tokens(table_, scale_denom, box, px_gw, px_gh);
|
||||||
|
if (table_.find_first_of(" \t") == std::string::npos) {
|
||||||
|
// no whitespace in table_; assume a table/view name
|
||||||
|
sql = "SELECT * from " + table_;
|
||||||
|
} else {
|
||||||
|
// whitespace in table_; assume a subquery
|
||||||
|
sql = table_ ;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream spatial_sql;
|
||||||
|
spatial_sql << " WHERE " << geometry_field_;
|
||||||
|
spatial_sql << ".STIntersects(" ;
|
||||||
|
spatial_sql << std::setprecision(16);
|
||||||
|
spatial_sql << "geometry::STPolyFromText('POLYGON((";
|
||||||
|
spatial_sql << box.minx() << " " << box.miny() << ", ";
|
||||||
|
spatial_sql << box.minx() << " " << box.maxy() << ", ";
|
||||||
|
spatial_sql << box.maxx() << " " << box.maxy() << ", ";
|
||||||
|
spatial_sql << box.maxx() << " " << box.miny() << ", ";
|
||||||
|
spatial_sql << box.minx() << " " << box.miny() << "))'," << srid_ <<")";
|
||||||
|
spatial_sql << ") = 1";
|
||||||
|
|
||||||
|
if (boost::algorithm::ifind_first(sql, "WHERE")) {
|
||||||
|
// change existing where clause; prefix it with the spatial predicate
|
||||||
|
boost::algorithm::ireplace_first(sql, "WHERE", spatial_sql.str() + " AND ");
|
||||||
|
} else if (boost::algorithm::ifind_first(sql, table_)) {
|
||||||
|
// no where clause, so add the spatial predicate as the where clause
|
||||||
|
boost::algorithm::ireplace_first(sql, table_, table_ + " " + spatial_sql.str());
|
||||||
|
} else {
|
||||||
|
MAPNIK_LOG_WARN(sqlserver) << "sqlserver_datasource: cannot determine where to add the spatial filter clause";
|
||||||
|
}
|
||||||
|
|
||||||
|
MAPNIK_LOG_DEBUG(sqlserver) << "sqlserver_datasource: " << sql;
|
||||||
|
|
||||||
|
return std::make_shared<sqlserver_featureset>(hdbc_,
|
||||||
|
sql,
|
||||||
|
desc_);
|
||||||
|
}
|
||||||
|
|
102
plugins/input/sqlserver/sqlserver_datasource.hpp
Normal file
102
plugins/input/sqlserver/sqlserver_datasource.hpp
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 we-do-IT
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SQLSERVER_DATASOURCE_HPP
|
||||||
|
#define SQLSERVER_DATASOURCE_HPP
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/datasource.hpp>
|
||||||
|
#include <mapnik/params.hpp>
|
||||||
|
#include <mapnik/query.hpp>
|
||||||
|
#include <mapnik/feature.hpp>
|
||||||
|
#include <mapnik/box2d.hpp>
|
||||||
|
#include <mapnik/coord.hpp>
|
||||||
|
#include <mapnik/feature_layer_desc.hpp>
|
||||||
|
#include <mapnik/value_types.hpp>
|
||||||
|
|
||||||
|
// boost
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
// stl
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// sql server (via odbc)
|
||||||
|
#ifdef _WINDOWS
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
#include "sql.h"
|
||||||
|
|
||||||
|
// extended from mapnik/attribute_descriptor.hpp (added 8,9)
|
||||||
|
namespace mapnik {
|
||||||
|
namespace sqlserver {
|
||||||
|
enum eAttributeTypeEx {
|
||||||
|
Integer=1,
|
||||||
|
Float=2,
|
||||||
|
Double=3,
|
||||||
|
String=4,
|
||||||
|
Boolean=5,
|
||||||
|
Geometry=6,
|
||||||
|
Object=7,
|
||||||
|
Geography=8,
|
||||||
|
Unknown=9
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SQL Server datasource for Mapnik
|
||||||
|
class sqlserver_datasource : public mapnik::datasource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
sqlserver_datasource(mapnik::parameters const& params);
|
||||||
|
virtual ~sqlserver_datasource ();
|
||||||
|
static const char * name();
|
||||||
|
mapnik::datasource::datasource_t type() const;
|
||||||
|
mapnik::featureset_ptr features(mapnik::query const& q) const;
|
||||||
|
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt, double tol = 0) const;
|
||||||
|
mapnik::box2d<double> envelope() const;
|
||||||
|
boost::optional<mapnik::datasource_geometry_t> get_geometry_type() const;
|
||||||
|
mapnik::layer_descriptor get_descriptor() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mapnik::datasource::datasource_t type_;
|
||||||
|
|
||||||
|
std::string table_;
|
||||||
|
std::string fields_;
|
||||||
|
std::string geometry_field_;
|
||||||
|
bool is_geometry_; // true=geometry, false=geography
|
||||||
|
|
||||||
|
mutable bool extent_initialized_;
|
||||||
|
mutable mapnik::box2d<double> extent_;
|
||||||
|
mutable int srid_;
|
||||||
|
|
||||||
|
mapnik::layer_descriptor desc_;
|
||||||
|
|
||||||
|
SQLHENV henv_;
|
||||||
|
SQLHDBC hdbc_;
|
||||||
|
|
||||||
|
mapnik::featureset_ptr features_in_box(mapnik::box2d<double> const& box) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// if anything goes wrong in the sqlserver_datasource class, one of these
|
||||||
|
// exceptions will be thrown. usually the message will include details from
|
||||||
|
// the SQL Server error
|
||||||
|
class sqlserver_datasource_exception : public mapnik::datasource_exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
sqlserver_datasource_exception(std::string const& message);
|
||||||
|
sqlserver_datasource_exception(std::string const& message, SQLSMALLINT HandleType, SQLHANDLE Handle);
|
||||||
|
|
||||||
|
virtual ~sqlserver_datasource_exception() throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::string sql_diagnostics(SQLSMALLINT HandleType, SQLHANDLE Handle);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SQLSERVER_DATASOURCE_HPP
|
179
plugins/input/sqlserver/sqlserver_featureset.cpp
Normal file
179
plugins/input/sqlserver/sqlserver_featureset.cpp
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 we-do-IT
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "sqlserver_featureset.hpp"
|
||||||
|
#include "sqlserver_datasource.hpp"
|
||||||
|
#include "sqlserver_geometry_parser.hpp"
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/global.hpp>
|
||||||
|
#include <mapnik/debug.hpp>
|
||||||
|
#include <mapnik/box2d.hpp>
|
||||||
|
#include <mapnik/geometry.hpp>
|
||||||
|
#include <mapnik/feature.hpp>
|
||||||
|
#include <mapnik/feature_layer_desc.hpp>
|
||||||
|
#include <mapnik/wkb.hpp>
|
||||||
|
#include <mapnik/unicode.hpp>
|
||||||
|
#include <mapnik/feature_factory.hpp>
|
||||||
|
|
||||||
|
// sql server (odbc)
|
||||||
|
#include <sqlext.h>
|
||||||
|
#include <msodbcsql.h>
|
||||||
|
|
||||||
|
using mapnik::query;
|
||||||
|
using mapnik::box2d;
|
||||||
|
using mapnik::feature_ptr;
|
||||||
|
using mapnik::geometry_utils;
|
||||||
|
using mapnik::transcoder;
|
||||||
|
using mapnik::feature_factory;
|
||||||
|
using mapnik::attribute_descriptor;
|
||||||
|
|
||||||
|
sqlserver_featureset::sqlserver_featureset(SQLHDBC hdbc,
|
||||||
|
std::string const& sqlstring,
|
||||||
|
mapnik::layer_descriptor const& desc
|
||||||
|
)
|
||||||
|
: hstmt_(0),
|
||||||
|
desc_(desc),
|
||||||
|
tr_(new transcoder(desc.get_encoding())),
|
||||||
|
feature_id_(1)
|
||||||
|
{
|
||||||
|
SQLRETURN retcode;
|
||||||
|
|
||||||
|
// allocate statement handle
|
||||||
|
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt_);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not allocate statement", SQL_HANDLE_DBC, hdbc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute statement
|
||||||
|
retcode = SQLExecDirect(hstmt_, (SQLCHAR*)sqlstring.c_str(), SQL_NTS);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not execute statement", SQL_HANDLE_STMT, hstmt_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<attribute_descriptor>::const_iterator itr = desc_.get_descriptors().begin();
|
||||||
|
std::vector<attribute_descriptor>::const_iterator end = desc_.get_descriptors().end();
|
||||||
|
ctx_ = std::make_shared<mapnik::context_type>();
|
||||||
|
while (itr != end) {
|
||||||
|
ctx_->push(itr->get_name());
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlserver_featureset::~sqlserver_featureset() {
|
||||||
|
if (hstmt_) {
|
||||||
|
(void)SQLFreeStmt(hstmt_, SQL_CLOSE);
|
||||||
|
hstmt_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
feature_ptr sqlserver_featureset::next()
|
||||||
|
{
|
||||||
|
SQLRETURN retcode;
|
||||||
|
|
||||||
|
// fetch next result
|
||||||
|
retcode = SQLFetch(hstmt_);
|
||||||
|
if (retcode == SQL_NO_DATA) {
|
||||||
|
// normal end of recordset
|
||||||
|
return feature_ptr();
|
||||||
|
}
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not fetch result", SQL_HANDLE_STMT, hstmt_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an empty feature with the next id
|
||||||
|
feature_ptr feature(feature_factory::create(ctx_, feature_id_));
|
||||||
|
|
||||||
|
// populate feature geometry and attributes from this row
|
||||||
|
std::vector<attribute_descriptor>::const_iterator itr = desc_.get_descriptors().begin();
|
||||||
|
std::vector<attribute_descriptor>::const_iterator end = desc_.get_descriptors().end();
|
||||||
|
SQLUSMALLINT ColumnNum=1;
|
||||||
|
while (itr != end) {
|
||||||
|
SQLCHAR sval[2048];
|
||||||
|
long ival;
|
||||||
|
double dval;
|
||||||
|
SQLCHAR *BinaryPtr = NULL; // Allocate dynamically
|
||||||
|
SQLLEN BinaryLenOrInd;
|
||||||
|
SQLLEN LenOrInd;
|
||||||
|
switch (itr->get_type()) {
|
||||||
|
case mapnik::sqlserver::String:
|
||||||
|
retcode = SQLGetData(hstmt_, ColumnNum, SQL_C_CHAR, sval, sizeof(sval), &LenOrInd);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not get string data", SQL_HANDLE_STMT, hstmt_);
|
||||||
|
}
|
||||||
|
feature->put(itr->get_name(), (UnicodeString)tr_->transcode((char*)sval));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case mapnik::sqlserver::Integer:
|
||||||
|
retcode = SQLGetData(hstmt_, ColumnNum, SQL_C_SLONG, &ival, sizeof(ival), &LenOrInd);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not get int data", SQL_HANDLE_STMT, hstmt_);
|
||||||
|
}
|
||||||
|
feature->put(itr->get_name(), static_cast<mapnik::value_integer>(ival));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case mapnik::sqlserver::Double:
|
||||||
|
retcode = SQLGetData(hstmt_, ColumnNum, SQL_C_DOUBLE, &dval, sizeof(dval), &LenOrInd);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
throw sqlserver_datasource_exception("could not get double data", SQL_HANDLE_STMT, hstmt_);
|
||||||
|
}
|
||||||
|
feature->put(itr->get_name(), dval);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case mapnik::sqlserver::Geometry:
|
||||||
|
case mapnik::sqlserver::Geography: {
|
||||||
|
// Call SQLGetData with a zero buffer size to determine the amount of data that's waiting.
|
||||||
|
SQLCHAR DummyBinaryPtr[10]; // cannot pass a NULL pointer, even though we pass length of zero
|
||||||
|
retcode = SQLGetData(hstmt_, ColumnNum, SQL_C_BINARY, DummyBinaryPtr, 0, &BinaryLenOrInd);
|
||||||
|
if (retcode != SQL_SUCCESS_WITH_INFO) {
|
||||||
|
throw sqlserver_datasource_exception("could not get geometry data - failed to get buffer length", SQL_HANDLE_STMT, hstmt_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate a suitably sized buffer
|
||||||
|
BinaryPtr = new SQLCHAR[BinaryLenOrInd];
|
||||||
|
|
||||||
|
// get the geometry data
|
||||||
|
retcode = SQLGetData(hstmt_, ColumnNum, SQL_C_BINARY, BinaryPtr, BinaryLenOrInd, &BinaryLenOrInd);
|
||||||
|
if (!SQL_SUCCEEDED(retcode)) {
|
||||||
|
delete[] BinaryPtr;
|
||||||
|
BinaryPtr = NULL;
|
||||||
|
throw sqlserver_datasource_exception("could not get geometry data into buffer", SQL_HANDLE_STMT, hstmt_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to parse
|
||||||
|
try {
|
||||||
|
sqlserver_geometry_parser geometry_parser((itr->get_type() == mapnik::sqlserver::Geometry ? Geometry : Geography));
|
||||||
|
mapnik::geometry::geometry<double> geom = geometry_parser.parse(BinaryPtr, BinaryLenOrInd);
|
||||||
|
feature->set_geometry(std::move(geom));
|
||||||
|
} catch (mapnik::datasource_exception e) {
|
||||||
|
// Cleanup and rethrow the caught exception
|
||||||
|
delete[] BinaryPtr;
|
||||||
|
BinaryPtr = NULL;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal cleanup
|
||||||
|
delete[] BinaryPtr;
|
||||||
|
BinaryPtr = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
MAPNIK_LOG_WARN(sqlserver) << "sqlserver_datasource: unknown/unsupported datatype in column: " << itr->get_name() << " (" << itr->get_type() << ")";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++ColumnNum;
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
++feature_id_;
|
||||||
|
|
||||||
|
return feature;
|
||||||
|
}
|
||||||
|
|
49
plugins/input/sqlserver/sqlserver_featureset.hpp
Normal file
49
plugins/input/sqlserver/sqlserver_featureset.hpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 we-do-IT
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SQLSERVER_FEATURESET_HPP
|
||||||
|
#define SQLSERVER_FEATURESET_HPP
|
||||||
|
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/feature.hpp>
|
||||||
|
#include <mapnik/datasource.hpp>
|
||||||
|
#include <mapnik/geometry.hpp>
|
||||||
|
#include <mapnik/unicode.hpp>
|
||||||
|
#include <mapnik/attribute_descriptor.hpp>
|
||||||
|
|
||||||
|
// boost
|
||||||
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
// sql server (via odbc)
|
||||||
|
#ifdef _WINDOWS
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
#include "sql.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class sqlserver_featureset : public mapnik::Featureset
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
sqlserver_featureset(SQLHDBC hdbc,
|
||||||
|
std::string const& sqlstring,
|
||||||
|
mapnik::layer_descriptor const& desc);
|
||||||
|
virtual ~sqlserver_featureset();
|
||||||
|
mapnik::feature_ptr next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SQLHANDLE hstmt_;
|
||||||
|
mapnik::layer_descriptor desc_;
|
||||||
|
boost::scoped_ptr<mapnik::transcoder> tr_;
|
||||||
|
mapnik::value_integer feature_id_;
|
||||||
|
mapnik::context_ptr ctx_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SQLSERVER_FEATURESET_HPP
|
500
plugins/input/sqlserver/sqlserver_geometry_parser.cpp
Normal file
500
plugins/input/sqlserver/sqlserver_geometry_parser.cpp
Normal file
|
@ -0,0 +1,500 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* adapted from ogrmssqlgeometryparser.cpp, part of OGR
|
||||||
|
* original copyright notice follows
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* $Id: ogrmssqlgeometryparser.cpp 24918 2012-09-07 12:02:01Z tamas $
|
||||||
|
*
|
||||||
|
* Project: MSSQL Spatial driver
|
||||||
|
* Purpose: Implements ogrmssqlgeometryparser class to parse native SqlGeometries.
|
||||||
|
* Author: Tamas Szekeres, szekerest at gmail.com
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright (c) 2010, Tamas Szekeres
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "sqlserver_geometry_parser.hpp"
|
||||||
|
#include "sqlserver_datasource.hpp"
|
||||||
|
|
||||||
|
#include <mapnik/datasource.hpp>
|
||||||
|
|
||||||
|
class sqlserver_geometry_parser_exception : public sqlserver_datasource_exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
sqlserver_geometry_parser_exception(std::string const& message)
|
||||||
|
: sqlserver_datasource_exception("Geometry Parser: "+message) {}
|
||||||
|
|
||||||
|
virtual ~sqlserver_geometry_parser_exception() throw() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* SqlGeometry serialization format
|
||||||
|
|
||||||
|
Simple Point (SerializationProps & IsSinglePoint)
|
||||||
|
[SRID][0x01][SerializationProps][Point][z][m]
|
||||||
|
|
||||||
|
Simple Line Segment (SerializationProps & IsSingleLineSegment)
|
||||||
|
[SRID][0x01][SerializationProps][Point1][Point2][z1][z2][m1][m2]
|
||||||
|
|
||||||
|
Complex Geometries
|
||||||
|
[SRID][0x01][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN]
|
||||||
|
[NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape]
|
||||||
|
|
||||||
|
SRID
|
||||||
|
Spatial Reference Id (4 bytes)
|
||||||
|
|
||||||
|
SerializationProps (bitmask) 1 byte
|
||||||
|
0x01 = HasZValues
|
||||||
|
0x02 = HasMValues
|
||||||
|
0x04 = IsValid
|
||||||
|
0x08 = IsSinglePoint
|
||||||
|
0x10 = IsSingleLineSegment
|
||||||
|
0x20 = IsWholeGlobe
|
||||||
|
|
||||||
|
Point (2-4)x8 bytes, size depends on SerializationProps & HasZValues & HasMValues
|
||||||
|
[x][y] - SqlGeometry
|
||||||
|
[latitude][longitude] - SqlGeography
|
||||||
|
|
||||||
|
Figure
|
||||||
|
[FigureAttribute][PointOffset]
|
||||||
|
|
||||||
|
FigureAttribute (1 byte)
|
||||||
|
0x00 = Interior Ring
|
||||||
|
0x01 = Stroke
|
||||||
|
0x02 = Exterior Ring
|
||||||
|
|
||||||
|
Shape
|
||||||
|
[ParentFigureOffset][FigureOffset][ShapeType]
|
||||||
|
|
||||||
|
ShapeType (1 byte)
|
||||||
|
0x00 = Unknown
|
||||||
|
0x01 = Point
|
||||||
|
0x02 = LineString
|
||||||
|
0x03 = Polygon
|
||||||
|
0x04 = MultiPoint
|
||||||
|
0x05 = MultiLineString
|
||||||
|
0x06 = MultiPolygon
|
||||||
|
0x07 = GeometryCollection
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* Geometry parser macros */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
#define SP_NONE 0
|
||||||
|
#define SP_HASZVALUES 1
|
||||||
|
#define SP_HASMVALUES 2
|
||||||
|
#define SP_ISVALID 4
|
||||||
|
#define SP_ISSINGLEPOINT 8
|
||||||
|
#define SP_ISSINGLELINESEGMENT 0x10
|
||||||
|
#define SP_ISWHOLEGLOBE 0x20
|
||||||
|
|
||||||
|
#define ST_UNKNOWN 0
|
||||||
|
#define ST_POINT 1
|
||||||
|
#define ST_LINESTRING 2
|
||||||
|
#define ST_POLYGON 3
|
||||||
|
#define ST_MULTIPOINT 4
|
||||||
|
#define ST_MULTILINESTRING 5
|
||||||
|
#define ST_MULTIPOLYGON 6
|
||||||
|
#define ST_GEOMETRYCOLLECTION 7
|
||||||
|
|
||||||
|
#define ReadInt32(nPos) (*((unsigned int*)(pszData + (nPos))))
|
||||||
|
|
||||||
|
#define ReadByte(nPos) (pszData[nPos])
|
||||||
|
|
||||||
|
#define ReadDouble(nPos) (*((double*)(pszData + (nPos))))
|
||||||
|
|
||||||
|
#define ParentOffset(iShape) (ReadInt32(nShapePos + (iShape) * 9 ))
|
||||||
|
#define FigureOffset(iShape) (ReadInt32(nShapePos + (iShape) * 9 + 4))
|
||||||
|
#define ShapeType(iShape) (ReadByte(nShapePos + (iShape) * 9 + 8))
|
||||||
|
|
||||||
|
#define NextFigureOffset(iShape) (iShape + 1 < nNumShapes? FigureOffset((iShape) +1) : nNumFigures)
|
||||||
|
|
||||||
|
#define FigureAttribute(iFigure) (ReadByte(nFigurePos + (iFigure) * 5))
|
||||||
|
#define PointOffset(iFigure) (ReadInt32(nFigurePos + (iFigure) * 5 + 1))
|
||||||
|
#define NextPointOffset(iFigure) (iFigure + 1 < nNumFigures? PointOffset((iFigure) +1) : nNumPoints)
|
||||||
|
|
||||||
|
#define ReadX(iPoint) (ReadDouble(nPointPos + 16 * (iPoint)))
|
||||||
|
#define ReadY(iPoint) (ReadDouble(nPointPos + 16 * (iPoint) + 8))
|
||||||
|
#define ReadZ(iPoint) (ReadDouble(nPointPos + 16 * nNumPoints + 8 * (iPoint)))
|
||||||
|
#define ReadM(iPoint) (ReadDouble(nPointPos + 24 * nNumPoints + 8 * (iPoint)))
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* sqlserver_geometry_parser() */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
sqlserver_geometry_parser::sqlserver_geometry_parser(spatial_data_type columnType)
|
||||||
|
{
|
||||||
|
colType = columnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* ReadPoint() */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
mapnik::geometry::point<double> sqlserver_geometry_parser::ReadPoint(int iShape)
|
||||||
|
{
|
||||||
|
mapnik::geometry::point<double> geom;
|
||||||
|
int iFigure = FigureOffset(iShape);
|
||||||
|
if ( iFigure < nNumFigures ) {
|
||||||
|
int iPoint = PointOffset(iFigure);
|
||||||
|
if ( iPoint < nNumPoints ) {
|
||||||
|
if (colType == Geography) {
|
||||||
|
geom.x = ReadY(iPoint);
|
||||||
|
geom.y = ReadX(iPoint);
|
||||||
|
} else {
|
||||||
|
geom.x = ReadX(iPoint);
|
||||||
|
geom.y = ReadY(iPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* ReadMultiPoint() */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
mapnik::geometry::multi_point<double> sqlserver_geometry_parser::ReadMultiPoint(int iShape)
|
||||||
|
{
|
||||||
|
mapnik::geometry::multi_point<double> geom;
|
||||||
|
|
||||||
|
for (int i = iShape + 1; i < nNumShapes; i++) {
|
||||||
|
if (ParentOffset(i) == (unsigned int)iShape) {
|
||||||
|
if ( ShapeType(i) == ST_POINT ) {
|
||||||
|
geom.emplace_back(ReadPoint(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* ReadLineString() */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
mapnik::geometry::line_string<double> sqlserver_geometry_parser::ReadLineString(int iShape)
|
||||||
|
{
|
||||||
|
mapnik::geometry::line_string<double> geom;
|
||||||
|
int iFigure = FigureOffset(iShape);
|
||||||
|
|
||||||
|
int iPoint = PointOffset(iFigure);
|
||||||
|
int iNextPoint = NextPointOffset(iFigure);
|
||||||
|
while (iPoint < iNextPoint) {
|
||||||
|
if (colType == Geography) {
|
||||||
|
geom.emplace_back(ReadY(iPoint), ReadX(iPoint));
|
||||||
|
} else {
|
||||||
|
geom.emplace_back(ReadX(iPoint), ReadY(iPoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
++iPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* ReadMultiLineString() */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
mapnik::geometry::multi_line_string<double> sqlserver_geometry_parser::ReadMultiLineString(int iShape)
|
||||||
|
{
|
||||||
|
mapnik::geometry::multi_line_string<double> geom;
|
||||||
|
|
||||||
|
for (int i = iShape + 1; i < nNumShapes; i++) {
|
||||||
|
if (ParentOffset(i) == (unsigned int)iShape) {
|
||||||
|
if ( ShapeType(i) == ST_LINESTRING ) {
|
||||||
|
geom.emplace_back(ReadLineString(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* ReadPolygon() */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
mapnik::geometry::polygon<double> sqlserver_geometry_parser::ReadPolygon(int iShape)
|
||||||
|
{
|
||||||
|
mapnik::geometry::polygon<double> geom;
|
||||||
|
int iNextFigure = NextFigureOffset(iShape);
|
||||||
|
|
||||||
|
for (int iFigure = FigureOffset(iShape); iFigure < iNextFigure; iFigure++) {
|
||||||
|
mapnik::geometry::linear_ring<double> ring;
|
||||||
|
int iPoint = PointOffset(iFigure);
|
||||||
|
int iNextPoint = NextPointOffset(iFigure);
|
||||||
|
while (iPoint < iNextPoint) {
|
||||||
|
if (colType == Geography) {
|
||||||
|
ring.emplace_back(ReadY(iPoint), ReadX(iPoint));
|
||||||
|
} else {
|
||||||
|
ring.emplace_back(ReadX(iPoint), ReadY(iPoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
++iPoint;
|
||||||
|
}
|
||||||
|
if (iFigure == 0) {
|
||||||
|
geom.set_exterior_ring(std::move(ring));
|
||||||
|
} else {
|
||||||
|
geom.add_hole(std::move(ring));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* ReadMultiPolygon() */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
mapnik::geometry::multi_polygon<double> sqlserver_geometry_parser::ReadMultiPolygon(int iShape)
|
||||||
|
{
|
||||||
|
mapnik::geometry::multi_polygon<double> geom;
|
||||||
|
|
||||||
|
for (int i = iShape + 1; i < nNumShapes; i++) {
|
||||||
|
if (ParentOffset(i) == (unsigned int)iShape) {
|
||||||
|
if ( ShapeType(i) == ST_POLYGON ) {
|
||||||
|
geom.emplace_back(ReadPolygon(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* ReadGeometryCollection() */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
mapnik::geometry::geometry_collection<double> sqlserver_geometry_parser::ReadGeometryCollection(int iShape)
|
||||||
|
{
|
||||||
|
mapnik::geometry::geometry_collection<double> geom;
|
||||||
|
|
||||||
|
for (int i = iShape + 1; i < nNumShapes; i++) {
|
||||||
|
mapnik::geometry::geometry<double> shape = mapnik::geometry::geometry_empty();
|
||||||
|
if (ParentOffset(i) == (unsigned int)iShape) {
|
||||||
|
switch (ShapeType(i))
|
||||||
|
{
|
||||||
|
case ST_POINT:
|
||||||
|
shape = ReadPoint(i);
|
||||||
|
break;
|
||||||
|
case ST_LINESTRING:
|
||||||
|
shape = ReadLineString(i);
|
||||||
|
break;
|
||||||
|
case ST_POLYGON:
|
||||||
|
shape = ReadPolygon(i);
|
||||||
|
break;
|
||||||
|
case ST_MULTIPOINT:
|
||||||
|
shape = ReadMultiPoint(i);
|
||||||
|
break;
|
||||||
|
case ST_MULTILINESTRING:
|
||||||
|
shape = ReadMultiLineString(i);
|
||||||
|
break;
|
||||||
|
case ST_MULTIPOLYGON:
|
||||||
|
shape = ReadMultiPolygon(i);
|
||||||
|
break;
|
||||||
|
case ST_GEOMETRYCOLLECTION:
|
||||||
|
shape = ReadGeometryCollection(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
geom.push_back(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* parse_sql_geometry() */
|
||||||
|
/************************************************************************/
|
||||||
|
|
||||||
|
mapnik::geometry::geometry<double> sqlserver_geometry_parser::parse(unsigned char* pszInput, int nLen)
|
||||||
|
{
|
||||||
|
if (nLen < 10) {
|
||||||
|
throw sqlserver_geometry_parser_exception("not enough data, nLen < 10");
|
||||||
|
}
|
||||||
|
|
||||||
|
pszData = pszInput;
|
||||||
|
|
||||||
|
/* store the SRS id for further use */
|
||||||
|
nSRSId = ReadInt32(0);
|
||||||
|
|
||||||
|
if ( ReadByte(4) != 1 )
|
||||||
|
{
|
||||||
|
throw sqlserver_geometry_parser_exception("corrupt data, ReadByte(4) != 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
chProps = ReadByte(5);
|
||||||
|
|
||||||
|
if ( chProps & SP_HASMVALUES )
|
||||||
|
nPointSize = 32;
|
||||||
|
else if ( chProps & SP_HASZVALUES )
|
||||||
|
nPointSize = 24;
|
||||||
|
else
|
||||||
|
nPointSize = 16;
|
||||||
|
|
||||||
|
mapnik::geometry::geometry<double> geom;
|
||||||
|
if ( chProps & SP_ISSINGLEPOINT )
|
||||||
|
{
|
||||||
|
// single point geometry
|
||||||
|
nNumPoints = 1;
|
||||||
|
nPointPos = 6;
|
||||||
|
|
||||||
|
if (nLen < 6 + nPointSize)
|
||||||
|
{
|
||||||
|
throw sqlserver_geometry_parser_exception("not enough data, nLen < 6 + nPointSize");
|
||||||
|
}
|
||||||
|
|
||||||
|
mapnik::geometry::point<double> point;
|
||||||
|
|
||||||
|
if (colType == Geography)
|
||||||
|
{
|
||||||
|
point.x = ReadY(0);
|
||||||
|
point.y = ReadX(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
point.x = ReadX(0);
|
||||||
|
point.y = ReadY(0);
|
||||||
|
}
|
||||||
|
geom = point;
|
||||||
|
}
|
||||||
|
else if ( chProps & SP_ISSINGLELINESEGMENT )
|
||||||
|
{
|
||||||
|
// single line segment with 2 points
|
||||||
|
nNumPoints = 2;
|
||||||
|
nPointPos = 6;
|
||||||
|
|
||||||
|
if (nLen < 6 + 2 * nPointSize)
|
||||||
|
{
|
||||||
|
throw sqlserver_geometry_parser_exception("not enough data, nLen < 6 + 2 * nPointSize");
|
||||||
|
}
|
||||||
|
|
||||||
|
mapnik::geometry::line_string<double> line;
|
||||||
|
|
||||||
|
if (colType == Geography)
|
||||||
|
{
|
||||||
|
line.emplace_back(ReadY(0), ReadX(0));
|
||||||
|
line.emplace_back(ReadY(1), ReadX(1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line.emplace_back(ReadX(0), ReadY(0));
|
||||||
|
line.emplace_back(ReadX(1), ReadY(1));
|
||||||
|
}
|
||||||
|
geom = line;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// complex geometries
|
||||||
|
nNumPoints = ReadInt32(6);
|
||||||
|
|
||||||
|
if ( nNumPoints <= 0 )
|
||||||
|
{
|
||||||
|
throw sqlserver_geometry_parser_exception("negative number of points, nNumPoints <= 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// position of the point array
|
||||||
|
nPointPos = 10;
|
||||||
|
|
||||||
|
// position of the figures
|
||||||
|
nFigurePos = nPointPos + nPointSize * nNumPoints + 4;
|
||||||
|
|
||||||
|
if (nLen < nFigurePos)
|
||||||
|
{
|
||||||
|
throw sqlserver_geometry_parser_exception("not enough data, nLen < nFigurePos");
|
||||||
|
}
|
||||||
|
|
||||||
|
nNumFigures = ReadInt32(nFigurePos - 4);
|
||||||
|
|
||||||
|
if ( nNumFigures <= 0 )
|
||||||
|
{
|
||||||
|
throw sqlserver_geometry_parser_exception("negative number of figures, nNumFigures <= 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// position of the shapes
|
||||||
|
nShapePos = nFigurePos + 5 * nNumFigures + 4;
|
||||||
|
|
||||||
|
if (nLen < nShapePos)
|
||||||
|
{
|
||||||
|
throw sqlserver_geometry_parser_exception("not enough data, nLen < nShapePos");
|
||||||
|
}
|
||||||
|
|
||||||
|
nNumShapes = ReadInt32(nShapePos - 4);
|
||||||
|
|
||||||
|
if (nLen < nShapePos + 9 * nNumShapes)
|
||||||
|
{
|
||||||
|
throw sqlserver_geometry_parser_exception("not enough data, nLen < nShapePos + 9 * nNumShapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( nNumShapes <= 0 )
|
||||||
|
{
|
||||||
|
throw sqlserver_geometry_parser_exception("negative number of shapes, nNumShapes <= 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// pick up the root shape
|
||||||
|
if ( ParentOffset(0) != 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
throw sqlserver_geometry_parser_exception("corrupt data, ParentOffset(0) != 0xFFFFFFFF");
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine the shape type
|
||||||
|
switch (ShapeType(0))
|
||||||
|
{
|
||||||
|
case ST_POINT:
|
||||||
|
geom = ReadPoint(0);
|
||||||
|
break;
|
||||||
|
case ST_LINESTRING:
|
||||||
|
geom = ReadLineString(0);
|
||||||
|
break;
|
||||||
|
case ST_POLYGON:
|
||||||
|
geom = ReadPolygon(0);
|
||||||
|
break;
|
||||||
|
case ST_MULTIPOINT:
|
||||||
|
geom = ReadMultiPoint(0);
|
||||||
|
break;
|
||||||
|
case ST_MULTILINESTRING:
|
||||||
|
geom = ReadMultiLineString(0);
|
||||||
|
break;
|
||||||
|
case ST_MULTIPOLYGON:
|
||||||
|
geom = ReadMultiPolygon(0);
|
||||||
|
break;
|
||||||
|
case ST_GEOMETRYCOLLECTION:
|
||||||
|
geom = ReadGeometryCollection(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw sqlserver_geometry_parser_exception("unsupported geometry type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
45
plugins/input/sqlserver/sqlserver_geometry_parser.hpp
Normal file
45
plugins/input/sqlserver/sqlserver_geometry_parser.hpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// mapnik
|
||||||
|
#include <mapnik/geometry.hpp>
|
||||||
|
|
||||||
|
enum spatial_data_type {
|
||||||
|
Geometry,
|
||||||
|
Geography
|
||||||
|
};
|
||||||
|
|
||||||
|
class sqlserver_geometry_parser
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
unsigned char* pszData;
|
||||||
|
/* serialization propeties */
|
||||||
|
char chProps;
|
||||||
|
/* point array */
|
||||||
|
int nPointSize;
|
||||||
|
int nPointPos;
|
||||||
|
int nNumPoints;
|
||||||
|
/* figure array */
|
||||||
|
int nFigurePos;
|
||||||
|
int nNumFigures;
|
||||||
|
/* shape array */
|
||||||
|
int nShapePos;
|
||||||
|
int nNumShapes;
|
||||||
|
int nSRSId;
|
||||||
|
/* geometry or geography */
|
||||||
|
spatial_data_type colType;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mapnik::geometry::point<double> ReadPoint(int iShape);
|
||||||
|
mapnik::geometry::multi_point<double> ReadMultiPoint(int iShape);
|
||||||
|
mapnik::geometry::line_string<double> ReadLineString(int iShape);
|
||||||
|
mapnik::geometry::multi_line_string<double> ReadMultiLineString(int iShape);
|
||||||
|
mapnik::geometry::polygon<double> ReadPolygon(int iShape);
|
||||||
|
mapnik::geometry::multi_polygon<double> ReadMultiPolygon(int iShape);
|
||||||
|
mapnik::geometry::geometry_collection<double> ReadGeometryCollection(int iShape);
|
||||||
|
|
||||||
|
public:
|
||||||
|
sqlserver_geometry_parser(spatial_data_type columnType);
|
||||||
|
|
||||||
|
mapnik::geometry::geometry<double> parse(unsigned char* pszInput, int nLen);
|
||||||
|
int get_srs_id() { return nSRSId; };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue