This forum has been archived. All content is frozen. Please use KDE Discuss instead.

Some fixes to the marblewidget library...

Tags: None
(comma "," separated)
User avatar
artembogdanov
Registered Member
Posts
11
Karma
0
Hello!

Some fixes are needed for a long time but they are still absent or not really fixing issues. In code snippets below I will use "<...>" for omit some normal code, "///-" for deleted line, "///+" for added line and "///#" for fix comments, of course without quotes, as follows:
Code: Select all
    <...>
///# This is comment about fix - why some lines must be used instead of the others.
///-    if( it = 1 )
    if( it == 1 ) ///+


Ok, I'm starting now...



1) When drawing a polyline with a label at the end, out of bounds occurs in function ClipPainterPrivate::labelPosition (file ClipPainter.cpp).
Code: Select all
void ClipPainterPrivate::labelPosition( const QPolygonF & polygon, QVector<QPointF>& labelNodes,
                                        LabelPositionFlags labelPositionFlags)
{
    int labelPosition = 0;

    <...>

    if ( polygon.size() > 1 && labelPositionFlags.testFlag( LineEnd ) ) {
        if ( pointAllowsLabel( polygon.at( polygon.size() - 1 ) ) ) {
            labelNodes << polygon.at( polygon.size() - 1 );
        }

///# Error occurs at first iteration
///-        // The Label at the start of the polyline:
///-        for ( int it = polygon.size() - 1; it > 1; --it ) {
///# Instead use this:
        // The Label at the end of the polyline:
        for ( int it = polygon.size() - 2; it > 0; --it ) {
///# End of fix.
            currentAllowsLabel = pointAllowsLabel( polygon.at( it ) );

            if ( currentAllowsLabel ) {
                QPointF node = interpolateLabelPoint( polygon.at( it + 1 ), polygon.at( it ),
                                                    labelPositionFlags );
                if ( node != QPointF( -1.0, -1.0 ) ) {
                    labelNodes << node;
                }
                break;
            }
        }
    }
}



2) When displaying two boxes handling crossing the IDL there may have duplicate objects from left and right box - this duplicates must be erased, otherwise they will be drawn twice. This effect can be seen on the semitransparent objects, such as user-opened files (kml for example) with alpha channel in fill polygon colors. Issue occurs in function GeoGraphicsScene::items (file GeoGraphicsScene.cpp).
Code: Select all
QList< GeoGraphicsItem* > GeoGraphicsScene::items( const Marble::GeoDataLatLonAltBox& box, int zoomLevel ) const
{
    if ( box.west() > box.east() ) {
        // Handle boxes crossing the IDL by splitting it into two separate boxes
        GeoDataLatLonAltBox left;
        left.setWest( -M_PI );
        left.setEast( box.east() );
        left.setNorth( box.north() );
        left.setSouth( box.south() );

        GeoDataLatLonAltBox right;
        right.setWest( box.west() );
        right.setEast( M_PI );
        right.setNorth( box.north() );
        right.setSouth( box.south() );

///-        return items( left, zoomLevel ) + items( right, zoomLevel );
        QList<GeoGraphicsItem *> leftItems = items( left, zoomLevel );      ///+
        QList<GeoGraphicsItem *> rightItems = items( right, zoomLevel );    ///+
        for( int i = 0; i < leftItems.size(); i++ ) {                       ///+
            for( int j = 0; j < rightItems.size(); ) {                      ///+
                if( leftItems.at( i ) == rightItems.at( j ) ) {             ///+
                    rightItems.removeAt( j );                               ///+
                    continue;                                               ///+
                }                                                           ///+
                j++;                                                        ///+
            }                                                               ///+
        }                                                                   ///+
        return leftItems + rightItems;                                      ///+
    }

    <...>

    return result;
}




3) Again, next issue occurs when displaying two boxes handling crossing the IDL - some placemarks may suddnely disappear from screen while the IDL is in view. However, though special case for IDL is provided, there are still disappearing placemarks, even with provided cities.txt / cityplacemarks.kml ! This is why another fix must be applied. Problem occurs in function PlacemarkLayout::visibleTiles (file PlacemarkLayout.cpp).
Code: Select all
QSet<TileId> PlacemarkLayout::visibleTiles( const ViewportParams *viewport ) const
{
    int zoomLevel = qLn( viewport->radius() *4 / 256 ) / qLn( 2.0 );

    /**
     * rely on m_placemarkCache to find the placemarks for the tiles which
     * matter. The top level tiles have the more popular placemarks,
     * the bottom level tiles have the smaller ones, and we only get the ones
     * matching our latLonAltBox.
     */

    QRect rect;
    qreal north, south, east, west;
    viewport->viewLatLonAltBox().boundaries(north, south, east, west);
    QSet<TileId> tileIdSet;                                                 ///+
    QVector<QRectF> frects;                                                 ///+
    if( west <= east )                                                      ///+
        frects << QRectF(west, north, east - west, south - north);          ///+
    else {                                                                  ///+
        frects << QRectF(west, north, M_PI - west, south - north);          ///+
        frects << QRectF(-M_PI, north, east + M_PI, south - north);         ///+
    }                                                                       ///+

    foreach( QRectF frect, frects ) {                                       ///+

        TileId key;
///-        key = TileId::fromCoordinates( GeoDataCoordinates(west, north, 0), zoomLevel);
        key = TileId::fromCoordinates( GeoDataCoordinates(frect.left(),     ///+
            north, 0), zoomLevel);                                          ///+
        rect.setLeft( key.x() );
        rect.setTop( key.y() );

///-        key = TileId::fromCoordinates( GeoDataCoordinates(east, south, 0), zoomLevel);
        key = TileId::fromCoordinates( GeoDataCoordinates(frect.right(),    ///+
            south, 0), zoomLevel);                                          ///+
        rect.setRight( key.x() );
        rect.setBottom( key.y() );

        TileCoordsPyramid pyramid( 0, zoomLevel );
        pyramid.setBottomLevelCoords( rect );

///-        QSet<TileId> tileIdSet;
        bool crossesDateLine = viewport->viewLatLonAltBox().crossesDateLine();
        for ( int level = pyramid.topLevel(); level <= pyramid.bottomLevel(); ++level ) {
            QRect const coords = pyramid.coords( level );
            int x1, y1, x2, y2;
            coords.getCoords( &x1, &y1, &x2, &y2 );
///-            if ( !crossesDateLine ) { // normal case, rect does not cross dateline
            for ( int x = x1; x <= x2; ++x ) {
                for ( int y = y1; y <= y2; ++y ) {
                    TileId const tileId( 0, level, x, y );
                    tileIdSet.insert(tileId);
                }
            }
///-            } else { // as we cross dateline, we first get west part, then east part
///-                // go till max tile
///-                for ( int x = x1; x <= ((2 << (level-1))-1); ++x ) {
///-                    for ( int y = y1; y <= y2; ++y ) {
///-                        TileId const tileId( 0, level, x, y );
///-                        tileIdSet.insert(tileId);
///-                    }
///-                }
///-                // start from min tile
///-                for ( int x = 0; x <= x2; ++x ) {
///-                    for ( int y = y1; y <= y2; ++y ) {
///-                        TileId const tileId( 0, level, x, y );
///-                        tileIdSet.insert(tileId);
///-                    }
///-                }
///-            }
        }
    }

    return tileIdSet;
}




