Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions src/core/symbology-ng/qgssymbollayerv2utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2299,6 +2299,8 @@ QgsVectorColorRampV2* QgsSymbolLayerV2Utils::loadColorRamp( QDomElement& element
return QgsVectorRandomColorRampV2::create( props );
else if ( rampType == "colorbrewer" )
return QgsVectorColorBrewerColorRampV2::create( props );
else if ( rampType == "cpt-city" )
return QgsCptCityColorRampV2::create( props );
else
{
QgsDebugMsg( "unknown colorramp type " + rampType );
Expand All @@ -2317,6 +2319,20 @@ QDomElement QgsSymbolLayerV2Utils::saveColorRamp( QString name, QgsVectorColorRa
return rampEl;
}

// parse color definition with format "rgb(0,0,0)" or "0,0,0"
// should support other formats like FFFFFF
QColor QgsSymbolLayerV2Utils::parseColor( QString colorStr )
{
if ( colorStr.startsWith( "rgb(" ) )
{
colorStr = colorStr.mid( 4, colorStr.size() - 5 ).trimmed();
}
QStringList p = colorStr.split( QChar( ',' ) );
if ( p.count() != 3 )
return QColor();
return QColor( p[0].toInt(), p[1].toInt(), p[2].toInt() );
}

double QgsSymbolLayerV2Utils::lineWidthScaleFactor( QgsRenderContext& c, QgsSymbolV2::OutputUnit u )
{

Expand Down
3 changes: 3 additions & 0 deletions src/core/symbology-ng/qgssymbollayerv2utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ class CORE_EXPORT QgsSymbolLayerV2Utils
static QgsVectorColorRampV2* loadColorRamp( QDomElement& element );
static QDomElement saveColorRamp( QString name, QgsVectorColorRampV2* ramp, QDomDocument& doc );

/** parse color definition with format "rgb(0,0,0)" or "0,0,0" */
static QColor parseColor( QString colorStr );

/**Returns the line width scale factor depending on the unit and the paint device*/
static double lineWidthScaleFactor( QgsRenderContext& c, QgsSymbolV2::OutputUnit u );
/**Returns scale factor painter units -> pixel dimensions*/
Expand Down
538 changes: 536 additions & 2 deletions src/core/symbology-ng/qgsvectorcolorrampv2.cpp

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions src/core/symbology-ng/qgsvectorcolorrampv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,86 @@ class CORE_EXPORT QgsVectorColorBrewerColorRampV2 : public QgsVectorColorRampV2
QList<QColor> mPalette;
};


#define DEFAULT_CPTCITY_SCHEMENAME "cb/div/BrBG_" //change this
#define DEFAULT_CPTCITY_VARIANTNAME "05"

class CORE_EXPORT QgsCptCityColorRampV2 : public QgsVectorColorRampV2
{
public:
QgsCptCityColorRampV2( QString schemeName = DEFAULT_CPTCITY_SCHEMENAME,
QString variantName = DEFAULT_CPTCITY_VARIANTNAME );

static QgsVectorColorRampV2* create( const QgsStringMap& properties = QgsStringMap() );

virtual QColor color( double value ) const;

virtual QString type() const { return "cpt-city"; }

virtual QgsVectorColorRampV2* clone() const;

virtual QgsStringMap properties() const;

int count() const { return mPalette.count(); }

QString schemeName() const { return mSchemeName; }
QString variantName() const { return mVariantName; }

/* void setSchemeName( QString schemeName ) { mSchemeName = schemeName; loadPalette(); } */
/* void setVariantName( QString variantName ) { mVariantName = variantName; loadPalette(); } */
/* lazy loading - have to call loadPalette() explicitly */
void setSchemeName( QString schemeName ) { mSchemeName = schemeName; }
void setVariantName( QString variantName ) { mVariantName = variantName; }
void setName( QString schemeName, QString variantName = "" )
{ mSchemeName = schemeName; mVariantName = variantName; loadPalette(); }

void loadPalette();
bool isContinuous() const { return mContinuous; }

QString getFilename() const;
bool loadFile( QString filename = "" );

/* static QList<QColor> listSchemeColors( QString schemeName, int colors ); */
static QList<int> listSchemeVariants( QString schemeName );

static QString getBaseDir();
static void setBaseDir( QString dirName ) { mBaseDir = dirName; }
static bool loadSchemes( QString rootDir = "", bool reset = false );
/** Is the minimal (free to distribute) set of schemes available? Currently returns hasAllSchemes, because we don't have a minimal set yet. */
static bool hasBasicSchemes();
/** Is the entire archive available? Currently tests that there is at least one scheme. */
static bool hasAllSchemes();
static QStringList listSchemeCollections( QString collectionName = "", bool recursive = false );
static QStringList listSchemeNames( QString collectionName );
static QgsCptCityColorRampV2* colorRampFromSVGFile( QString svgFile );
static QgsCptCityColorRampV2* colorRampFromSVGString( QString svgString );

static const QMap< QString, QStringList > schemeMap() { return mSchemeMap; }
/* static const QMap< QString, int > schemeNumColors() { return mSchemeNumColors; } */
static const QMap< QString, QStringList > schemeVariants() { return mSchemeVariants; }
static const QMap< QString, QString > collectionNames() { return mCollectionNames; }
static const QMap< QString, QStringList > collectionSelections() { return mCollectionSelections; }

protected:

typedef QMap<double, QColor> StopsMap;

QString mSchemeName;
QString mVariantName;
bool mContinuous;
QList< QColor > mPalette;
QList< double > mPaletteStops;
/* QMap< double, QColor > mPalette; */

static QString mBaseDir;
static QStringList mCollections;
static QMap< QString, QStringList > mSchemeMap; //key is collection, value is schemes
/* mSchemeNumColors removed, instead read on demand */
/* static QMap< QString, int > mSchemeNumColors; //key is scheme, value is # colors (if no variants) */
static QMap< QString, QStringList > mSchemeVariants; //key is scheme, value is variants
static QMap< QString, QString > mCollectionNames; //key is name, value is description
static QMap< QString, QStringList > mCollectionSelections;

};

#endif
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ symbology-ng/qgssymbolv2selectordialog.cpp
symbology-ng/qgsvectorgradientcolorrampv2dialog.cpp
symbology-ng/qgsvectorrandomcolorrampv2dialog.cpp
symbology-ng/qgsvectorcolorbrewercolorrampv2dialog.cpp
symbology-ng/qgscptcitycolorrampv2dialog.cpp
symbology-ng/characterwidget.cpp
symbology-ng/qgsstylev2exportimportdialog.cpp
symbology-ng/qgsellipsesymbollayerv2widget.cpp
Expand Down Expand Up @@ -123,6 +124,7 @@ symbology-ng/qgssymbolv2selectordialog.h
symbology-ng/qgsvectorgradientcolorrampv2dialog.h
symbology-ng/qgsvectorrandomcolorrampv2dialog.h
symbology-ng/qgsvectorcolorbrewercolorrampv2dialog.h
symbology-ng/qgscptcitycolorrampv2dialog.h
symbology-ng/qgscolorrampcombobox.h
symbology-ng/characterwidget.h
symbology-ng/qgspenstylecombobox.h
Expand Down
35 changes: 34 additions & 1 deletion src/gui/raster/qgssinglebandpseudocolorrendererwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
#include "qgssinglebandpseudocolorrendererwidget.h"
#include "qgssinglebandpseudocolorrenderer.h"
#include "qgsrasterlayer.h"

// for color ramps - todo add rasterStyle and refactor raster vs. vector ramps
#include "qgsstylev2.h"
#include "qgsvectorcolorrampv2.h"

#include <QColorDialog>
#include <QFileDialog>
#include <QMessageBox>
Expand All @@ -29,6 +34,8 @@ QgsSingleBandPseudoColorRendererWidget::QgsSingleBandPseudoColorRendererWidget(
{
setupUi( this );

mColorRampComboBox->populate( QgsStyleV2::defaultStyle() );

if ( !mRasterLayer )
{
return;
Expand Down Expand Up @@ -213,19 +220,45 @@ void QgsSingleBandPseudoColorRendererWidget::on_mClassifyButton_clicked()
}
}

#if 0
//hard code color range from blue -> red for now. Allow choice of ramps in future
int colorDiff = 0;
if ( numberOfEntries != 0 )
{
colorDiff = ( int )( 255 / numberOfEntries );
}

for ( int i = 0; i < numberOfEntries; ++i )
{
QColor currentColor;
currentColor.setRgb( colorDiff*i, 0, 255 - colorDiff * i );
entryColors.push_back( currentColor );
}
#endif

QgsVectorColorRampV2* colorRamp = mColorRampComboBox->currentColorRamp();
if ( ! colorRamp )
{
//hard code color range from blue -> red (previous default)
int colorDiff = 0;
if ( numberOfEntries != 0 )
{
colorDiff = ( int )( 255 / numberOfEntries );
}

for ( int i = 0; i < numberOfEntries; ++i )
{
QColor currentColor;
currentColor.setRgb( colorDiff*i, 0, 255 - colorDiff * i );
entryColors.push_back( currentColor );
}
}
else
{
for ( int i = 0; i < numberOfEntries; ++i )
{
entryColors.push_back( colorRamp->color( ( ( double ) i ) / numberOfEntries ) );
}
}

mColormapTreeWidget->clear();

Expand Down
385 changes: 385 additions & 0 deletions src/gui/symbology-ng/qgscptcitycolorrampv2dialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,385 @@
/***************************************************************************
qgscptcitycolorrampv2dialog.cpp
---------------------
begin : July 2012
copyright : (C) 2012 by Etienne Tourigny
email : etourigny dot dev at gmail.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgscptcitycolorrampv2dialog.h"

#include "qgsvectorcolorrampv2.h"
#include "qgslogger.h"
#include "qgsapplication.h"

#include <QPushButton>
#include <QTextEdit>

#if 0 // unused
static void updateColorButton( QAbstractButton* button, QColor color )
{
QPixmap p( 20, 20 );
p.fill( color );
button->setIcon( QIcon( p ) );
}
#endif

/////////

QgsCptCityColorRampV2Dialog::QgsCptCityColorRampV2Dialog( QgsCptCityColorRampV2* ramp, QWidget* parent )
: QDialog( parent ), mRamp( ramp )
{

setupUi( this );

QgsCptCityColorRampV2::loadSchemes( "" );
// QgsCptCityColorRampV2::loadSchemes( "cb" );

// show information on how to install cpt-city files if none are found
if ( ! QgsCptCityColorRampV2::hasAllSchemes() )
{
QTextEdit *edit = new QTextEdit();
edit->setReadOnly( true );
// not sure if we want this long string to be translated
QString helpText = tr( "Error - cpt-city gradient files not found.\n\n"
"Please download the complete collection (in svg format) "
"and unzip it to your QGis settings directory [%1] .\n\n"
"This file can be found at [%2]\nand current file is [%3]"
).arg( QgsApplication::qgisSettingsDirPath()
).arg( "http://soliton.vm.bytemark.co.uk/pub/cpt-city/pkg/"
).arg( "http://soliton.vm.bytemark.co.uk/pub/cpt-city/pkg/cpt-city-svg-2.02.zip" );
edit->setText( helpText );
stackedWidget->addWidget( edit );
stackedWidget->setCurrentIndex( 1 );
buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
return;
}

populateSchemes( "author" );
treeWidget->setCurrentItem( findPath( ramp->schemeName() ) );
populateVariants();
cboVariantName->setCurrentIndex( cboVariantName->findData( ramp->variantName(), Qt::UserRole ) );
connect( cboVariantName, SIGNAL( currentIndexChanged( int ) ), this, SLOT( setVariantName() ) );
updatePreview();
}

// inspired from QgsBrowserModel::findPath( QString path )
// but using a QTreeWidget instead of QgsBrowserModel
// would be simpler if we used the model/view framework
QTreeWidgetItem* QgsCptCityColorRampV2Dialog::findPath( QString path )
{
QTreeWidgetItem *item = 0, *childItem = 0;
QString itemPath, childPath;

for ( int i = 0; i < treeWidget->topLevelItemCount(); i++ )
{
item = treeWidget->topLevelItem( i );
if ( !item )
return 0; // an error occurred
itemPath = item->data( 0, Qt::UserRole ).toString();

if ( itemPath == path )
{
QgsDebugMsg( "Arrived " + itemPath );
return item; // we have found the item we have been looking for
}

if ( itemPath.endsWith( "/" ) && path.startsWith( itemPath ) )
{
// we have found a preceding item: stop searching on this level and go deeper
bool foundChild = true;

while ( foundChild )
{
foundChild = false; // assume that the next child item will not be found

for ( int j = 0; j < item->childCount(); j++ )
{
childItem = item->child( j );
if ( !childItem )
return 0; // an error occurred
childPath = childItem->data( 0, Qt::UserRole ).toString();

if ( childPath == path )
{
return childItem; // we have found the item we have been looking for
}

if ( childPath.endsWith( "/" ) && path.startsWith( childPath ) )
{
// we have found a preceding item: stop searching on this level and go deeper
foundChild = true;
item = childItem;
break;
}
}
}
break;
}
}
return 0; // not found
}

void QgsCptCityColorRampV2Dialog::populateSchemes( QString view )
{
QStringList collections;
QTreeWidgetItem *item, *childItem;
if ( view == "author" )
{
treeWidget->blockSignals( true );
treeWidget->clear();
foreach( QString collectionName, QgsCptCityColorRampV2::listSchemeCollections() )
{
item = makeCollectionItem( collectionName );
treeWidget->addTopLevelItem( item );
}
treeWidget->blockSignals( false );
}
else if ( view == "selections" )
{
treeWidget->blockSignals( true );
treeWidget->clear();

QMapIterator< QString, QStringList> it( QgsCptCityColorRampV2::collectionSelections() );
while ( it.hasNext() )
{
it.next();
QString path = it.key();
QString descStr = QgsCptCityColorRampV2::collectionNames().value( path );

// TODO shorten child names and fix / at beginning of name
// TODO when collection has only 1 item (e.g. es_skywalker), bring up one level
item = new QTreeWidgetItem( QStringList() << path << descStr << QString() );
QFont font;
font.setBold( true );
item->setData( 0, Qt::FontRole, QVariant( font ) );
item->setData( 1, Qt::FontRole, QVariant( font ) );

// add children schemes and collections
foreach( QString childPath, it.value() )
{
if ( childPath.endsWith( "/" ) )
{
childPath.chop( 1 );
childItem = makeCollectionItem( childPath );
childItem->setText( 0, childPath ); //make sure full path is visible
item->addChild( childItem );
}
else
{
makeSchemeItem( item, "", childPath );
}
}
treeWidget->addTopLevelItem( item );
}
treeWidget->blockSignals( false );
}
else
{
QgsDebugMsg( "invalid view " + view );
return;
}

treeWidget->setColumnWidth( 0, 250 );
}

void QgsCptCityColorRampV2Dialog::populateVariants()
{
if ( ! treeWidget->currentItem() )
return;

cboVariantName->blockSignals( true );

QString oldVariant = cboVariantName->currentText();
cboVariantName->clear();

foreach( QString variant, treeWidget->currentItem()->data( 1, Qt::UserRole ).toString().split( " ", QString::SkipEmptyParts ) )
{
QString variantStr = variant;
if ( variantStr.startsWith( "-" ) || variantStr.startsWith( "_" ) )
variantStr.remove( 0, 1 );
cboVariantName->addItem( variantStr );
cboVariantName->setItemData( cboVariantName->count() - 1, variant, Qt::UserRole );
}

cboVariantName->blockSignals( false );

if ( cboVariantName->count() > 0 )
{
cboVariantName->setEnabled( true );
// try to set the original variant again (if exists)
// TODO - is this really necessary?
int idx = cboVariantName->findText( oldVariant );
if ( idx == -1 ) // not found?
{
// use the item in the middle
idx = cboVariantName->count() / 2;
}
cboVariantName->setCurrentIndex( idx );
}
else
{
cboVariantName->setEnabled( false );
setVariantName();
}

}

void QgsCptCityColorRampV2Dialog::on_treeWidget_currentItemChanged( QTreeWidgetItem * current, QTreeWidgetItem * previous )
{
Q_UNUSED( previous );
// directories have name data that ends with /
if ( ! current->data( 0, Qt::UserRole ).toString().endsWith( "/" ) )
{
lblSchemeName->setText( current->data( 0, Qt::UserRole ).toString() );
setSchemeName();
// populateVariants();
}
}

void QgsCptCityColorRampV2Dialog::on_treeWidget_itemExpanded( QTreeWidgetItem * item )
{
// set color count when item is expanded
QgsCptCityColorRampV2 ramp( "", "" );
QTreeWidgetItem* childItem = 0;
QString itemPath, itemDesc;

for ( int i = 0; i < item->childCount(); i++ )
{
childItem = item->child( i );

// items with null description need information, those with "" (i.e. unnamed collections) not be checked
// TODO set type when there are variants - based on the first file
if ( childItem && childItem->text( 1 ).isNull() )
{
itemPath = childItem->data( 0, Qt::UserRole ).toString();
itemDesc = "";
ramp.setSchemeName( itemPath );
if ( ramp.loadFile() )
{
itemDesc = QString::number( ramp.count() ) + " " + tr( "colors" ) + " - ";
if ( ramp.isContinuous() )
itemDesc += tr( "continuous" );
else
itemDesc += tr( "discrete" );
}
childItem->setText( 1, " " + itemDesc );
}
}
}

void QgsCptCityColorRampV2Dialog::on_buttonGroupView_buttonClicked( QAbstractButton * button )
{
if ( button->objectName() == "rbtnAuthor" )
{
populateSchemes( "author" );
}
else if ( button->objectName() == "rbtnSelections" )
{
populateSchemes( "selections" );
}
else
{
QgsDebugMsg( "invalid button " + button->objectName() );
}
}

void QgsCptCityColorRampV2Dialog::updatePreview()
{
QSize size( 300, 40 );
mRamp->loadFile();
lblPreview->setPixmap( QgsSymbolLayerV2Utils::colorRampPreviewPixmap( mRamp, size ) );
}

void QgsCptCityColorRampV2Dialog::setSchemeName()
{
if ( treeWidget->currentItem() )
{
mRamp->setSchemeName( treeWidget->currentItem()->data( 0, Qt::UserRole ).toString() );
// setVariantName();

// populate list of variants
populateVariants();

// updatePreview();
}
}

void QgsCptCityColorRampV2Dialog::setVariantName()
{
mRamp->setVariantName( cboVariantName->itemData( cboVariantName->currentIndex(), Qt::UserRole ).toString() );
updatePreview();
}

/* collection items have columns name / path/ and desc/null */
QTreeWidgetItem * QgsCptCityColorRampV2Dialog::makeCollectionItem( const QString& path )
{
// add root item
QString descStr = QgsCptCityColorRampV2::collectionNames().value( path );
if ( descStr.isNull() )
descStr = "";
QTreeWidgetItem *item = new QTreeWidgetItem( QStringList() << QFileInfo( path ).baseName() << descStr );
item->setData( 0, Qt::UserRole, QString( path ) + "/" ); // add / to end to identify it as a collection item
//set collections bold to identify them
QFont font;
font.setBold( true );
item->setData( 0, Qt::FontRole, QVariant( font ) );
item->setData( 1, Qt::FontRole, QVariant( font ) );

// add children collections
QTreeWidgetItem *childItem;
foreach( QString childPath, QgsCptCityColorRampV2::listSchemeCollections( path ) )
{
childItem = makeCollectionItem( childPath );
item->addChild( childItem );
}

// add children schemes
foreach( QString schemeName, QgsCptCityColorRampV2::schemeMap().value( path ) )
{
makeSchemeItem( item, path, schemeName );
}
return item;
}

/* scheme items have columns 0 = ( name / path ) and
[if has variants] 1 = ( #variants / variants )
[if has colors] 1 = ( null / null ) which becomes ( <info> / null ) after populating
*/
void QgsCptCityColorRampV2Dialog::makeSchemeItem( QTreeWidgetItem *item, const QString& path, const QString& schemeName )
{
QString descStr, descData;//, variantStr;
QString curName, curVariant;
QStringList listVariant;
QTreeWidgetItem *childItem;

// // descStr = schemeName;
listVariant = QgsCptCityColorRampV2::schemeVariants().value( path + "/" + schemeName );

if ( listVariant.count() > 1 )
{
// variantStr = QString::number( listVariant.count() ) + " " + tr( "variants" );
descStr = " " + QString::number( listVariant.count() ) + " " + tr( "variants" );
descData = listVariant.join( " " );
}

childItem = new QTreeWidgetItem( QStringList() << schemeName << descStr );
childItem->setData( 0, Qt::UserRole, path + "/" + schemeName );
if ( ! descData.isNull() )
childItem->setData( 1, Qt::UserRole, descData );
item->addChild( childItem );

// create a preview icon using five color variant
// QgsCptCityColorRampV2* r = new QgsCptCityColorRampV2( schemeName, 5 );
// QIcon icon = QgsSymbolLayerV2Utils::colorRampPreviewIcon( r, iconSize );
// delete r;

}

53 changes: 53 additions & 0 deletions src/gui/symbology-ng/qgscptcitycolorrampv2dialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/***************************************************************************
qgscptcitycolorrampv2dialog.h
---------------------
begin : July 2012
copyright : (C) 2012 by Etienne Tourigny
email : etourigny dot dev at gmail.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSCPTCITYCOLORRAMPV2DIALOG_H
#define QGSCPTCITYCOLORRAMPV2DIALOG_H

#include <QDialog>

#include "ui_qgscptcitycolorrampv2dialogbase.h"

class QgsCptCityColorRampV2;

class GUI_EXPORT QgsCptCityColorRampV2Dialog : public QDialog, private Ui::QgsCptCityColorRampV2DialogBase
{
Q_OBJECT

public:
QgsCptCityColorRampV2Dialog( QgsCptCityColorRampV2* ramp, QWidget* parent = NULL );

public slots:
void setSchemeName();
void setVariantName();
void populateSchemes( QString view = "author" );
void populateVariants();

void on_treeWidget_currentItemChanged( QTreeWidgetItem * current, QTreeWidgetItem * previous );
void on_treeWidget_itemExpanded( QTreeWidgetItem * item );
void on_buttonGroupView_buttonClicked ( QAbstractButton * button );

protected:

void updatePreview();
QTreeWidgetItem* findPath( QString path );
QTreeWidgetItem * makeCollectionItem( const QString& path );
// TODO rename Scheme to something else, maybe data
void makeSchemeItem( QTreeWidgetItem *item, const QString& path, const QString& schemeName );

QgsCptCityColorRampV2* mRamp;
};

#endif
28 changes: 27 additions & 1 deletion src/gui/symbology-ng/qgsstylev2managerdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "qgsvectorgradientcolorrampv2dialog.h"
#include "qgsvectorrandomcolorrampv2dialog.h"
#include "qgsvectorcolorbrewercolorrampv2dialog.h"
#include "qgscptcitycolorrampv2dialog.h"
#include "qgsstylev2exportimportdialog.h"

#include <QFile>
Expand Down Expand Up @@ -302,6 +303,7 @@ QString QgsStyleV2ManagerDialog::addColorRampStatic( QWidget* parent, QgsStyleV2
// let the user choose the color ramp type
QStringList rampTypes;
rampTypes << tr( "Gradient" ) << tr( "Random" ) << tr( "ColorBrewer" );
rampTypes << tr( "cpt-city" ); // todo, only for rasters?
bool ok;
QString rampType = QInputDialog::getItem( parent, tr( "Color ramp type" ),
tr( "Please select color ramp type:" ), rampTypes, 0, false, &ok );
Expand Down Expand Up @@ -342,9 +344,23 @@ QString QgsStyleV2ManagerDialog::addColorRampStatic( QWidget* parent, QgsStyleV2
}
ramp = brewerRamp;
}
else if ( rampType == tr( "cpt-city" ) )
{
QgsCptCityColorRampV2* cptCityRamp = new QgsCptCityColorRampV2();
QgsCptCityColorRampV2Dialog dlg( cptCityRamp, parent );
if ( !dlg.exec() )
{
delete cptCityRamp;
return QString();
}
ramp = cptCityRamp;
}
else
{
Q_ASSERT( 0 && "invalid ramp type" );
// Q_ASSERT( 0 && "invalid ramp type" );
// bailing out is rather harsh!
QgsDebugMsg( "invalid ramp type " + rampType );
return QString();
}

// get name
Expand Down Expand Up @@ -456,6 +472,16 @@ bool QgsStyleV2ManagerDialog::editColorRamp()
return false;
}
}
else if ( ramp->type() == "cpt-city" )
{
QgsCptCityColorRampV2* cptCityRamp = static_cast<QgsCptCityColorRampV2*>( ramp );
QgsCptCityColorRampV2Dialog dlg( cptCityRamp, this );
if ( !dlg.exec() )
{
delete ramp;
return false;
}
}
else
{
Q_ASSERT( 0 && "invalid ramp type" );
Expand Down
298 changes: 298 additions & 0 deletions src/ui/qgscptcitycolorrampv2dialogbase.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsCptCityColorRampV2DialogBase</class>
<widget class="QDialog" name="QgsCptCityColorRampV2DialogBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>807</width>
<height>503</height>
</rect>
</property>
<property name="windowTitle">
<string>cpt-city color ramp</string>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>View</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QRadioButton" name="rbtnAuthor">
<property name="text">
<string>All by author</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroupView</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbtnSelections">
<property name="text">
<string>Selections by theme</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroupView</string>
</attribute>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTreeWidget" name="treeWidget">
<attribute name="headerStretchLastSection">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Info</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QGridLayout" name="_2">
<item row="1" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Variant</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Scheme name</string>
</property>
</widget>
</item>
<item row="0" column="4" rowspan="2">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>71</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3">
<widget class="QLabel" name="lblSchemeName">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QComboBox" name="cboVariantName">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Preview</string>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QLabel" name="lblPreview">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QgsCptCityColorRampV2DialogBase</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>258</x>
<y>281</y>
</hint>
<hint type="destinationlabel">
<x>168</x>
<y>256</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QgsCptCityColorRampV2DialogBase</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>363</x>
<y>273</y>
</hint>
<hint type="destinationlabel">
<x>371</x>
<y>259</y>
</hint>
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="buttonGroupView"/>
</buttongroups>
</ui>
68 changes: 62 additions & 6 deletions src/ui/qgssinglebandpseudocolorrendererwidgetbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>505</width>
<width>584</width>
<height>400</height>
</rect>
</property>
Expand Down Expand Up @@ -146,7 +146,7 @@
<item row="0" column="0">
<widget class="QLabel" name="mNumberOfEntriesLabel">
<property name="text">
<string>Number of entries</string>
<string>Classes</string>
</property>
</widget>
</item>
Expand All @@ -163,28 +163,84 @@
</property>
</widget>
</item>
<item row="0" column="2">
<item row="0" column="3">
<widget class="QLabel" name="mClassificationModeLabel">
<property name="text">
<string>Classification mode</string>
<string>Mode</string>
</property>
</widget>
</item>
<item row="0" column="3">
<item row="0" column="4">
<widget class="QComboBox" name="mClassificationModeComboBox"/>
</item>
<item row="0" column="4">
<item row="0" column="9">
<widget class="QPushButton" name="mClassifyButton">
<property name="text">
<string>Classify</string>
</property>
</widget>
</item>
<item row="0" column="5">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="6">
<widget class="QLabel" name="label">
<property name="text">
<string>Color ramp</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="7">
<widget class="QgsColorRampComboBox" name="mColorRampComboBox"/>
</item>
<item row="0" column="8">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsColorRampComboBox</class>
<extends>QComboBox</extends>
<header>qgscolorrampcombobox.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../images/images.qrc"/>
</resources>
Expand Down
1 change: 1 addition & 0 deletions tests/src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,4 @@ ADD_QGIS_TEST(rulebasedrenderertest testqgsrulebasedrenderer.cpp)
ADD_QGIS_TEST(ziplayertest testziplayer.cpp)
ADD_QGIS_TEST(dataitemtest testqgsdataitem.cpp)
ADD_QGIS_TEST(composermaptest testqgscomposermap.cpp)
ADD_QGIS_TEST(stylev2test testqgsstylev2.cpp)
98 changes: 98 additions & 0 deletions tests/src/core/testqgsrasterlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <qgsmaprenderer.h>
#include <qgsmaplayerregistry.h>
#include "qgssinglebandpseudocolorrenderer.h"
#include "qgsvectorcolorrampv2.h"

//qgis unit test includes
#include <qgsrenderchecker.h>
Expand All @@ -54,6 +55,9 @@ class TestQgsRasterLayer: public QObject

void isValid();
void pseudoColor();
void colorRamp1();
void colorRamp2();
void colorRamp3();
void landsatBasic();
void landsatBasic875Qml();
void checkDimensions();
Expand All @@ -63,6 +67,11 @@ class TestQgsRasterLayer: public QObject
private:
bool render( QString theFileName );
bool setQml( QString theType );
void populateColorRampShader( QgsColorRampShader* colorRampShader,
QgsVectorColorRampV2* colorRamp,
int numberOfEntries );
bool testColorRamp( QString name, QgsVectorColorRampV2* colorRamp,
QgsColorRampShader::ColorRamp_TYPE type, int numberOfEntries );
QString mTestDataDir;
QgsRasterLayer * mpRasterLayer;
QgsRasterLayer * mpLandsatRasterLayer;
Expand Down Expand Up @@ -162,6 +171,95 @@ void TestQgsRasterLayer::pseudoColor()
QVERIFY( render( "raster_pseudo" ) );
}

void TestQgsRasterLayer::populateColorRampShader( QgsColorRampShader* colorRampShader,
QgsVectorColorRampV2* colorRamp,
int numberOfEntries )

{
// adapted from QgsSingleBandPseudoColorRendererWidget::on_mClassifyButton_clicked()
// and TestQgsRasterLayer::pseudoColor()
// would be better to add a more generic function to api that does this
int bandNr = 1;
QgsRasterBandStats myRasterBandStats = mpRasterLayer->dataProvider()->bandStatistics( bandNr );

QList<double> entryValues;
QList<QColor> entryColors;
double currentValue = myRasterBandStats.minimumValue;
double intervalDiff;
if ( numberOfEntries > 1 )
{
//because the highest value is also an entry, there are (numberOfEntries - 1)
//intervals
intervalDiff = ( myRasterBandStats.maximumValue - myRasterBandStats.minimumValue ) /
( numberOfEntries - 1 );
}
else
{
intervalDiff = myRasterBandStats.maximumValue - myRasterBandStats.minimumValue;
}

for ( int i = 0; i < numberOfEntries; ++i )
{
entryValues.push_back( currentValue );
currentValue += intervalDiff;
entryColors.push_back( colorRamp->color((( double ) i ) / numberOfEntries ) );
}

//items to imitate old pseudo color renderer
QList<QgsColorRampShader::ColorRampItem> colorRampItems;
QList<double>::const_iterator value_it = entryValues.begin();
QList<QColor>::const_iterator color_it = entryColors.begin();
for ( ; value_it != entryValues.end(); ++value_it, ++color_it )
{
colorRampItems.append( QgsColorRampShader::ColorRampItem( *value_it, *color_it ) );
}
colorRampShader->setColorRampItemList( colorRampItems );
}

bool TestQgsRasterLayer::testColorRamp( QString name, QgsVectorColorRampV2* colorRamp,
QgsColorRampShader::ColorRamp_TYPE type, int numberOfEntries )
{
QgsRasterShader* rasterShader = new QgsRasterShader();
QgsColorRampShader* colorRampShader = new QgsColorRampShader();
colorRampShader->setColorRampType( type );

populateColorRampShader( colorRampShader, colorRamp, numberOfEntries );

rasterShader->setRasterShaderFunction( colorRampShader );
QgsSingleBandPseudoColorRenderer* r = new QgsSingleBandPseudoColorRenderer( mpRasterLayer->dataProvider(), 1, rasterShader );
mpRasterLayer->setRenderer( r );
mpMapRenderer->setExtent( mpRasterLayer->extent() );
return render( name );
}

void TestQgsRasterLayer::colorRamp1()
{
// gradient ramp
QgsVectorGradientColorRampV2* colorRamp = new QgsVectorGradientColorRampV2( QColor( Qt::red ), QColor( Qt::black ) );
QgsVectorGradientColorRampV2::StopsMap stops;
stops[ 0.5 ] = QColor( Qt::white );
colorRamp->setStops( stops );

// QVERIFY( testColorRamp( "raster_colorRamp1", colorRamp, QgsColorRampShader::INTERPOLATED, 5 ) );
QVERIFY( testColorRamp( "raster_colorRamp1", colorRamp, QgsColorRampShader::DISCRETE, 10 ) );
}

void TestQgsRasterLayer::colorRamp2()
{
// ColorBrewer ramp
QVERIFY( testColorRamp( "raster_colorRamp2",
new QgsVectorColorBrewerColorRampV2( "BrBG", 10 ),
QgsColorRampShader::DISCRETE, 10 ) );
}

void TestQgsRasterLayer::colorRamp3()
{
// cpt-city ramp
QVERIFY( testColorRamp( "raster_colorRamp3",
new QgsCptCityColorRampV2( "gmt/GMT_panoply", "" ),
QgsColorRampShader::DISCRETE, 10 ) );
}

void TestQgsRasterLayer::landsatBasic()
{
mpLandsatRasterLayer->setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchToMinimumMaximum, false );
Expand Down
209 changes: 209 additions & 0 deletions tests/src/core/testqgsstylev2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/***************************************************************************
testqgsstylev2.cpp
--------------------------------------
Date : Wed Aug 1 12:13:24 BRT 2012
Copyright : (C) 2012 Etienne Tourigny and Tim Sutton
Email : etourigny dot dev at gmail.com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QtTest>
#include <QObject>
#include <QStringList>
#include <QObject>
#include <QApplication>
#include <QFileInfo>

//qgis includes...
#include <qgsapplication.h>
#include "qgsconfig.h"
#include "qgslogger.h"
#include "qgsvectorcolorrampv2.h"

#include "qgsstylev2.h"

/** \ingroup UnitTests
* This is a unit test to verify that styles are working correctly
*/
class TestStyleV2: public QObject
{
Q_OBJECT;

private:

QgsStyleV2 *mStyle;

bool testValidColor( QgsVectorColorRampV2 *ramp, double value, QColor expected );

private slots:

// init / cleanup
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init() {};// will be called before each testfunction is executed.
void cleanup() {};// will be called after every testfunction.
// void initStyles();

void testCreateColorRamps();
void testLoadColorRamps();
void testSaveLoad();
};


// slots
void TestStyleV2::initTestCase()
{
// initialize with test directory so we don't mess with user's stuff
QgsApplication::init( QDir::homePath() + QString( "/.qgis_test" ) );
QgsApplication::initQgis();

// output test environment
QgsApplication::showSettings();

// Set up the QSettings environment
QCoreApplication::setOrganizationName( "QuantumGIS" );
QCoreApplication::setOrganizationDomain( "qgis.org" );
QCoreApplication::setApplicationName( "QGIS-TEST" );

// initialize with a clean style
QFile styleFile( QgsApplication::userStyleV2Path() );
if ( styleFile.exists() )
{
styleFile.remove();
QgsDebugMsg( "removed user style file " + styleFile.fileName() );
}
mStyle = QgsStyleV2::defaultStyle();
// mStyle->clear();
}

void TestStyleV2::cleanupTestCase()
{
// don't save
// mStyle->save();
delete mStyle;
}

bool TestStyleV2::testValidColor( QgsVectorColorRampV2 *ramp, double value, QColor expected )
{
QColor result = ramp->color( value );
if ( result != expected )
{
QWARN( QString( "value = %1 result = %2 expected = %3" ).arg( value ).arg(
result.name() ).arg( expected.name() ).toLocal8Bit().data() );
return false;
}
return true;
}

void TestStyleV2::testCreateColorRamps()
{
// gradient ramp
QgsVectorGradientColorRampV2* gradientRamp = new QgsVectorGradientColorRampV2( QColor( Qt::red ), QColor( Qt::blue ) );
QgsVectorGradientColorRampV2::StopsMap stops;
stops[ 0.5 ] = QColor( Qt::white );
gradientRamp->setStops( stops );
QVERIFY( mStyle->addColorRamp( "test_gradient", gradientRamp ) == true );

// random ramp
QgsVectorRandomColorRampV2* randomRamp = new QgsVectorRandomColorRampV2();
QVERIFY( mStyle->addColorRamp( "test_random", randomRamp ) == true );

// color brewer ramp
QgsVectorColorBrewerColorRampV2* cb1Ramp = new QgsVectorColorBrewerColorRampV2();
QVERIFY( mStyle->addColorRamp( "test_cb1", cb1Ramp ) == true );
QgsVectorColorBrewerColorRampV2* cb2Ramp = new QgsVectorColorBrewerColorRampV2( "RdYlGn", 6 );
QVERIFY( mStyle->addColorRamp( "test_cb2", cb2Ramp ) == true );

// cpt-city ramp - use gradients that are free to distribute
// set base dir because we are using a test home path - change this if we distribute a minimal set with qgis
QgsCptCityColorRampV2::setBaseDir( QDir::homePath() + QString( "/.qgis/cpt-city" ) );
QgsCptCityColorRampV2::loadSchemes( "" );
if ( QgsCptCityColorRampV2::hasBasicSchemes() )
{
QgsCptCityColorRampV2* cc1Ramp = new QgsCptCityColorRampV2( "jjg/misc/temperature", "" );
QVERIFY( mStyle->addColorRamp( "test_cc1", cc1Ramp ) == true );
QgsCptCityColorRampV2* cc2Ramp = new QgsCptCityColorRampV2( "cb/div/PiYG", "_10" );
QVERIFY( mStyle->addColorRamp( "test_cc2", cc2Ramp ) == true );
}
else
{
QWARN( "cpt-city support files not found - skipping cpt-city color ramp tests" );
}
}

void TestStyleV2::testLoadColorRamps()
{
QStringList colorRamps = mStyle->colorRampNames();
QStringList colorRampsTest = QStringList() << "BrBG" << "Spectral"
<< "test_gradient" << "test_random"
<< "test_cb1" << "test_cb2";

// values for color tests
QMultiMap< QString, QPair< double, QColor> > colorTests;
colorTests.insert( "test_gradient", qMakePair( 0.25, QColor( "#ff7f7f" ) ) );
colorTests.insert( "test_gradient", qMakePair( 0.66, QColor( "#adadff" ) ) );
// cannot test random colors!
colorTests.insert( "test_cb1", qMakePair( 0.25, QColor( "#fdae61" ) ) );
colorTests.insert( "test_cb1", qMakePair( 0.66, QColor( "#abdda4" ) ) );
colorTests.insert( "test_cb2", qMakePair( 0.25, QColor( "#fc8d59" ) ) );
colorTests.insert( "test_cb2", qMakePair( 0.66, QColor( "#d9ef8b" ) ) );

// cpt-city
if ( QgsCptCityColorRampV2::hasAllSchemes() )
{
colorRampsTest << "test_cc1";
colorTests.insert( "test_cc1", qMakePair( 0.25, QColor( "#466fcf" ) ) );
colorTests.insert( "test_cc1", qMakePair( 0.66, QColor( "#dbc85b" ) ) );
colorRampsTest << "test_cc2";
colorTests.insert( "test_cc2", qMakePair( 0.25, QColor( "#de77ae" ) ) );
colorTests.insert( "test_cc2", qMakePair( 0.66, QColor( "#b8e186" ) ) );
}

foreach( QString name, colorRampsTest )
{
QgsDebugMsg( "colorRamp " + name );
QVERIFY( colorRamps.contains( name ) );
QgsVectorColorRampV2* ramp = mStyle->colorRamp( name );
QVERIFY( ramp != 0 );
// test colors
if ( colorTests.contains( name ) )
{
QList< QPair< double, QColor> > values = colorTests.values( name );
for ( int i = 0; i < values.size(); ++i )
{
QVERIFY( testValidColor( ramp, values.at( i ).first, values.at( i ).second ) );
}
}
if ( ramp )
delete ramp;
}
}

void TestStyleV2::testSaveLoad()
{
mStyle->save();
mStyle->clear();
mStyle->load( QgsApplication::userStyleV2Path() );

QStringList colorRamps = mStyle->colorRampNames();
QStringList colorRampsTest = QStringList() << "test_gradient";

foreach( QString name, colorRampsTest )
{
QgsDebugMsg( "colorRamp " + name );
QVERIFY( colorRamps.contains( name ) );
QgsVectorColorRampV2* ramp = mStyle->colorRamp( name );
QVERIFY( ramp != 0 );
if ( ramp )
delete ramp;
}
}


QTEST_MAIN( TestStyleV2 )
#include "moc_testqgsstylev2.cxx"
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 0 additions & 2 deletions tests/testdata/zip/points3.geojson.gz.properties

This file was deleted.