dxf export: fix export of curved geometries

This commit is contained in:
Juergen E. Fischer 2019-06-21 01:23:17 +02:00 committed by Jürgen Fischer
parent 059277ceca
commit 88cb4d4eda
5 changed files with 286 additions and 48 deletions

View File

@ -335,6 +335,8 @@ Write a tuple of group code and a handle
void writeLine( const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width = -1 );
%Docstring
Write line (as a polyline)

View File

@ -29,6 +29,11 @@
#include "qgsdxfexport.h"
#include "qgsdxfpallabeling.h"
#include "qgsgeometrygeneratorsymbollayer.h"
#include "qgsgeometrycollection.h"
#include "qgscurvepolygon.h"
#include "qgscompoundcurve.h"
#include "qgscircularstring.h"
#include "qgslinestring.h"
#include "qgsvectordataprovider.h"
#include "qgspointxy.h"
#include "qgsproject.h"
@ -3545,6 +3550,150 @@ void QgsDxfExport::writePolyline( const QgsPointSequence &line, const QString &l
}
}
void QgsDxfExport::appendCurve( const QgsCurve &c, QVector<QgsPoint> &points, QVector<double> &bulges )
{
switch ( QgsWkbTypes::flatType( c.wkbType() ) )
{
case QgsWkbTypes::LineString:
appendLineString( *dynamic_cast<const QgsLineString *>( &c ), points, bulges );
break;
case QgsWkbTypes::CircularString:
appendCircularString( *dynamic_cast<const QgsCircularString *>( &c ), points, bulges );
break;
case QgsWkbTypes::CompoundCurve:
appendCompoundCurve( *dynamic_cast<const QgsCompoundCurve *>( &c ), points, bulges );
break;
default:
QgsMessageLog( QStringLiteral( "Unexpected curve type %1" ).arg( c.wktTypeStr() ) );
break;
}
}
void QgsDxfExport::appendLineString( const QgsLineString &ls, QVector<QgsPoint> &points, QVector<double> &bulges )
{
for ( int i = 0; i < ls.numPoints(); i++ )
{
const QgsPoint &p = ls.pointN( i );
if ( !points.isEmpty() && points.last() == p )
continue;
points << p;
bulges << 0.0;
}
}
void QgsDxfExport::appendCircularString( const QgsCircularString &cs, QVector<QgsPoint> &points, QVector<double> &bulges )
{
for ( int i = 0; i < cs.numPoints() - 2; i += 2 )
{
const QgsPoint &p1 = cs.pointN( i );
const QgsPoint &p2 = cs.pointN( i + 1 );
const QgsPoint &p3 = cs.pointN( i + 2 );
if ( points.isEmpty() || points.last() != p1 )
points << p1;
else if ( !bulges.isEmpty() )
bulges.removeLast();
double a = ( M_PI - ( p1 - p2 ).angle() + ( p3 - p2 ).angle() ) / 2.0;
bulges << sin( a ) / cos( a );
points << p3;
bulges << 0.0;
}
}
void QgsDxfExport::appendCompoundCurve( const QgsCompoundCurve &cc, QVector<QgsPoint> &points, QVector<double> &bulges )
{
for ( int i = 0; i < cc.nCurves(); i++ )
{
const QgsCurve *c = cc.curveAt( i );
Q_ASSERT( c );
appendCurve( *c, points, bulges );
}
}
void QgsDxfExport::writePolyline( const QgsCurve &curve, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
{
int n = curve.numPoints();
if ( n == 0 )
{
QgsDebugMsg( QStringLiteral( "writePolyline: empty line layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
return;
}
if ( n < 2 )
{
QgsDebugMsg( QStringLiteral( "writePolyline: line too short layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
return;
}
QVector<QgsPoint> points;
QVector<double> bulges;
appendCurve( curve, points, bulges );
if ( mForce2d || !curve.is3D() )
{
writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
writeHandle();
writeGroup( 8, layer );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
writeGroup( 6, lineStyleName );
writeGroup( color );
writeGroup( 90, points.size() );
writeGroup( 70, ( curve.isClosed() ? 1 : 0 ) | ( curve.hasCurvedSegments() ? 2 : 0 ) );
writeGroup( 43, width );
for ( int i = 0; i < points.size(); i++ )
{
writeGroup( 0, points[i] );
if ( bulges[i] != 0.0 )
writeGroup( 42, bulges[i] );
}
}
else
{
writeGroup( 0, QStringLiteral( "POLYLINE" ) );
int plHandle = writeHandle();
writeGroup( 330, mBlockHandle );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, layer );
writeGroup( 6, lineStyleName );
writeGroup( color );
writeGroup( 100, QStringLiteral( "AcDb3dPolyline" ) );
writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ ) );
writeGroup( 70, 8 );
for ( int i = 0; i < points.size(); i++ )
{
writeGroup( 0, QStringLiteral( "VERTEX" ) );
writeHandle();
writeGroup( 330, plHandle );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, layer );
writeGroup( color );
writeGroup( 100, QStringLiteral( "AcDbVertex" ) );
writeGroup( 100, QStringLiteral( "AcDb3dPolylineVertex" ) );
writeGroup( 0, points[i] );
if ( bulges[i] != 0.0 )
writeGroup( 42, bulges[i] );
writeGroup( 70, 32 );
}
writeGroup( 0, QStringLiteral( "SEQEND" ) );
writeHandle();
writeGroup( 330, plHandle );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, layer );
writeGroup( color );
}
}
void QgsDxfExport::writePolygon( const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
{
writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
@ -3568,7 +3717,7 @@ void QgsDxfExport::writePolygon( const QgsRingSequence &polygon, const QString &
writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
writeGroup( 72, 0 ); // Has bulge flag
writeGroup( 73, 1 ); // Is closed flag
writeGroup( 93, polygon[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline
writeGroup( 93, polygon[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline)
for ( int j = 0; j < polygon[i].size(); ++j )
{
@ -3584,6 +3733,67 @@ void QgsDxfExport::writePolygon( const QgsRingSequence &polygon, const QString &
writeGroup( 98, 0 ); // Number of seed points
}
void QgsDxfExport::writePolygon( const QgsCurvePolygon &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
{
writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
writeHandle();
writeGroup( 330, mBlockHandle );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, layer ); // Layer name
writeGroup( color ); // Color
writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ ) ); // Elevation point (in OCS)
writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
writeGroup( 2, hatchPattern ); // Hatch pattern name
writeGroup( 70, hatchPattern == QLatin1String( "SOLID" ) ); // Solid fill flag (solid fill = 1; pattern fill = 0)
writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
QVector<QVector<QgsPoint>> points;
QVector<QVector<double>> bulges;
points << QVector<QgsPoint>();
bulges << QVector<double>();
appendCurve( *polygon.exteriorRing(), points.last(), bulges.last() );
for ( int i = 0; i < polygon.numInteriorRings(); i++ )
{
points << QVector<QgsPoint>();
bulges << QVector<double>();
appendCurve( *polygon.interiorRing( i ), points.last(), bulges.last() );
}
bool hasBulges = false;
for ( int i = 0; i < points.size() && !hasBulges; ++i )
for ( int j = 0; j < points[i].size() && !hasBulges; ++j )
hasBulges = bulges[i][j] != 0.0;
writeGroup( 91, points.size() ); // Number of boundary paths (loops)
for ( int i = 0; i < points.size(); ++i )
{
writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
writeGroup( 72, hasBulges ? 1 : 0 ); // Has bulge flag
writeGroup( 73, 1 ); // Is closed flag
writeGroup( 93, points[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline)
for ( int j = 0; j < points[i].size(); ++j )
{
writeGroup( 0, points[i][j] ); // Vertex location (in OCS)
if ( hasBulges )
writeGroup( 42, bulges[i][j] );
}
writeGroup( 97, 0 ); // Number of source boundary objects
}
writeGroup( 75, 0 ); // Hatch style: 0 = Hatch "odd parity" area (Normal style), 1 = Hatch outermost area only (Outer style), 2 = Hatch through entire area (Ignore style)
writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
writeGroup( 98, 0 ); // Number of seed points
}
void QgsDxfExport::writeLine( const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
{
writePolyline( QgsPointSequence() << pt1 << pt2, layer, lineStyleName, color, width );
@ -3787,12 +3997,8 @@ void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateT
{
case QgsWkbTypes::CircularString:
case QgsWkbTypes::CompoundCurve:
tempGeom.reset( geom.constGet()->segmentize() );
sourceGeom = tempGeom.get();
if ( !tempGeom )
break;
FALLTHROUGH
case QgsWkbTypes::LineString:
{
if ( !qgsDoubleNear( offset, 0.0 ) )
{
QgsGeos geos( sourceGeom );
@ -3803,16 +4009,14 @@ void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateT
sourceGeom = geom.constGet();
}
writePolyline( sourceGeom->coordinateSequence().at( 0 ).at( 0 ), layer, lineStyleName, penColor, width );
const QgsCurve *curve = dynamic_cast<const QgsCurve *>( sourceGeom );
Q_ASSERT( curve );
writePolyline( *curve, layer, lineStyleName, penColor, width );
break;
}
case QgsWkbTypes::MultiCurve:
tempGeom.reset( geom.constGet()->segmentize() );
if ( !tempGeom )
break;
sourceGeom = tempGeom.get();
FALLTHROUGH
case QgsWkbTypes::MultiLineString:
{
if ( !qgsDoubleNear( offset, 0.0 ) )
@ -3825,21 +4029,20 @@ void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateT
sourceGeom = geom.constGet();
}
const QgsCoordinateSequence &cs = sourceGeom->coordinateSequence();
for ( int i = 0; i < cs.size(); i++ )
const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
Q_ASSERT( gc );
for ( int i = 0; i < gc->numGeometries(); i++ )
{
writePolyline( cs.at( i ).at( 0 ), layer, lineStyleName, penColor, width );
const QgsCurve *curve = dynamic_cast<const QgsCurve *>( gc->geometryN( i ) );
Q_ASSERT( curve );
writePolyline( *curve, layer, lineStyleName, penColor, width );
}
break;
}
case QgsWkbTypes::CurvePolygon:
tempGeom.reset( geom.constGet()->segmentize() );
if ( !tempGeom )
break;
sourceGeom = tempGeom.get();
FALLTHROUGH
case QgsWkbTypes::Polygon:
{
if ( !qgsDoubleNear( offset, 0.0 ) )
@ -3852,21 +4055,17 @@ void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateT
sourceGeom = geom.constGet();
}
const QgsCoordinateSequence &cs = sourceGeom->coordinateSequence();
for ( int i = 0; i < cs.at( 0 ).size(); i++ )
{
writePolyline( cs.at( 0 ).at( i ), layer, lineStyleName, penColor, width );
}
const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
Q_ASSERT( polygon );
writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
for ( int i = 0; i < polygon->numInteriorRings(); i++ )
writePolyline( *polygon->interiorRing( i ), layer, lineStyleName, penColor, width );
break;
}
case QgsWkbTypes::MultiSurface:
tempGeom.reset( geom.constGet()->segmentize() );
if ( !tempGeom )
break;
sourceGeom = tempGeom.get();
FALLTHROUGH
case QgsWkbTypes::MultiPolygon:
{
if ( !qgsDoubleNear( offset, 0.0 ) )
@ -3879,10 +4078,18 @@ void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateT
sourceGeom = geom.constGet();
}
const QgsCoordinateSequence &cs = sourceGeom->coordinateSequence();
for ( int i = 0; i < cs.size(); i++ )
for ( int j = 0; j < cs.at( i ).size(); j++ )
writePolyline( cs.at( i ).at( j ), layer, lineStyleName, penColor, width );
const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
Q_ASSERT( gc );
for ( int i = 0; i < gc->numGeometries(); i++ )
{
const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
Q_ASSERT( polygon );
writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
for ( int j = 0; j < polygon->numInteriorRings(); j++ )
writePolyline( *polygon->interiorRing( j ), layer, lineStyleName, penColor, width );
}
break;
}
@ -3901,27 +4108,25 @@ void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateT
switch ( QgsWkbTypes::flatType( geometryType ) )
{
case QgsWkbTypes::CurvePolygon:
tempGeom.reset( geom.constGet()->segmentize() );
if ( !tempGeom )
break;
sourceGeom = tempGeom.get();
FALLTHROUGH
case QgsWkbTypes::Polygon:
writePolygon( sourceGeom->coordinateSequence().at( 0 ), layer, QStringLiteral( "SOLID" ), brushColor );
{
const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
Q_ASSERT( polygon );
writePolygon( *polygon, layer, QStringLiteral( "SOLID" ), brushColor );
break;
}
case QgsWkbTypes::MultiSurface:
tempGeom.reset( geom.constGet()->segmentize() );
if ( !tempGeom )
break;
sourceGeom = tempGeom.get();
FALLTHROUGH
case QgsWkbTypes::MultiPolygon:
{
const QgsCoordinateSequence &cs = sourceGeom->coordinateSequence();
for ( int i = 0; i < cs.size(); i++ )
const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
Q_ASSERT( gc );
for ( int i = 0; i < gc->numGeometries(); i++ )
{
writePolygon( cs.at( i ), layer, QStringLiteral( "SOLID" ), brushColor );
const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
Q_ASSERT( polygon );
writePolygon( *polygon, layer, QStringLiteral( "SOLID" ), brushColor );
}
break;
}

View File

@ -34,6 +34,10 @@ class QgsPointXY;
class QgsSymbolLayer;
class QIODevice;
class QgsPalLayerSettings;
class QgsCurve;
class QgsCurvePolygon;
class QgsCircularString;
class QgsCompoundCurve;
#define DXF_HANDSEED 100
#define DXF_HANDMAX 9999999
@ -332,6 +336,18 @@ class CORE_EXPORT QgsDxfExport
*/
void writePolyline( const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width = -1 ) SIP_SKIP;
/**
* Draw dxf primitives (LWPOLYLINE)
* \param geom polyline (including curved)
* \param layer layer name to use
* \param lineStyleName line type to use
* \param color color to use
* \param width line width to use
* \note not available in Python bindings
* \since QGIS 3.8
*/
void writePolyline( const QgsCurve &curve, const QString &layer, const QString &lineStyleName, const QColor &color, double width = -1 ) SIP_SKIP;
/**
* Draw dxf filled polygon (HATCH)
* \param polygon polygon
@ -343,6 +359,17 @@ class CORE_EXPORT QgsDxfExport
*/
void writePolygon( const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color ) SIP_SKIP;
/**
* Draw dxf curved filled polygon (HATCH)
* \param polygon polygon (including curves)
* \param layer layer name to use
* \param hatchPattern hatchPattern to use
* \param color color to use
* \note not available in Python bindings
* \since QGIS 3.8
*/
void writePolygon( const QgsCurvePolygon &polygon, const QString &layer, const QString &hatchPattern, const QColor &color ) SIP_SKIP;
/**
* Write line (as a polyline)
* \since QGIS 2.15
@ -511,6 +538,10 @@ class CORE_EXPORT QgsDxfExport
QgsDxfExport::Flags mFlags = nullptr;
void appendCurve( const QgsCurve &c, QVector<QgsPoint> &points, QVector<double> &bulges );
void appendLineString( const QgsLineString &ls, QVector<QgsPoint> &points, QVector<double> &bulges );
void appendCircularString( const QgsCircularString &cs, QVector<QgsPoint> &points, QVector<double> &bulges );
void appendCompoundCurve( const QgsCompoundCurve &cc, QVector<QgsPoint> &points, QVector<double> &bulges );
};
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsDxfExport::Flags )

0
tests/src/python/test_qgsfieldmodel.py Executable file → Normal file
View File

0
tests/src/python/test_qgsvectorlayer.py Executable file → Normal file
View File