/****************************************************************************** * Project: GDAL * Purpose: Implements The Two-Arm Chains EdgeTracing Algorithm * Author: kikitte.lee * ****************************************************************************** * Copyright (c) 2023, kikitte.lee * * 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. ****************************************************************************/ #ifndef POLYGONIZE_POLYGONIZER_H_INCLUDED #define POLYGONIZE_POLYGONIZER_H_INCLUDED /*! @cond Doxygen_Suppress */ // Implements Junhua Teng, Fahui Wang, Yu Liu: An Efficient Algorithm for // Raster-to-Vector Data Conversion: https://doi.org/10.1080/10824000809480639 #include #include #include #include #include #include "cpl_error.h" #include "ogr_api.h" namespace gdal { namespace polygonizer { using IndexType = std::uint32_t; using Point = std::array; using Arc = std::vector; struct IndexedArc { Arc *poArc; std::size_t iIndex; }; /** * A raster polygon(RPolygon) is formed by a list of arcs in order. * Each arc has two properties: * 1. does the arc follows the right-hand rule respect to the area it bounds * 2. the next arc of the current arc */ struct RPolygon { IndexType iBottomRightRow{0}; IndexType iBottomRightCol{0}; // arc object list std::vector oArcs{}; // does arc follows the right-hand rule with std::vector oArcRighthandFollow{}; // each element is the next arc index of the current arc std::vector oArcConnections{}; RPolygon() = default; RPolygon(const RPolygon &) = delete; RPolygon &operator=(const RPolygon &) = delete; ~RPolygon(); /** * create a new arc object */ IndexedArc newArc(bool bFollowRighthand); /** * set the next arc index of the current arc */ void setArcConnection(IndexedArc &oArc, IndexedArc &oNextArc); /** * update the bottom-right most cell index of the current polygon */ void updateBottomRightPos(IndexType iRow, IndexType iCol); }; /** * Arm class is used to record the tracings of both arcs and polygons. */ struct TwoArm { IndexType iRow{0}; IndexType iCol{0}; RPolygon *poPolyInside{nullptr}; RPolygon *poPolyAbove{nullptr}; RPolygon *poPolyLeft{nullptr}; IndexedArc oArcHorOuter{}; IndexedArc oArcHorInner{}; IndexedArc oArcVerInner{}; IndexedArc oArcVerOuter{}; bool bSolidHorizontal{false}; bool bSolidVertical{false}; }; template class PolygonReceiver { public: PolygonReceiver() = default; PolygonReceiver(const PolygonReceiver &) = delete; virtual ~PolygonReceiver() = default; PolygonReceiver & operator=(const PolygonReceiver &) = delete; virtual void receive(RPolygon *poPolygon, DataType nPolygonCellValue) = 0; }; /** * Polygonizer is used to manage polygon memory and do the edge tracing process */ template class Polygonizer { public: static constexpr PolyIdType THE_OUTER_POLYGON_ID = std::numeric_limits::max(); private: using PolygonMap = std::map; using PolygonMapEntry = typename PolygonMap::value_type; PolyIdType nInvalidPolyId_; RPolygon *poTheOuterPolygon_{nullptr}; PolygonMap oPolygonMap_{}; PolygonReceiver *poPolygonReceiver_; RPolygon *getPolygon(PolyIdType nPolygonId); RPolygon *createPolygon(PolyIdType nPolygonId); void destroyPolygon(PolyIdType nPolygonId); public: explicit Polygonizer(PolyIdType nInvalidPolyId, PolygonReceiver *poPolygonReceiver); Polygonizer(const Polygonizer &) = delete; ~Polygonizer(); Polygonizer & operator=(const Polygonizer &) = delete; RPolygon *getTheOuterPolygon() const { return poTheOuterPolygon_; } void processLine(const PolyIdType *panThisLineId, const DataType *panLastLineVal, TwoArm *poThisLineArm, TwoArm *poLastLineArm, IndexType nCurrentRow, IndexType nCols); }; /** * Write raster polygon object to OGR layer. */ template class OGRPolygonWriter : public PolygonReceiver { OGRLayerH hOutLayer_; int iPixValField_; double *padfGeoTransform_; CPLErr eErr_{CE_None}; public: OGRPolygonWriter(OGRLayerH hOutLayer, int iPixValField, double *padfGeoTransform); OGRPolygonWriter(const OGRPolygonWriter &) = delete; ~OGRPolygonWriter() = default; OGRPolygonWriter & operator=(const OGRPolygonWriter &) = delete; void receive(RPolygon *poPolygon, DataType nPolygonCellValue) override; inline CPLErr getErr() { return eErr_; } }; } // namespace polygonizer } // namespace gdal /*! @endcond */ #endif /* POLYGONIZE_POLYGONIZER_H_INCLUDED */