Please, someone, consider adding these fixes. I look forward to.
Thanks!

P.S. Sorry for my bad English...
User avatar
Earthwings
KDE Developer
Posts
172
Karma
1
OS
Hi,

thanks for reporting and thanks for the fixes!

I filed Bug #314539, Bug #314540 and Bug #314542 to make sure they are properly tracked.
User avatar
artembogdanov
Registered Member
Posts
11
Karma
0
Earthwings!
Thank you for your help and patience! Can I post one more fix (it's about correct parsing shp-files including simple/stupid coloring for encountered polygons)?
User avatar
bcooksley
Administrator
Posts
19765
Karma
87
OS
If you will be submitting fixes often, you may wish to try submitting them via Reviewboard (https://git.reviewboard.kde.org), to ensure they are not lost.


KDE Sysadmin
[img]content/bcooksley_sig.png[/img]
User avatar
Earthwings
KDE Developer
Posts
172
Karma
1
OS
artembogdanov wrote:Earthwings!
Thank you for your help and patience! Can I post one more fix (it's about correct parsing shp-files including simple/stupid coloring for encountered polygons)?

Sure. Either here, in Bugzilla or Reviewboard as bcooksley suggested - whatever works best for you.

Thanks!
User avatar
artembogdanov
Registered Member
Posts
11
Karma
0
Thanks for formalization and official fixes for Bug #314539 and Bug #314540 . But what about Bug #314542? Still no official fix, why?..


This version is obsolete. ShpParser updated. See this post, p.1.

But lets back now. I want to public rewritten shp-plugin. Although this plugin is a simple shp-files parser, it's structure rather complicated. This is because of three things:

1. There is a try for coloring polygons based on it's objects distinctions based on dbf data. User can colorize each placemark after document was created but if shp-parser opens and reads dbf-file (using shapelib), doing similar work again seems superfluous. So shp-parser needs some settings (global defaults and per-file) to do simple colorizing based on dbf data.

2. There is no settings provided for "runner" (i.e. parser) plugins. But such settings can be nevertheless successfully used.

3. Some shape files are too large and cannot be simply parsed on 32-bit systems. GADM (full versions 1 and 2) - vivid example. I implemented quite stupid way to parse such files: if two points of a polygon is too close to each other, then one of the points is discarded. First time, we try to open file as usual and minimum distance between two points is null, But, if we ran into the lack of memory while parsing large file, we try again to open the file, but now the minimum distance is 1''. Again, if out of memory, next minimum is 1'. And so on. (On 32-bit Windows systems this trick is not working because of specific memory management, so you can try to use other memory allocators, such as nedmalloc patched dll - but there is no guarantee of results stability... But on Linux and others this must be ok...)

This was (almost) successfully tested with some shapes from Natural Earth (10m - Admin0, Admin1, TimeZones and others) and from GADM (v1 all countries and v2 all-in-one).


Now then, for correct shp-file parsing simply replace contents of the files ShpRunner.cpp and ShpRunner.h.


ShpRunner.cpp:
Code: Select all
//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2011 Thibaut Gridel <tgridel@free.fr>
// Copyright 2013 Bogdanov Artem <temabogdanov@gmail.com>

#include "ShpRunner.h"

#include "GeoDataDocument.h"
#include "GeoDataPlacemark.h"
#include "GeoDataPolygon.h"
#include "GeoDataStyle.h"

#include "MarbleMath.h"
#include "MarbleModel.h"
#include "MarbleDebug.h"

#include <QtCore/QFileInfo>
#include <QtCore/QVector>
#include <QtCore/QSettings>
#include <QtCore/QRegExp>

#include <shapefil.h>

namespace Marble
{

#define SHPPLUGIN_DEFAULT_NAMEFIELDS                ".*(name|Name|NAME|title|Title|TITLE).*"
#define SHPPLUGIN_DEFAULT_NOTEFIELDS                ".*(note|Note|NOTE|remark|Remark|REMARK|descr|Descr|DESCR).*"
#define SHPPLUGIN_DEFAULT_ORIGFIELDS                "(name|Name|NAME|title|Title|TITLE).*"
#define SHPPLUGIN_DEFAULT_POLYSTARTCOLOR            QColor( QRgb( 0xF0F000 ) )
#define SHPPLUGIN_DEFAULT_POLYENDCOLOR              QColor( QRgb( 0x202000 ) )
#define SHPPLUGIN_DEFAULT_POLYMASKCOLOR             QColor( QRgb( 0xF8F81F ) )
#define SHPPLUGIN_DEFAULT_POLYALPHACHANNEL          0x70
#define SHPPLUGIN_DEFAULT_LINESCOLOR                QColor( Qt::darkRed )
#define SHPPLUGIN_DEFAULT_LINESWIDTH                1.5


ShpRunner::ShpRunner(QObject *parent) :
    ParsingRunner(parent)
{
    m_nameFields = SHPPLUGIN_DEFAULT_NAMEFIELDS;
    m_noteFields = SHPPLUGIN_DEFAULT_NOTEFIELDS;
    m_origFields = SHPPLUGIN_DEFAULT_ORIGFIELDS;

    m_polyStartColor = SHPPLUGIN_DEFAULT_POLYSTARTCOLOR.rgb();
    m_polyEndColor = SHPPLUGIN_DEFAULT_POLYENDCOLOR.rgb();
    m_polyMaskColor = SHPPLUGIN_DEFAULT_POLYMASKCOLOR.rgb();

    m_polyAlphaChannel = SHPPLUGIN_DEFAULT_POLYALPHACHANNEL;
    m_linesColor = SHPPLUGIN_DEFAULT_LINESCOLOR;
    m_linesWidth = SHPPLUGIN_DEFAULT_LINESWIDTH;
    m_rangeLimits << 0 << 1 << 60;
    m_setStyle = false;
}

ShpRunner::~ShpRunner()
{
}

void ShpRunner::parseFile( const QString &fileName, DocumentRole role = UnknownDocument )
{
    int i, j, k, ke;
    static int s_l = 0;

    SHPHandle handle = SHPOpen( fileName.toStdString().c_str(), "rb" );
    if ( handle == 0 ) {
        emit parsingFinished( 0 );
        return;
    }

    QSettings settings;
    settings.beginGroup("plugin_shp");
    loadSettings( settings, true );
    settings.beginGroup( fileName );
    loadSettings( settings, false, true );

    int entities;
    int shapeType;
    SHPGetInfo( handle, &entities, &shapeType, NULL, NULL );
    mDebug() << " SHP info " << entities << " Entities " << shapeType << " Shape Type ";

    QVector<int> nameField, noteField, origField;
    DBFHandle dbfhandle;
    dbfhandle = DBFOpen( fileName.toStdString().c_str(), "rb");

    //
    // Search for key fields in DBF by regular expressions
    //

    if( dbfhandle != 0 ) {
        int nfields = DBFGetFieldCount( dbfhandle );
        QStringList fieldsInfo;
        for( i = 0; i < nfields; i++ ) {
            char fieldName[ 250 ];
            int nWidth, nDecimals;
            DBFFieldType type = DBFGetFieldInfo( dbfhandle, i, fieldName, &nWidth, &nDecimals );
            fieldsInfo.append( QString( fieldName ) );
        }

        typedef QPair<QString *, QVector<int> *> servT;
        QVector<servT> fieldData;
        fieldData << servT( &m_nameFields, &nameField )
                  << servT( &m_noteFields, &noteField )
                  << servT( &m_origFields, &origField );
        foreach( servT fd, fieldData )
            if( !fd.first->isEmpty() )
                for( i = 0; i < nfields; i++ )
                    if( QRegExp( *fd.first, Qt::CaseSensitive, QRegExp::RegExp2 ).exactMatch( fieldsInfo.at( i ) ) )
                        fd.second->append( i );
    }

    //
    // Try to build document using current limit
    //

    SHPObject *shape;
    GeoDataDocument *document = new GeoDataDocument();
    document->setDocumentRole( role );
    QStringList origPlacemarks;

    double d, distLim = 0;
    if( s_l < m_rangeLimits.size() )
        distLim = DEG2RAD * m_rangeLimits.at( s_l ) / HOUR2SEC;

    try {
        for ( i = 0; i < entities; i++ ) {

            shape = SHPReadObject( handle, i );
            if( shape == 0 )
                break;

            GeoDataPlacemark *placemark = new GeoDataPlacemark();
            document->append( placemark );

            QString info, note, orig;
            if( !nameField.isEmpty() ) {
                for( j = 0; j < nameField.size() && info.isEmpty(); j++ )
                    info = QString::fromAscii( DBFReadStringAttribute(
                        dbfhandle, i, nameField.at( j ) ) );
                placemark->setName( info );
                mDebug() << "name " << placemark->name();
            }
            if( !noteField.isEmpty() ) {
                for( j = 0; j < noteField.size() && note.isEmpty(); j++ )
                    note = QString::fromAscii( DBFReadStringAttribute(
                        dbfhandle, i, noteField.at( j ) ) );
                placemark->setDescription( note );
                mDebug() << "desc " << placemark->description();
            }
            for( j = 0; j < origField.size(); j++ )
                orig.append( QString::fromAscii( DBFReadStringAttribute(
                    dbfhandle, i, origField.at( j ) ) ) );
            origPlacemarks.append( orig );

            bool z = false;
            switch ( shape->nSHPType ) {
                case SHPT_POINTZ :
                    z = true;
                case SHPT_POINT: {
                    placemark->setCoordinate( shape->padfX[0], shape->padfY[0],
                        z ? shape->padfZ[0] : 0, GeoDataCoordinates::Degree );
                    mDebug() << "point " << placemark->name();
                    break;
                }

                case SHPT_MULTIPOINTZ:
                    z = true;
                case SHPT_MULTIPOINT: {
                    GeoDataMultiGeometry *geom = new GeoDataMultiGeometry();
                    for( j = 0; j < shape->nVertices; j++ ) {
                        GeoDataPoint *point = new GeoDataPoint(
                            GeoDataCoordinates( shape->padfX[j], shape->padfY[j],
                                z ? shape->padfZ[j] : 0, GeoDataCoordinates::Degree ) );
                        geom->append( point );
                    }

                    placemark->setGeometry( geom );
                    mDebug() << "multipoint " << placemark->name();
                    break;
                }

                case SHPT_ARCZ:
                    z = true;
                case SHPT_ARC: {
                    GeoDataMultiGeometry *geom = new GeoDataMultiGeometry();
                    for( j = 0; j < shape->nParts; j++ ) {
                        GeoDataLineString *line = new GeoDataLineString();
                        GeoDataCoordinates coords1;
                        ke = (j + 1) >= shape->nParts ? shape->nVertices : shape->panPartStart[j + 1];
                        for( k = shape->panPartStart[j]; k < ke; k++ ) {
                            GeoDataCoordinates coords2( shape->padfX[k], shape->padfY[k],
                                z ? shape->padfZ[k] : 0, GeoDataCoordinates::Degree );
                            if( distLim > 0 ) {
                                d = distanceSphere( coords1, coords2 );
                                if( ( d < distLim ) && ( k > 0 ) && ( k < ke - 1 ) && ( ke > 4 ) )
                                    continue;
                            }
                            line->append( coords2 );
                            coords1 = coords2;
                        }
                        geom->append( line );
                    }

                    placemark->setGeometry( geom );
                    mDebug() << "arc " << placemark->name() << " " << shape->nParts;
                    break;
                }

                case SHPT_POLYGONZ:
                    z = true;
                case SHPT_POLYGON: {
                    GeoDataMultiGeometry *geom = new GeoDataMultiGeometry();
                    GeoDataPolygon *poly = new GeoDataPolygon();
                    for( j = 0; j < shape->nParts; j++ ) {
                        GeoDataLineString *line = new GeoDataLineString();
                        GeoDataCoordinates coords1;
                        ke = (j + 1) >= shape->nParts ? shape->nVertices : shape->panPartStart[j + 1];
                        for( k = shape->panPartStart[j]; k < ke; k++ ) {
                            GeoDataCoordinates coords2( shape->padfX[k], shape->padfY[k],
                                z ? shape->padfZ[k] : 0, GeoDataCoordinates::Degree );
                            if( distLim > 0 ) {
                                d = distanceSphere( coords1, coords2 );
                                if( ( d < distLim ) && ( k > 0 ) && ( k < ke - 1 ) && ( ke > 4 ) )
                                    continue;
                            }
                            line->append( coords2 );
                            coords1 = coords2;
                        }
                        if( coords1 != line->at( 0 ) )
                            line->append( coords1 );
                        if( isLineClockwise( line ) || shape->nParts == 1 ) {
                            if( j > 0 ) {
                                poly = new GeoDataPolygon();
                            }
                            poly->setOuterBoundary( *line );
                            geom->append( poly );
                        }
                        else
                            poly->appendInnerBoundary( *line );
                    }

                    placemark->setGeometry( geom );
                    mDebug() << "poly " << placemark->name() << " " << shape->nParts;
                    break;
                }

                default : {
                    mDebug() << "unsupported shape type " << placemark->name();
                    break;
                }
            }
            SHPDestroyObject( shape );
        }
    }
    catch(...) {
        SHPDestroyObject( shape );
    }

    settings.endGroup();

    SHPClose( handle );
    DBFClose( dbfhandle );

    if ( i >= entities && document->size() != 0 ) {
        s_l = 0;
        document->setName( QFileInfo( fileName ).baseName() );
        if( m_setStyle ) {
            i = 0;
            foreach( GeoDataFeature *feature, document->featureList() ) {
                GeoDataPlacemark *mark = dynamic_cast<GeoDataPlacemark*>( feature );
                if( mark != 0 ) {
                    k = origPlacemarks.indexOf( origPlacemarks.at( i ) );
                    setPlacemarkStyle( mark, QRgb( uint(
                        ( m_polyStartColor + ( m_polyEndColor - m_polyStartColor ) * k / entities ) & m_polyMaskColor ) ) );
                }
                i++;
            }
        }
        emit parsingFinished( document );
        settings.sync();
    }
    else {
        delete document;

        s_l++;
        if( s_l >= m_rangeLimits.size() ) {
            s_l = 0;
            emit parsingFinished( 0 );
        }
        else
            parseFile( fileName, role );
    }
}

bool ShpRunner::isLineClockwise( GeoDataLineString *line )
{
    int s = line->size() - 1;
    double clkw = 0;
    for( int k = 0; k < s; k++ ) {
        clkw += ( line->at( k+1 ).longitude() - line->at( k ).longitude() )
            * ( line->at( k+1 ).latitude() + line->at( k ).latitude() + M_PI );
    }
    return clkw >= 0;
}

void ShpRunner::setPlacemarkStyle( GeoDataPlacemark *placemark, QColor color )
{
    GeoDataStyle *style = new GeoDataStyle();

    if( color.isValid() ) {
        if( color.alpha() == 0 || color.alpha() == 0xFF )
            color.setAlpha( m_polyAlphaChannel );
        style->polyStyle().setColor( color );
        style->polyStyle().setFill( true );
    }
    if( m_linesColor.isValid() ) {
        style->lineStyle().setColor( m_linesColor );
        style->polyStyle().setOutline( true );
    }
    if( m_linesWidth > 0 )
        style->lineStyle().setWidth( m_linesWidth );

    placemark->setStyle( style );
}

QColor ShpRunner::colorFromString( QString str )
{
    QColor res;

    if( QColor::isValidColor( str ) )
        res.setNamedColor( str );
    else if( str.startsWith( "#" ) ) {
        str = str.mid( 1 );
        bool ok;
        uint rgba = str.toUInt( &ok, 16 );
        if( ok ) {
            if( str.size() <= 6 )
                rgba |= 0xFF000000UL;
            res.setRgba( rgba );
        }
    }

    return res;
}

void ShpRunner::loadSettings( QSettings& settings, bool loadDefaults, bool removeGroup )
{
    QString key = "setStyle";
    QVariant res = settings.value( key );
    if( loadDefaults || settings.contains( key ) )
        m_setStyle = res.toInt() != 0;

    if( !m_setStyle )
        return;

    key = "rxNameFields";
    res = settings.value( key, removeGroup ? QVariant() : SHPPLUGIN_DEFAULT_NAMEFIELDS );
    if( loadDefaults || settings.contains( key ) )
        m_nameFields = res.toString();

    key = "rxNoteFields";
    res = settings.value( key, removeGroup ? QVariant() : SHPPLUGIN_DEFAULT_NOTEFIELDS );
    if( loadDefaults || settings.contains( key ) )
        m_noteFields = res.toString();

    key = "rxOrigFields";
    res = settings.value( key, removeGroup ? QVariant() : SHPPLUGIN_DEFAULT_ORIGFIELDS );
    if( loadDefaults || settings.contains( key ) )
        m_origFields = res.toString();

    key = "linesColor";
    res = settings.value( key, removeGroup ? QVariant() : SHPPLUGIN_DEFAULT_LINESCOLOR.name() );
    if( loadDefaults || settings.contains( key ) )
        m_linesColor = colorFromString( res.toString() );

    key = "linesWidth";
    res = settings.value( key, removeGroup ? QVariant() : SHPPLUGIN_DEFAULT_LINESWIDTH );
    if( loadDefaults || settings.contains( key ) )
        m_linesWidth = res.toString().toDouble();

    key = "polyAlphaChannel";
    res = settings.value( key, removeGroup ? QVariant() : SHPPLUGIN_DEFAULT_POLYALPHACHANNEL );
    if( loadDefaults || settings.contains( key ) )
        m_polyAlphaChannel = res.toInt() & 0xFF;

    key = "polyStartColor";
    res = settings.value( key, removeGroup ? QVariant() : SHPPLUGIN_DEFAULT_POLYSTARTCOLOR.name() );
    if( loadDefaults || settings.contains( key ) )
        m_polyStartColor = colorFromString( res.toString() ).rgb();

    key = "polyEndColor";
    res = settings.value( key, removeGroup ? QVariant() : SHPPLUGIN_DEFAULT_POLYENDCOLOR.name() );
    if( loadDefaults || settings.contains( key ) )
        m_polyEndColor = colorFromString( res.toString() ).rgb();

    key = "polyMaskColor";
    res = settings.value( key, removeGroup ? QVariant() : SHPPLUGIN_DEFAULT_POLYMASKCOLOR.name() );
    if( loadDefaults || settings.contains( key ) )
        m_polyMaskColor = colorFromString( res.toString() ).rgb();

    if( removeGroup ) {
        foreach( QString key, settings.childKeys() ) {
            settings.remove( key );
        }
        QString group = settings.group();
        settings.endGroup();
        settings.remove( group );
    }

}

}

#include "ShpRunner.moc"

ShpRunner.h:
Code: Select all
//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2011 Thibaut Gridel <tgridel@free.fr>
// Copyright 2013 Bogdanov Artem <temabogdanov@gmail.com>

#ifndef MARBLESHPRUNNER_H
#define MARBLESHPRUNNER_H

#include "ParsingRunner.h"


class QSettings;
class QColor;

namespace Marble
{

class GeoDataDocument;
class GeoDataPlacemark;
class GeoDataLineString;

class ShpRunner : public ParsingRunner
{
    Q_OBJECT
public:
    explicit ShpRunner(QObject *parent = 0);
    ~ShpRunner();
    virtual void parseFile( const QString &fileName, DocumentRole role );

    void loadSettings( QSettings& settings, bool loadDefaults, bool removeGroup = false );
    bool isLineClockwise( GeoDataLineString *line );
    void setPlacemarkStyle( GeoDataPlacemark *placemark, QColor color = QColor() );
    bool simplifyShapes( GeoDataDocument *document, int startPos = 0 );
    QColor colorFromString( QString str );

protected:
    QString m_nameFields;
    QString m_noteFields;
    QString m_origFields;

    int m_polyStartColor;
    int m_polyEndColor;
    int m_polyMaskColor;

    bool m_setStyle;
    int m_polyAlphaChannel;
    double m_linesWidth;
    QColor m_linesColor;
    QVector<int> m_rangeLimits;
};

}
#endif // MARBLESHPRUNNER_H


If you will find (or already found) a better way/algorithm for simple (and universal) colorizing, please tell about and explain it. Thanks!

Last edited by artembogdanov on Fri May 17, 2013 6:56 pm, edited 3 times in total.
User avatar
artembogdanov
Registered Member
Posts
11
Karma
0
This version is obsolete. Try updated patch from this post, p.2

And after, I want to introduce some basic z-ordering when drawing in function GeometryLayer::render (file GeometryLayer.cpp).

Code: Select all
bool GeometryLayer::render( GeoPainter *painter, ViewportParams *viewport,
                            const QString& renderPos, GeoSceneLayer * layer )
{
    Q_UNUSED( renderPos )
    Q_UNUSED( layer )

    painter->save();
    painter->autoMapQuality();

    int maxZoomLevel = qMin<int>( qLn( viewport->radius() *4 / 256 ) / qLn( 2.0 ), GeometryLayerPrivate::maximumZoomLevel() );

    QList<GeoGraphicsItem*> items = d->m_scene.items( viewport->viewLatLonAltBox(), maxZoomLevel );
    int painted = 0;
///-    foreach( GeoGraphicsItem* item, items )
///-    {
///-        if ( item->latLonAltBox().intersects( viewport->viewLatLonAltBox() ) ) {
///-            item->paint( painter, viewport );
///-            ++painted;
///-        }
///-    }

    for( int i = items.size()-1; i >= 0; i-- ) {                            ///+
        if( !items.at( i )->latLonAltBox().intersects(                      ///+
            viewport->viewLatLonAltBox() ) )                                ///+
        {                                                                   ///+
            items.removeAt( i );                                            ///+
        }                                                                   ///+
    }                                                                       ///+

    int k = items.size();                                                   ///+
    for( int i = 0; i < k-1; i++ ) {                                        ///+
        for( int j = i+1; j > 0; j-- ) {                                    ///+
            if( items.at( j-1 )->latLonAltBox().center().altitude()         ///+
                > items.at( j )->latLonAltBox().center().altitude() )       ///+
            {                                                               ///+
                items.swap( j, j-1 );                                       ///+
            }                                                               ///+
            else                                                            ///+
                break;                                                      ///+
        }                                                                   ///+
    }                                                                       ///+

    for( ; painted < k; painted++ )                                         ///+
        items.at( painted )->paint( painter, viewport );                    ///+

    foreach( ScreenOverlayGraphicsItem* item, d->m_items ) {
        item->paintEvent( painter, viewport );
    }

    painter->restore();
    d->m_runtimeTrace = QString( "Items: %1 Drawn: %2 Zoom: %3")
                .arg( items.size() )
                .arg( painted )
                .arg( maxZoomLevel );
    return true;
}


Try to open these kml-files before and after the changes and you'll see the difference (in 3D-Globe mode of Earth). Zip-archive with example files can be downloaded from here: rghost.ru/45009252 (30 days)

Last edited by artembogdanov on Fri May 17, 2013 6:58 pm, edited 1 time in total.
User avatar
artembogdanov
Registered Member
Posts
11
Karma
0
bcooksley
And to wich repository (marble or marble-kde-org) should I submit review requests?
User avatar
Earthwings
KDE Developer
Posts
172
Karma
1
OS
For marble, please. The marble-kde-org repository is the marble.kde.org website.

Regarding the z-order patch: I see two problems with the approach above, both performance related. First it does the ordering with a bubble sort approach, which has complexity O(n^2). Using qSort would be much faster. Second the ordering is done on each paint event. Instead it should be only done when items change, e.g. when they are added to the scene.
User avatar
artembogdanov
Registered Member
Posts
11
Karma
0
Thank you! You are certainly right about performance issues. Also there is another issue - bad sorting of a three-dimensional shapes. This approach is only a draft, For this moment, I don't know how to solve this gracefully and correctly. But this is a temporary patch for those who need some z-ordering now and who do not care about this issues.
And what about Bug #314542? Is something wrong with it or with provided approach?
Also, what about correct shapefiles parsing approach? Maybe I should do something? Please, tell me what to do, I'll try to.
Thank you!
User avatar
artembogdanov
Registered Member
Posts
11
Karma
0
Ok, while waiting for shp-parser to be committed, let me suggest a couple of another small fixes.


1) If you enable FileViewWidget, you may want to enable/disable or open/close user-opened documents (shapefiles, kml or others). Usually, everything's fine when you open or enable document (or separate objects in it). But when you try to disable document, you may see that not all document's objects gone from map. Furthermore, if you try to close that document, application will crash when rendering GeometryLayer.
All this is because, when document deleted from TreeModel or erased at all from memory, not all of its objects deleted from GeoGraphicsScene. Root of the problem is in function GeoGraphicsScene::removeItem (file GeoGraphicsScene.cpp) - not all pointers to document's object are deleted from graphics scene.
Code: Select all
void GeoGraphicsScene::removeItem( const GeoDataFeature* feature )
{
///-    const TileId key = d->m_features.value( feature );
///-    QList< GeoGraphicsItem* >& tileList = d->m_items[key];
///-    foreach( GeoGraphicsItem* item, tileList ) {
///-        if( item->feature() == feature ) {
///-            d->m_features.remove( feature );
///-            tileList.removeAll( item );
///-            return;
///-        }
///-    }
    foreach( TileId key, d->m_items.keys() ) {                              ///+
        QList< GeoGraphicsItem* >& tileList = d->m_items[key];              ///+
        foreach( GeoGraphicsItem* item, tileList ) {                        ///+
            if( item->feature() == feature )                                ///+
                tileList.removeAll( item );                                 ///+
        }                                                                   ///+
    }                                                                       ///+
    d->m_features.remove( feature );                                        ///+
}



2) Next fix is... obscure? Problem occurs in MarbleLegendBrowser::generateSectionsHtml (file MarbleLegendBrowser.cpp). Generating html from dgml, we add image properties tags. Among these properties is the path to the image file. Path generated by adding two strings - "file://" and canonical path. On Linux all seems to be ok, because Linux canonical paths begins with '/' symbol. The result is an url like "file:///opt/marble/data/bitmaps/flag.png", according to this article.
But on Windows the result is invalid, like "file://c:/marble/data/bitmaps/flag.png". This result cannot be shown correctly in LegendWidget. We must add one more '/' to get normal "file:///c:/marble/data/bitmaps/flag.png" url.
When we fix this, we fall into another problem. When image file is not used and canonical path is null, we finally get a stupid url like "file:///". On Windows, in release build such urls provokes an internal crash in Qt, resulting in application crash with error message from Microsoft Visual C++ Runtime Library: "Runtime error! <...> This application has requested the Runtime to terminate it in an unusual way. <...>".
See Atlas theme (data/maps/earth/srtm/srtm.dgml - elevation explanation section) for such imageless elements.
Consider to check is the following fix correct:
Code: Select all
///-            QString src  =  "file://" + path;
            QString src;                                                    ///+
            if( !path.isEmpty() ) {                                         ///+
                src = "file://";                                            ///+
                if( path.at( 0 ) != '/' )                                   ///+
                    path.push_front( '/' );                                 ///+
                src += path;                                                ///+
            }                                                               ///+


Thanks for attention and patience!
User avatar
Earthwings
KDE Developer
Posts
172
Karma
1
OS
I filed Bug #318736 for the scene not being cleared properly.

For the legend problem I delegated the url generation to QUrl which is supposed to handle stuff like that, see Bug #318735. Can you test if that fixes it? I don't have a Windows system at hand at the moment.
User avatar
artembogdanov
Registered Member
Posts
11
Karma
0
Earthwings
Thank you! Your fix works perfectly, and for empty path it generates string "file:" which does not crash Qt in release (and in debug of course) mode! Awesome! :)
User avatar
artembogdanov
Registered Member
Posts
11
Karma
0
Another minor issue. If we switch off glowing on labels, in some circumstances labels does not fit to the calculated rectangles - last letters partially or completely not visible, especially when using italic font. Because of glowing always turned on by default (but comments in code say otherwise!), the problem goes unnoticed (?).
If we want to fix it, we must change something in function VisiblePlacemark::drawLabelPixmap (file VisiblePlacemark.cpp):
Code: Select all
    if ( style->labelStyle().glow() ) {
        labelFont.setWeight( 75 ); // Needed to calculate the correct pixmap size;
        textWidth = ( QFontMetrics( labelFont ).width( labelName )
            + qRound( 2 * s_labelOutlineWidth ) );
    } else {
///-        textWidth = ( QFontMetrics( labelFont ).width( labelName ) );
        textWidth = ( QFontMetrics( labelFont ).boundingRect(               ///+
            labelName ).width() ) + 1;                                      ///+
    }

