/***************************************************************************** * * This file is part of Mapnik (c++ mapping toolkit) * * Copyright (C) 2014 Artem Pavlenko * * This library 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 * *****************************************************************************/ // 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/value_types.hpp> #include <mapnik/feature_factory.hpp> #include <mapnik/make_unique.hpp> // ogr #include "occi_featureset.hpp" using mapnik::query; using mapnik::box2d; using mapnik::feature_ptr; using mapnik::geometry_type; using mapnik::geometry_utils; using mapnik::transcoder; using mapnik::datasource_exception; using mapnik::feature_factory; using oracle::occi::Connection; using oracle::occi::Statement; using oracle::occi::ResultSet; using oracle::occi::StatelessConnectionPool; using oracle::occi::MetaData; using oracle::occi::SQLException; using oracle::occi::Type; using oracle::occi::Number; using oracle::occi::Blob; occi_featureset::occi_featureset(StatelessConnectionPool* pool, Connection* conn, mapnik::context_ptr const& ctx, std::string const& sqlstring, std::string const& encoding, bool use_connection_pool, bool use_wkb, unsigned prefetch_rows) : rs_(nullptr), tr_(new transcoder(encoding)), feature_id_(1), ctx_(ctx), use_wkb_(use_wkb) { if (use_connection_pool) { conn_.set_pool(pool); } else { conn_.set_connection(conn, false); } try { rs_ = conn_.execute_query(sqlstring, prefetch_rows); } catch (SQLException &ex) { MAPNIK_LOG_ERROR(occi) << "OCCI Plugin: error processing " << sqlstring << " : " << ex.getMessage(); rs_ = nullptr; } } occi_featureset::~occi_featureset() { } feature_ptr occi_featureset::next() { while (rs_ != nullptr && rs_->next() == oracle::occi::ResultSet::DATA_AVAILABLE) { feature_ptr feature(feature_factory::create(ctx_, feature_id_)); if (use_wkb_) { Blob blob = rs_->getBlob(1); blob.open(oracle::occi::OCCI_LOB_READONLY); unsigned int size = blob.length(); if (buffer_.size() < size) { buffer_.resize(size); } oracle::occi::Stream* instream = blob.getStream(1, 0); instream->readBuffer(buffer_.data(), size); blob.closeStream(instream); blob.close(); if (! geometry_utils::from_wkb(feature->paths(), buffer_.data(), size)) { continue; } } else { const std::unique_ptr<SDOGeometry> geom(dynamic_cast<SDOGeometry*>(rs_->getObject(1))); if (geom.get()) { convert_geometry(geom.get(), feature); } else { continue; } } std::vector<MetaData> listOfColumns = rs_->getColumnListMetaData(); for (unsigned int i = 1; i < listOfColumns.size(); ++i) { MetaData columnObj = listOfColumns[i]; std::string fld_name = columnObj.getString(MetaData::ATTR_NAME); int type_oid = columnObj.getInt(MetaData::ATTR_DATA_TYPE); /* int type_code = columnObj.getInt(MetaData::ATTR_TYPECODE); if (type_code == OCCI_TYPECODE_OBJECT) { continue; } */ switch (type_oid) { case oracle::occi::OCCIBOOL: feature->put(fld_name, (rs_->getInt(i + 1) != 0)); break; case oracle::occi::OCCIINT: case oracle::occi::OCCIUNSIGNED_INT: feature->put(fld_name, static_cast<mapnik::value_integer>(rs_->getInt(i + 1))); break; case oracle::occi::OCCIFLOAT: case oracle::occi::OCCIBFLOAT: feature->put(fld_name, (double)rs_->getFloat(i + 1)); break; case oracle::occi::OCCIDOUBLE: case oracle::occi::OCCIBDOUBLE: case oracle::occi::OCCINUMBER: case oracle::occi::OCCI_SQLT_NUM: feature->put(fld_name, rs_->getDouble(i + 1)); break; case oracle::occi::OCCICHAR: case oracle::occi::OCCISTRING: case oracle::occi::OCCI_SQLT_AFC: case oracle::occi::OCCI_SQLT_AVC: case oracle::occi::OCCI_SQLT_CHR: case oracle::occi::OCCI_SQLT_LNG: case oracle::occi::OCCI_SQLT_LVC: case oracle::occi::OCCI_SQLT_STR: case oracle::occi::OCCI_SQLT_VCS: case oracle::occi::OCCI_SQLT_VNU: case oracle::occi::OCCI_SQLT_VBI: case oracle::occi::OCCI_SQLT_VST: case oracle::occi::OCCIROWID: case oracle::occi::OCCI_SQLT_RDD: case oracle::occi::OCCI_SQLT_RID: case oracle::occi::OCCIDATE: case oracle::occi::OCCI_SQLT_DAT: case oracle::occi::OCCI_SQLT_DATE: case oracle::occi::OCCI_SQLT_TIME: case oracle::occi::OCCI_SQLT_TIME_TZ: case oracle::occi::OCCITIMESTAMP: case oracle::occi::OCCI_SQLT_TIMESTAMP: case oracle::occi::OCCI_SQLT_TIMESTAMP_LTZ: case oracle::occi::OCCI_SQLT_TIMESTAMP_TZ: feature->put(fld_name, static_cast<mapnik::value_unicode_string>(tr_->transcode(rs_->getString(i + 1).c_str()))); break; case oracle::occi::OCCIINTERVALDS: case oracle::occi::OCCIINTERVALYM: case oracle::occi::OCCI_SQLT_INTERVAL_YM: case oracle::occi::OCCI_SQLT_INTERVAL_DS: case oracle::occi::OCCIANYDATA: case oracle::occi::OCCIBLOB: case oracle::occi::OCCIBFILE: case oracle::occi::OCCIBYTES: case oracle::occi::OCCICLOB: case oracle::occi::OCCIVECTOR: case oracle::occi::OCCIMETADATA: case oracle::occi::OCCIPOBJECT: case oracle::occi::OCCIREF: case oracle::occi::OCCIREFANY: case oracle::occi::OCCISTREAM: case oracle::occi::OCCICURSOR: case oracle::occi::OCCI_SQLT_FILE: case oracle::occi::OCCI_SQLT_CFILE: case oracle::occi::OCCI_SQLT_REF: case oracle::occi::OCCI_SQLT_CLOB: case oracle::occi::OCCI_SQLT_BLOB: case oracle::occi::OCCI_SQLT_RSET: { MAPNIK_LOG_WARN(occi) << "occi_featureset: Unsupported datatype " << occi_enums::resolve_datatype(type_oid) << " (type_oid=" << type_oid << ")"; break; } default: // shouldn't get here { MAPNIK_LOG_WARN(occi) << "occi_featureset: Unknown datatype " << "(type_oid=" << type_oid << ")"; break; } } } ++feature_id_; return feature; } return feature_ptr(); } void occi_featureset::convert_geometry(SDOGeometry* geom, feature_ptr feature) { int gtype = (int)geom->getSdo_gtype(); int dimensions = gtype / 1000; int lrsvalue = (gtype - dimensions * 1000) / 100; int geomtype = (gtype - dimensions * 1000 - lrsvalue * 100); const std::vector<Number>& elem_info = geom->getSdo_elem_info(); const std::vector<Number>& ordinates = geom->getSdo_ordinates(); const int ordinates_size = (int)ordinates.size(); switch (geomtype) { case SDO_GTYPE_POINT: { SDOPointType* sdopoint = geom->getSdo_point(); if (sdopoint && ! sdopoint->isNull()) { std::unique_ptr<geometry_type> point = std::make_unique<geometry_type>(mapnik::geometry_type::types::Point); point->move_to(sdopoint->getX(), sdopoint->getY()); feature->add_geometry(point.release()); } } break; case SDO_GTYPE_LINE: { if (ordinates_size >= dimensions) { const bool is_single_geom = true; const bool is_point_type = false; convert_ordinates(feature, mapnik::geometry_type::types::LineString, elem_info, ordinates, dimensions, is_single_geom, is_point_type); } } break; case SDO_GTYPE_POLYGON: { if (ordinates_size >= dimensions) { const bool is_single_geom = true; const bool is_point_type = false; convert_ordinates(feature, mapnik::geometry_type::types::Polygon, elem_info, ordinates, dimensions, is_single_geom, is_point_type); } } break; case SDO_GTYPE_MULTIPOINT: { if (ordinates_size >= dimensions) { const bool is_single_geom = false; const bool is_point_type = true; convert_ordinates(feature, mapnik::geometry_type::types::Point, elem_info, ordinates, dimensions, is_single_geom, is_point_type); } } break; case SDO_GTYPE_MULTILINE: { if (ordinates_size >= dimensions) { const bool is_single_geom = false; const bool is_point_type = false; convert_ordinates(feature, mapnik::geometry_type::types::LineString, elem_info, ordinates, dimensions, is_single_geom, is_point_type); } } break; case SDO_GTYPE_MULTIPOLYGON: { if (ordinates_size >= dimensions) { const bool is_single_geom = false; const bool is_point_type = false; convert_ordinates(feature, mapnik::geometry_type::types::Polygon, elem_info, ordinates, dimensions, is_single_geom, is_point_type); } } break; case SDO_GTYPE_COLLECTION: { if (ordinates_size >= dimensions) { const bool is_single_geom = false; const bool is_point_type = false; convert_ordinates(feature, mapnik::geometry_type::types::Polygon, elem_info, ordinates, dimensions, is_single_geom, is_point_type); } } break; case SDO_GTYPE_UNKNOWN: default: { MAPNIK_LOG_WARN(occi) << "occi_featureset: Unknown oracle enum " << occi_enums::resolve_gtype(geomtype) << "(gtype=" << gtype << ")"; } break; } } void occi_featureset::convert_ordinates(mapnik::feature_ptr feature, const mapnik::geometry_type::types& geom_type, const std::vector<Number>& elem_info, const std::vector<Number>& ordinates, const int dimensions, const bool is_single_geom, const bool is_point_geom) { const int elem_size = elem_info.size(); const int ord_size = ordinates.size(); if (elem_size >= 0) { int offset = elem_info[0]; int etype = elem_info[1]; int interp = elem_info[2]; if (! is_single_geom && elem_size > SDO_ELEM_INFO_SIZE) { geometry_type* geom = new geometry_type(geom_type); for (int i = SDO_ELEM_INFO_SIZE; i < elem_size; i+=3) { int next_offset = elem_info[i]; int next_etype = elem_info[i + 1]; int next_interp = elem_info[i + 2]; bool is_linear_element = true; bool is_unknown_etype = false; mapnik::geometry_type::types gtype = mapnik::geometry_type::types::Point; switch (etype) { case SDO_ETYPE_POINT: if (interp == SDO_INTERPRETATION_POINT) {} if (interp > SDO_INTERPRETATION_POINT) {} gtype = mapnik::geometry_type::types::Point; break; case SDO_ETYPE_LINESTRING: if (interp == SDO_INTERPRETATION_STRAIGHT) {} if (interp == SDO_INTERPRETATION_CIRCULAR) {} gtype = mapnik::geometry_type::types::LineString; break; case SDO_ETYPE_POLYGON: case SDO_ETYPE_POLYGON_INTERIOR: if (interp == SDO_INTERPRETATION_STRAIGHT) {} if (interp == SDO_INTERPRETATION_CIRCULAR) {} if (interp == SDO_INTERPRETATION_RECTANGLE) {} if (interp == SDO_INTERPRETATION_CIRCLE) {} gtype = mapnik::geometry_type::types::Polygon; break; case SDO_ETYPE_COMPOUND_LINESTRING: case SDO_ETYPE_COMPOUND_POLYGON: case SDO_ETYPE_COMPOUND_POLYGON_INTERIOR: // interp = next ETYPE to consider is_linear_element = false; gtype = mapnik::geometry_type::types::Polygon; break; case SDO_ETYPE_UNKNOWN: // unknown default: is_unknown_etype = true; break; } if (is_unknown_etype) { break; } if (is_linear_element) { if (geom) { feature->add_geometry(geom); } geom = new geometry_type(gtype); fill_geometry_type(geom, offset - 1, next_offset - 1, ordinates, dimensions, is_point_geom); } offset = next_offset; etype = next_etype; interp = next_interp; } if (geom) { feature->add_geometry(geom); geom = 0; } } else { geometry_type * geom = new geometry_type(geom_type); fill_geometry_type(geom, offset - 1, ord_size, ordinates, dimensions, is_point_geom); feature->add_geometry(geom); } } } void occi_featureset::fill_geometry_type(geometry_type* geom, const int real_offset, const int next_offset, const std::vector<Number>& ordinates, const int dimensions, const bool is_point_geom) { geom->move_to((double) ordinates[real_offset], (double) ordinates[real_offset + 1]); if (is_point_geom) { for (int p = real_offset + dimensions; p < next_offset; p += dimensions) { geom->move_to((double) ordinates[p], (double) ordinates[p + 1]); } } else { for (int p = real_offset + dimensions; p < next_offset; p += dimensions) { geom->line_to((double) ordinates[p], (double) ordinates[p + 1]); } } }