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