But if we look at PlacemarkLayer::render (file PlacemarkLayer.cpp), we can see that the same rectangle is calculated twice: when drawing label internally (VisiblePlacemark::drawLabelPixmap) and when drawing on screen (PlacemarkLayout::roomForLabel). Both rectangles must be the same, or we can fall into some performance issue because of QPainter::drawPixmap documented features (The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree).
Then we must do one more change in function PlacemarkLayout::roomForLabel (file PlacemarkLayout.cpp):
Code: Select all
    if ( style->labelStyle().glow() ) {
        labelFont.setWeight( 75 ); // Needed to calculate the correct pixmap size;
        textWidth = ( QFontMetrics( labelFont ).width( labelText )
            + qRound( 2 * s_labelOutlineWidth ) );
    } else {
///-        textWidth = ( QFontMetrics( labelFont ).width( labelText ) );
        textWidth = ( QFontMetrics( labelFont ).boundingRect(               ///+
            labelText ).width() ) + 1;                                      ///+
    }

But both peaces of code are identical (except names labelName/labelText)! May be create new function in, say, VisiblePlacemark class? It would be much more logical and easier...
Please consider to fix it correctly or use the draft patch above.

P.S. I don't know why without "+ 1" last letter of non-glowing labels still can be shown only partially. But with "+ 1" it's all ok. I can upload screenshot(s) if in Linux this issue is not reproducible. To simply reproduce this issue in Windows go to the GeoDataLabelStyle.cpp file and replace all "m_glow( true )" strings with "m_glow( false )" ones. Then recompile and run.
User avatar
artembogdanov
Registered Member
Posts
11
Karma
0
1. I want to share an updated version of shapefiles parser plugin. Fixed dramatic performance issue (marblewidget works with GeoDataMultiGeometry very slow, therefore GeoDataMultiGeometry now created only when it actually needed).
Colorizing was removed (a plugin which wants to colorize do this operation itself). Maybe, in future it will be re-added, based on the *.style files as in the ESRI ArcGIS. Also, code which works with settings, was totally removed.

