Add dedicated conversion for MapInfo marker symbols

This commit is contained in:
Nyall Dawson 2021-05-10 13:17:48 +10:00
parent 8bbac0145a
commit 94dc209e9e
10 changed files with 237 additions and 4 deletions

View File

@ -67,6 +67,15 @@ The caller takes ownership of the returned symbol.
%Docstring
Converts the MapInfo fill symbol with the specified ``identifier`` to a :py:class:`QgsFillSymbol`.
The caller takes ownership of the returned symbol.
%End
static QgsMarkerSymbol *convertMarkerSymbol( int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &color, double size, QgsUnitTypes::RenderUnit sizeUnit ) /Factory/;
%Docstring
Converts the MapInfo marker symbol with the specified ``identifier`` to a :py:class:`QgsMarkerSymbol`.
This method will convert a MapInfo "MapInfo 3.0 Compatible" symbol with a specific ``identifier`` to a :py:class:`QgsMarkerSymbol`.
The caller takes ownership of the returned symbol.
%End

View File

@ -1451,6 +1451,22 @@ std::unique_ptr<QgsSymbol> QgsOgrUtils::symbolFromStyleString( const QString &st
const QString id = symbolStyle.value( QStringLiteral( "id" ) ).toString();
// if the symbol is a mapinfo symbol, use dedicated converter for more accurate results
const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-sym-(\\d+)" ) );
const QRegularExpressionMatch match = sMapInfoId.match( id );
if ( match.hasMatch() )
{
const int symbolId = match.captured( 1 ).toInt();
QgsMapInfoSymbolConversionContext context;
// ogr interpretations of mapinfo symbol sizes are too large -- scale these down
symbolSize *= 0.61;
std::unique_ptr<QgsSymbol> res( QgsMapInfoSymbolConverter::convertMarkerSymbol( symbolId, context, color, symbolSize, symbolSizeUnit ) );
if ( res )
return res;
}
std::unique_ptr< QgsMarkerSymbolLayer > markerLayer;
const thread_local QRegularExpression sFontId = QRegularExpression( QStringLiteral( "font-sym-(\\d+)" ) );
@ -1602,7 +1618,6 @@ std::unique_ptr<QgsSymbol> QgsOgrUtils::symbolFromStyleString( const QString &st
{
return nullptr;
}
break;
case QgsSymbol::Line:
if ( styles.contains( QStringLiteral( "pen" ) ) )

View File

@ -1386,3 +1386,172 @@ QgsFillSymbol *QgsMapInfoSymbolConverter::convertFillSymbol( int identifier, Qgs
}
return new QgsFillSymbol( layers );
}
QgsMarkerSymbol *QgsMapInfoSymbolConverter::convertMarkerSymbol( int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &color, double size, QgsUnitTypes::RenderUnit sizeUnit )
{
QgsSimpleMarkerSymbolLayerBase::Shape shape;
bool isFilled = true;
bool isNull = false;
bool hasShadow = false;
double angle = 0;
QgsMarkerSymbolLayer::VerticalAnchorPoint vertAlign = QgsMarkerSymbolLayer::VCenter;
QPointF shadowOffset;
switch ( identifier )
{
case 31:
// null symbol
isNull = true;
break;
case 32:
shape = QgsSimpleMarkerSymbolLayer::Shape::Square;
break;
case 33:
shape = QgsSimpleMarkerSymbolLayer::Shape::Diamond;
break;
case 34:
shape = QgsSimpleMarkerSymbolLayer::Shape::Circle;
break;
case 35:
shape = QgsSimpleMarkerSymbolLayer::Shape::Star;
break;
case 36:
shape = QgsSimpleMarkerSymbolLayer::Shape::Triangle;
break;
case 37:
shape = QgsSimpleMarkerSymbolLayer::Shape::Triangle;
angle = 180;
break;
case 38:
shape = QgsSimpleMarkerSymbolLayer::Shape::Square;
isFilled = false;
break;
case 39:
shape = QgsSimpleMarkerSymbolLayer::Shape::Diamond;
isFilled = false;
break;
case 40:
shape = QgsSimpleMarkerSymbolLayer::Shape::Circle;
isFilled = false;
break;
case 41:
shape = QgsSimpleMarkerSymbolLayer::Shape::Star;
isFilled = false;
break;
case 42:
shape = QgsSimpleMarkerSymbolLayer::Shape::Triangle;
isFilled = false;
break;
case 43:
shape = QgsSimpleMarkerSymbolLayer::Shape::Triangle;
angle = 180;
isFilled = false;
break;
case 44:
shape = QgsSimpleMarkerSymbolLayer::Shape::Square;
hasShadow = true;
shadowOffset = QPointF( size * 0.1, size * 0.1 );
break;
case 45:
shape = QgsSimpleMarkerSymbolLayer::Shape::Triangle;
shadowOffset = QPointF( size * 0.2, size * 0.1 );
hasShadow = true;
break;
case 46:
shape = QgsSimpleMarkerSymbolLayer::Shape::Circle;
shadowOffset = QPointF( size * 0.1, size * 0.1 );
hasShadow = true;
break;
case 47:
shape = QgsSimpleMarkerSymbolLayer::Shape::Arrow;
size *= 0.66666;
angle = 45;
vertAlign = QgsMarkerSymbolLayer::Top;
break;
case 48:
shape = QgsSimpleMarkerSymbolLayer::Shape::Arrow;
size *= 0.66666;
angle = 225;
vertAlign = QgsMarkerSymbolLayer::Top;
break;
case 49:
shape = QgsSimpleMarkerSymbolLayer::Shape::Cross;
break;
case 50:
shape = QgsSimpleMarkerSymbolLayer::Shape::Cross2;
break;
case 51:
shape = QgsSimpleMarkerSymbolLayer::Shape::Cross;
break;
default:
context.pushWarning( QObject::tr( "The symbol is not supported in QGIS" ) );
return nullptr;
}
std::unique_ptr< QgsSimpleMarkerSymbolLayer > simpleMarker = std::make_unique< QgsSimpleMarkerSymbolLayer >( shape, size );
simpleMarker->setSizeUnit( sizeUnit );
simpleMarker->setAngle( angle );
simpleMarker->setVerticalAnchorPoint( vertAlign );
if ( isNull )
{
simpleMarker->setFillColor( QColor( 0, 0, 0, 0 ) );
simpleMarker->setStrokeStyle( Qt::NoPen );
}
if ( isFilled && QgsSimpleMarkerSymbolLayer::shapeIsFilled( shape ) )
{
simpleMarker->setColor( color );
simpleMarker->setStrokeColor( QColor( 0, 0, 0 ) );
simpleMarker->setStrokeWidth( 0 );
}
else
{
simpleMarker->setFillColor( QColor( 0, 0, 0, 0 ) );
simpleMarker->setStrokeColor( color );
}
QgsSymbolLayerList symbols;
if ( hasShadow )
{
std::unique_ptr< QgsSimpleMarkerSymbolLayer > shadow( simpleMarker->clone() );
shadow->setColor( QColor( 0, 0, 0 ) );
shadow->setLocked( true );
shadow->setOffset( shadowOffset );
shadow->setOffsetUnit( sizeUnit );
symbols << shadow.release();
symbols << simpleMarker.release();
}
else
{
if ( identifier == 51 )
{
std::unique_ptr< QgsSimpleMarkerSymbolLayer > second( simpleMarker->clone() );
second->setShape( QgsSimpleMarkerSymbolLayer::Shape::Cross2 );
symbols << second.release();
}
symbols << simpleMarker.release();
}
return new QgsMarkerSymbol( symbols );
}

View File

@ -24,6 +24,7 @@
class QgsLineSymbol;
class QgsFillSymbol;
class QgsMarkerSymbol;
/**
* Context for a MapInfo symbol conversion operation.
@ -80,6 +81,15 @@ class CORE_EXPORT QgsMapInfoSymbolConverter
*/
static QgsFillSymbol *convertFillSymbol( int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &foreColor, const QColor &backColor = QColor() ) SIP_FACTORY;
/**
* Converts the MapInfo marker symbol with the specified \a identifier to a QgsMarkerSymbol.
*
* This method will convert a MapInfo "MapInfo 3.0 Compatible" symbol with a specific \a identifier to a QgsMarkerSymbol.
*
* The caller takes ownership of the returned symbol.
*/
static QgsMarkerSymbol *convertMarkerSymbol( int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &color, double size, QgsUnitTypes::RenderUnit sizeUnit ) SIP_FACTORY;
};
#endif // QGSMAPINFOSYMBOLCONVERTER_H