ShpRunner.cpp
Code: Select all
//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2011 Thibaut Gridel <tgridel@free.fr>
// Copyright 2013 Bogdanov Artem <temabogdanov@gmail.com>

#include "ShpRunner.h"

#include "GeoDataDocument.h"
#include "GeoDataPlacemark.h"
#include "GeoDataPolygon.h"
#include "GeoDataStyle.h"

#include "MarbleMath.h"
#include "MarbleModel.h"
#include "MarbleDebug.h"

#include <QtCore/QFileInfo>
#include <QtCore/QVector>
#include <QtCore/QSettings>
#include <QtCore/QRegExp>

#include <shapefil.h>

namespace Marble
{

#define SHPPLUGIN_DEFAULT_NAMEFIELDS                ".*(name|Name|NAME|title|Title|TITLE).*"
#define SHPPLUGIN_DEFAULT_NOTEFIELDS                ".*(note|Note|NOTE|remark|Remark|REMARK|descr|Descr|DESCR).*"


ShpRunner::ShpRunner(QObject *parent) :
    ParsingRunner(parent)
{
    m_nameFields = SHPPLUGIN_DEFAULT_NAMEFIELDS;
    m_noteFields = SHPPLUGIN_DEFAULT_NOTEFIELDS;
    m_rangeLimits << 0 << 1 << 60;
}

ShpRunner::~ShpRunner()
{
}

void ShpRunner::parseFile( const QString &fileName, DocumentRole role = UnknownDocument )
{
    int i, j, k, ke;
    static int s_l = 0;

    SHPHandle handle = SHPOpen( fileName.toStdString().c_str(), "rb" );
    if ( handle == 0 ) {
        emit parsingFinished( 0 );
        return;
    }

    int entities;
    int shapeType;
    SHPGetInfo( handle, &entities, &shapeType, NULL, NULL );
    mDebug() << " SHP info " << entities << " Entities " << shapeType << " Shape Type ";

    QVector<int> nameField, noteField;
    DBFHandle dbfhandle;
    dbfhandle = DBFOpen( fileName.toStdString().c_str(), "rb");

    //
    // Search for key fields in DBF by regular expressions
    //

    if( dbfhandle != 0 ) {
        int nfields = DBFGetFieldCount( dbfhandle );
        QStringList fieldsInfo;
        for( i = 0; i < nfields; i++ ) {
            char fieldName[ 250 ];
            int nWidth, nDecimals;
            DBFFieldType type = DBFGetFieldInfo( dbfhandle, i, fieldName, &nWidth, &nDecimals );
            fieldsInfo.append( QString( fieldName ) );
        }

        typedef QPair<QString *, QVector<int> *> servT;
        QVector<servT> fieldData;
        fieldData << servT( &m_nameFields, &nameField )
                  << servT( &m_noteFields, &noteField );
        foreach( servT fd, fieldData )
            if( !fd.first->isEmpty() )
                for( i = 0; i < nfields; i++ )
                    if( QRegExp( *fd.first, Qt::CaseSensitive, QRegExp::RegExp2 ).exactMatch( fieldsInfo.at( i ) ) )
                        fd.second->append( i );
    }

    //
    // Try to build document using current limit
    //

    SHPObject *shape;
    GeoDataDocument *document = new GeoDataDocument();

    double d, distLim = 0;
    if( s_l < m_rangeLimits.size() )
        distLim = DEG2RAD * m_rangeLimits.at( s_l ) / HOUR2SEC;

    try {
        for ( i = 0; i < entities; i++ ) {

            shape = SHPReadObject( handle, i );
            if( shape == 0 )
                break;

            GeoDataPlacemark *placemark = new GeoDataPlacemark();
            document->append( placemark );

            QString info, note, orig;
            if( !nameField.isEmpty() ) {
                for( j = 0; j < nameField.size() && info.isEmpty(); j++ )
                    info = QString::fromAscii( DBFReadStringAttribute(
                        dbfhandle, i, nameField.at( j ) ) );
                placemark->setName( info );
                mDebug() << "name " << placemark->name();
            }
            if( !noteField.isEmpty() ) {
                for( j = 0; j < noteField.size() && note.isEmpty(); j++ )
                    note = QString::fromAscii( DBFReadStringAttribute(
                        dbfhandle, i, noteField.at( j ) ) );
                placemark->setDescription( note );
                mDebug() << "desc " << placemark->description();
            }

            bool z = false;
            switch ( shape->nSHPType ) {
                case SHPT_POINTZ :
                    z = true;
                case SHPT_POINT: {
                    placemark->setCoordinate( shape->padfX[0], shape->padfY[0],
                        ( z ? shape->padfZ[0] : 0 ),
                        GeoDataCoordinates::Degree );
                    mDebug() << "point " << placemark->name();
                    break;
                }

                case SHPT_MULTIPOINTZ:
                    z = true;
                case SHPT_MULTIPOINT: {
                    GeoDataMultiGeometry *geom = new GeoDataMultiGeometry();
                    for( j = 0; j < shape->nVertices; j++ ) {
                        GeoDataPoint *point = new GeoDataPoint(
                            GeoDataCoordinates( shape->padfX[j], shape->padfY[j],
                                ( z ? shape->padfZ[j] : 0 ),
                                GeoDataCoordinates::Degree ) );
                        geom->append( point );
                    }

                    placemark->setGeometry( geom );
                    mDebug() << "multipoint " << placemark->name();
                    break;
                }

                case SHPT_ARCZ:
                    z = true;
                case SHPT_ARC: {
                    GeoDataMultiGeometry *geom = ( shape->nParts <= 1 ) ? 0 : new GeoDataMultiGeometry();
                    GeoDataLineString *line;
                    for( j = 0; j < shape->nParts; j++ ) {
                        line = new GeoDataLineString();
                        GeoDataCoordinates coords1;
                        ke = (j + 1) >= shape->nParts ? shape->nVertices : shape->panPartStart[j + 1];
                        for( k = shape->panPartStart[j]; k < ke; k++ ) {
                            GeoDataCoordinates coords2( shape->padfX[k], shape->padfY[k],
                                ( z ? shape->padfZ[k] : 0 ),
                                GeoDataCoordinates::Degree );
                            if( distLim > 0 ) {
                                d = distanceSphere( coords1, coords2 );
                                if( ( d < distLim ) && ( k > 0 ) && ( k < ke - 1 ) && ( ke > 4 ) )
                                    continue;
                            }
                            line->append( coords2 );
                            coords1 = coords2;
                        }
                        if( geom != 0 )
                            geom->append( line );
                    }

                    placemark->setGeometry( geom != 0 ? geom : (GeoDataGeometry *)line );
                    mDebug() << "arc " << placemark->name() << " " << shape->nParts;
                    break;
                }

                case SHPT_POLYGONZ:
                    z = true;
                case SHPT_POLYGON: {
                    GeoDataMultiGeometry *geom = 0;
                    GeoDataPolygon *poly = new GeoDataPolygon();
                    for( j = 0; j < shape->nParts; j++ ) {
                        GeoDataLineString *line = new GeoDataLineString();
                        GeoDataCoordinates coords1;
                        ke = (j + 1) >= shape->nParts ? shape->nVertices : shape->panPartStart[j + 1];
                        for( k = shape->panPartStart[j]; k < ke; k++ ) {
                            GeoDataCoordinates coords2( shape->padfX[k], shape->padfY[k],
                                ( z ? shape->padfZ[k] : 0 ),
                                GeoDataCoordinates::Degree );
                            if( distLim > 0 ) {
                                d = distanceSphere( coords1, coords2 );
                                if( ( d < distLim ) && ( k > 0 ) && ( k < ke - 1 ) && ( ke > 4 ) )
                                    continue;
                            }
                            line->append( coords2 );
                            coords1 = coords2;
                        }
                        if( coords1 != line->at( 0 ) )
                            line->append( coords1 );

                        int s = line->size() - 1;
                        double clkw = 0;
                        for( int k = 0; k < s; k++ )
                            clkw += ( line->at( k+1 ).longitude() - line->at( k ).longitude() )
                                * ( line->at( k+1 ).latitude() + line->at( k ).latitude() + M_PI );

                        if( clkw >= 0 || shape->nParts == 1 ) {
                            if( j > 0 ) {
                                if( geom == 0 ) {
                                    geom = new GeoDataMultiGeometry();
                                    geom->append( poly );
                                }
                                poly = new GeoDataPolygon();
                            }
                            poly->setOuterBoundary( *line );
                            if( geom != 0 )
                                geom->append( poly );
                        }
                        else
                            poly->appendInnerBoundary( *line );
                    }

                    placemark->setGeometry( geom != 0 ? geom : (GeoDataGeometry *)poly );
                    mDebug() << "poly " << placemark->name() << " " << shape->nParts;
                    break;
                }

                default : {
                    mDebug() << "unsupported shape type " << placemark->name();
                    break;
                }
            }
            SHPDestroyObject( shape );
        }
    }
    catch(...) {
        SHPDestroyObject( shape );
    }

    SHPClose( handle );
    DBFClose( dbfhandle );

    if ( i >= entities && document->size() != 0 ) {
        s_l = 0;

        document->setDocumentRole( role );
        document->setName( QFileInfo( fileName ).fileName() );
        document->setFileName( fileName );

        emit parsingFinished( document );
    }
    else {
        delete document;

        s_l++;
        if( s_l >= m_rangeLimits.size() ) {
            s_l = 0;
            emit parsingFinished( 0 );
        }
        else
            parseFile( fileName, role );
    }
}

}

#include "ShpRunner.moc"

ShpRunner.h
Code: Select all
//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2011 Thibaut Gridel <tgridel@free.fr>
// Copyright 2013 Bogdanov Artem <temabogdanov@gmail.com>

#ifndef MARBLESHPRUNNER_H
#define MARBLESHPRUNNER_H

#include "ParsingRunner.h"


namespace Marble
{

class GeoDataDocument;
class GeoDataPlacemark;
class GeoDataLineString;

class ShpRunner : public ParsingRunner
{
    Q_OBJECT
public:
    explicit ShpRunner(QObject *parent = 0);
    ~ShpRunner();
    virtual void parseFile( const QString &fileName, DocumentRole role );

protected:
    QString m_nameFields;
    QString m_noteFields;

    QVector<int> m_rangeLimits;
};

}
#endif // MARBLESHPRUNNER_H


2. Also I want to share much more simple (without additional sorting and thus without additional performance penalties) Z-ordering. Nevertheless, a small additional performance penalty can occur in existing sorting (functions GeoGraphicsScene::addItem(GeoGraphicsItem*) and GeoGraphicsScenePrivate::addItems(const TileId&, QList<GeoGraphicsItem*>&, int).
So, change some code in function GeometryLayerPrivate::createGraphicsItemFromGeometry (file GeometryLayer.cpp):
Code: Select all
void GeometryLayerPrivate::createGraphicsItemFromGeometry( const GeoDataGeometry* object, const GeoDataPlacemark *placemark )
{
    GeoGraphicsItem *item = 0;
    qreal zAlt = 0;                                                         ///+
    if ( object->nodeType() == GeoDataTypes::GeoDataLineStringType )
    {
        const GeoDataLineString* line = static_cast<const GeoDataLineString*>( object );
        item = new GeoLineStringGraphicsItem( placemark, line );
        zAlt = line->latLonAltBox().center().altitude();                    ///+
    }
    else if ( object->nodeType() == GeoDataTypes::GeoDataLinearRingType )
    {
        const GeoDataLinearRing *ring = static_cast<const GeoDataLinearRing*>( object );
        item = new GeoPolygonGraphicsItem( placemark, ring );
        zAlt = ring->latLonAltBox().center().altitude();                    ///+
    }
    else if ( object->nodeType() == GeoDataTypes::GeoDataPolygonType )
    {
        const GeoDataPolygon *poly = static_cast<const GeoDataPolygon*>( object );
        item = new GeoPolygonGraphicsItem( placemark, poly );
        zAlt = poly->latLonAltBox().center().altitude();                    ///+
    }
    else if ( object->nodeType() == GeoDataTypes::GeoDataMultiGeometryType  )
    {
        const GeoDataMultiGeometry *multigeo = static_cast<const GeoDataMultiGeometry*>( object );
        int rowCount = multigeo->size();
        for ( int row = 0; row < rowCount; ++row )
        {
            createGraphicsItemFromGeometry( multigeo->child( row ), placemark );
        }
    }
    else if ( object->nodeType() == GeoDataTypes::GeoDataMultiTrackType  )
    {
        const GeoDataMultiTrack *multitrack = static_cast<const GeoDataMultiTrack*>( object );
        int rowCount = multitrack->size();
        for ( int row = 0; row < rowCount; ++row )
        {
            createGraphicsItemFromGeometry( multitrack->child( row ), placemark );
        }
    }
    else if ( object->nodeType() == GeoDataTypes::GeoDataTrackType )
    {
        const GeoDataTrack *track = static_cast<const GeoDataTrack*>( object );
        item = new GeoTrackGraphicsItem( placemark, track );
        zAlt = track->latLonAltBox().center().altitude();                   ///+
    }
    if ( !item )
        return;
    item->setStyle( placemark->style() );
    item->setVisible( placemark->isGloballyVisible() );
///-    item->setZValue( s_defaultZValues[placemark->visualCategory()] );
    item->setZValue( s_defaultZValues[placemark->visualCategory()]          ///+
        + zAlt / 1000000000.0 );                                            ///+
    item->setMinZoomLevel( s_defaultMinZoomLevels[placemark->visualCategory()] );
    m_scene.addItem( item );
}


Bookmarks



Who is online

Registered users: bancha, Bing [Bot], Evergrowing, Google [Bot], lockheed, mesutakcan, Sogou [Bot]