View File

@ -37,12 +37,14 @@ from qgis.core import (QgsVectorLayer,
QgsFeature,
QgsGeometry
)
from qgis.testing import unittest
from qgis.testing import unittest, start_app
from utilities import unitTestDataPath
TEST_DATA_DIR = unitTestDataPath()
start_app()
class TestQgsEmbeddedSymbolRenderer(unittest.TestCase):
@ -174,10 +176,10 @@ class TestQgsEmbeddedSymbolRenderer(unittest.TestCase):
renderchecker.setControlName('expected_embedded_mapinfo_lines')
self.assertTrue(renderchecker.runTest('embedded_mapinfo_lines'))
def testMapFillLineSymbolConversion(self):
def testMapInfoFillSymbolConversion(self):
line_layer = QgsVectorLayer(TEST_DATA_DIR + '/mapinfo/fill_styles.TAB', 'Fills', 'ogr')
renderer = QgsEmbeddedSymbolRenderer(defaultSymbol=QgsLineSymbol.createSimple({}))
renderer = QgsEmbeddedSymbolRenderer(defaultSymbol=QgsFillSymbol.createSimple({}))
line_layer.setRenderer(renderer)
mapsettings = QgsMapSettings()
@ -194,6 +196,26 @@ class TestQgsEmbeddedSymbolRenderer(unittest.TestCase):
renderchecker.setControlName('expected_embedded_mapinfo_fills')
self.assertTrue(renderchecker.runTest('embedded_mapinfo_fills'))
def testMapInfoMarkerSymbolConversion(self):
line_layer = QgsVectorLayer(TEST_DATA_DIR + '/mapinfo/marker_styles.TAB', 'Marker', 'ogr')
renderer = QgsEmbeddedSymbolRenderer(defaultSymbol=QgsMarkerSymbol.createSimple({}))
line_layer.setRenderer(renderer)
mapsettings = QgsMapSettings()
mapsettings.setOutputSize(QSize(2000, 4000))
mapsettings.setOutputDpi(96)
mapsettings.setMagnificationFactor(2)
mapsettings.setExtent(line_layer.extent().buffered(0.1))
mapsettings.setLayers([line_layer])
renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(mapsettings)
renderchecker.setControlPathPrefix('embedded')
renderchecker.setControlName('expected_embedded_mapinfo_markers')
self.assertTrue(renderchecker.runTest('embedded_mapinfo_markers'))
if __name__ == '__main__':
unittest.main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
tests/testdata/mapinfo/marker_styles.DAT vendored Normal file

Binary file not shown.

BIN
tests/testdata/mapinfo/marker_styles.ID vendored Normal file

Binary file not shown.

BIN
tests/testdata/mapinfo/marker_styles.MAP vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,8 @@
!table
!version 300
!charset WindowsLatin1
Definition Table
Type NATIVE Charset "WindowsLatin1"
Fields 1
Field1 Char (10) ;