featurecache_patch.diff
python/core/qgsvectorlayer.sip (working copy) | ||
---|---|---|
449 | 449 |
*/ |
450 | 450 |
virtual void updateExtents(); |
451 | 451 | |
452 |
/** |
|
453 |
* Fetch the specified feature from provider, if it is cached. This |
|
454 |
* must be called every time the provider data change outside vector layer control. |
|
455 |
* @param featureId id of feature that should be reloaded |
|
456 |
* @param rectFrom hint of rectangle, where the feature resided. If not provided, |
|
457 |
* whole cache will be searched for the id. |
|
458 |
* @param rectTo hint of rectangle, where the feature resides now. If not provided, |
|
459 |
* all provider features will be searched for the id. |
|
460 |
**/ |
|
461 |
void recacheFeature( int featureId, QgsRectangle rectFrom=QgsRectangle(), QgsRectangle rectTo=QgsRectangle() ); |
|
462 |
|
|
463 |
/** |
|
464 |
* Fetch features from specified rectangle from provider, if it is cached. This |
|
465 |
* must be called every time the provider data change outside vector layer control. |
|
466 |
* @param rect rectangle containing changed features. All features within |
|
467 |
* the rectangle will be reloaded. |
|
468 |
**/ |
|
469 |
void recacheFeatures( QgsRectangle rect ); |
|
470 | ||
452 | 471 |
signals: |
453 | 472 | |
454 | 473 |
/** This signal is emited when selection was changed */ |
src/app/qgsoptions.cpp (working copy) | ||
---|---|---|
140 | 140 |
cbxHideSplash->setChecked( settings.value( "/qgis/hideSplash", false ).toBool() ); |
141 | 141 |
cbxAttributeTableDocked->setChecked( settings.value( "/qgis/dockAttributeTable", false ).toBool() ); |
142 | 142 | |
143 |
//features cache mode |
|
144 |
QString cacheMode = settings.value( "qgis/vectorLayerCacheMode", "heuristics" ).toString(); |
|
145 |
if ( cacheMode == "all" ) |
|
146 |
radCacheAll->setChecked(true); |
|
147 |
else if ( cacheMode == "nothing" ) |
|
148 |
radCacheNothing->setChecked(true); |
|
149 |
else |
|
150 |
radCacheHeuristics->setChecked(true); |
|
151 |
|
|
143 | 152 |
//set the colour for selections |
144 | 153 |
int myRed = settings.value( "/qgis/default_selection_color_red", 255 ).toInt(); |
145 | 154 |
int myGreen = settings.value( "/qgis/default_selection_color_green", 255 ).toInt(); |
... | ... | |
345 | 354 |
settings.setValue( "qgis/capitaliseLayerName", capitaliseCheckBox->isChecked() ); |
346 | 355 |
settings.setValue( "qgis/askToSaveProjectChanges", chbAskToSaveProjectChanges->isChecked() ); |
347 | 356 |
settings.setValue( "qgis/warnOldProjectVersion", chbWarnOldProjectVersion->isChecked() ); |
348 | ||
357 |
|
|
358 |
settings.setValue( "qgis/vectorLayerCacheMode", |
|
359 |
radCacheNothing->isChecked() ? "nothing" : ( |
|
360 |
radCacheAll->isChecked() ? "all" : "heuristics" |
|
361 |
) |
|
362 |
); |
|
363 |
|
|
349 | 364 |
//overlay placement method |
350 | 365 |
int overlayIndex = mOverlayAlgorithmComboBox->currentIndex(); |
351 | 366 |
if ( overlayIndex == 1 ) |
src/core/qgsvectorlayer.cpp (working copy) | ||
---|---|---|
71 | 71 |
#include "qgsvectoroverlay.h" |
72 | 72 |
#include "qgslogger.h" |
73 | 73 |
#include "qgsmaplayerregistry.h" |
74 |
#include "qgsfeaturecache.h" |
|
74 | 75 | |
75 | 76 |
#ifdef Q_WS_X11 |
76 | 77 |
#include "qgsclipper.h" |
... | ... | |
107 | 108 |
mActiveCommand( NULL ) |
108 | 109 |
{ |
109 | 110 |
mActions = new QgsAttributeAction; |
111 |
|
|
112 |
mFeatureCache=new QgsFeatureCache; |
|
110 | 113 | |
111 | 114 |
// if we're given a provider type, try to create and bind one to this layer |
112 | 115 |
if ( ! mProviderKey.isEmpty() ) |
... | ... | |
167 | 170 | |
168 | 171 |
delete mLabel; |
169 | 172 | |
173 |
#ifdef ENABLE_GEOMETRYCACHE |
|
170 | 174 |
// Destroy any cached geometries and clear the references to them |
171 | 175 |
deleteCachedGeometries(); |
176 |
#endif |
|
172 | 177 | |
178 |
delete mFeatureCache; |
|
179 | ||
173 | 180 |
delete mActions; |
174 | 181 | |
175 | 182 |
//delete remaining overlays |
... | ... | |
687 | 694 | |
688 | 695 |
if ( mEditable ) |
689 | 696 |
{ |
697 |
#ifdef ENABLE_GEOMETRYCACHE |
|
690 | 698 |
// Destroy all cached geometries and clear the references to them |
691 | 699 |
deleteCachedGeometries(); |
692 | 700 |
mCachedGeometriesRect = rendererContext.extent(); |
701 |
#endif |
|
693 | 702 |
vertexMarker = currentVertexMarkerType(); |
694 | 703 |
mVertexMarkerOnlyForSelection = settings.value( "/qgis/digitizing/marker_only_for_selected", false ).toBool(); |
695 | 704 |
} |
... | ... | |
699 | 708 |
int featureCount = 0; |
700 | 709 |
QgsFeature fet; |
701 | 710 |
QgsAttributeList attributes = mRenderer->classificationAttributes(); |
702 |
select( attributes, rendererContext.extent() ); |
|
711 |
|
|
712 |
QgsRectangle renderExtent=rendererContext.extent(); |
|
713 |
|
|
714 |
//apply cache mode |
|
715 |
QSettings settings; |
|
716 |
QString cacheMode=settings.value( "qgis/vectorLayerCacheMode", "heuristics" ).toString(); |
|
717 |
if ( cacheMode == "heuristics" ) |
|
718 |
mFeatureCache->setCacheMode(QgsFeatureCache::CACHE_HEURISTICS); |
|
719 |
else if ( cacheMode == "all" ) |
|
720 |
mFeatureCache->setCacheMode(QgsFeatureCache::CACHE_ALL); |
|
721 |
else |
|
722 |
mFeatureCache->setCacheMode(QgsFeatureCache::CACHE_NOTHING); |
|
723 |
|
|
724 |
mFeatureCache->applyCurrentCacheMode(renderExtent); |
|
725 |
|
|
726 |
select( attributes, renderExtent); |
|
703 | 727 | |
728 |
if ( !mFeatureCache->isSelectValid() ) |
|
729 |
{ |
|
730 |
#ifdef LIMIT_CACHED_EXTENT |
|
731 |
if ( mDataProvider->extent() == mFeatureCache->getCachedRectangle() ) |
|
732 |
{ |
|
733 |
//everything is cached already, retrieve selected part |
|
734 |
//note: this will fail, if requested classification attributes changed |
|
735 |
mFeatureCache->makeSelectValid(); |
|
736 |
} |
|
737 |
- |
|
738 |
if ( !mFeatureCache->isSelectValid() ) |
|
739 |
{ |
|
740 |
//cache still invalid, we'll have to refill the cache |
|
741 |
|
|
742 |
if ( renderExtent.contains( mDataProvider->extent() ) ) |
|
743 |
{ |
|
744 |
//only cache as big rectangle as the provider can provide |
|
745 |
select( attributes, mDataProvider->extent() ); |
|
746 |
} |
|
747 |
#endif |
|
748 |
mFeatureCache->clear(); |
|
749 |
#ifdef LIMIT_CACHED_EXTENT |
|
750 |
} |
|
751 |
#endif |
|
752 |
} |
|
753 |
|
|
704 | 754 |
try |
705 | 755 |
{ |
706 | 756 |
while ( nextFeature( fet ) ) |
707 | 757 |
{ |
758 |
if ( !mFeatureCache->isInitialized() ) |
|
759 |
{ |
|
760 |
mFeatureCache->insert( fet ); |
|
761 |
} |
|
708 | 762 | |
709 | 763 |
if ( rendererContext.renderingStopped() ) |
710 | 764 |
{ |
... | ... | |
736 | 790 | |
737 | 791 |
if ( mEditable ) |
738 | 792 |
{ |
793 |
#ifdef ENABLE_GEOMETRYCACHE |
|
739 | 794 |
// Cache this for the use of (e.g.) modifying the feature's uncommitted geometry. |
740 | 795 |
mCachedGeometries[fet.id()] = *fet.geometry(); |
796 |
#endif |
|
741 | 797 | |
742 | 798 |
if ( !mVertexMarkerOnlyForSelection || sel ) |
743 | 799 |
{ |
... | ... | |
770 | 826 | |
771 | 827 |
++featureCount; |
772 | 828 |
} |
829 | ||
830 |
if ( !mFeatureCache->isInitialized() ) |
|
831 |
{ |
|
832 |
mFeatureCache->setAsInitialized(); |
|
833 |
} |
|
773 | 834 |
} |
774 | 835 |
catch ( QgsCsException &cse ) |
775 | 836 |
{ |
... | ... | |
787 | 848 | |
788 | 849 |
if ( mEditable ) |
789 | 850 |
{ |
851 |
#ifdef ENABLE_GEOMETRYCACHE |
|
790 | 852 |
QgsDebugMsg( QString( "Cached %1 geometries." ).arg( mCachedGeometries.count() ) ); |
853 |
#endif |
|
791 | 854 |
} |
792 | 855 | |
793 | 856 |
return TRUE; // Assume success always |
794 | 857 |
} |
795 | 858 | |
859 |
#ifdef ENABLE_GEOMETRYCACHE |
|
796 | 860 |
void QgsVectorLayer::deleteCachedGeometries() |
797 | 861 |
{ |
798 | 862 |
// Destroy any cached geometries |
799 | 863 |
mCachedGeometries.clear(); |
800 | 864 |
mCachedGeometriesRect = QgsRectangle(); |
801 | 865 |
} |
866 |
#endif |
|
802 | 867 | |
803 | 868 |
void QgsVectorLayer::drawVertexMarker( int x, int y, QPainter& p, QgsVectorLayer::VertexMarkerType type ) |
804 | 869 |
{ |
... | ... | |
1193 | 1258 |
mFetchChangedGeomIt = mChangedGeometries.begin(); |
1194 | 1259 |
} |
1195 | 1260 | |
1261 |
QgsAttributeList attrToSelect; |
|
1262 | ||
1196 | 1263 |
//look in the normal features of the provider |
1197 | 1264 |
if ( mFetchAttributes.size() > 0 ) |
1198 | 1265 |
{ |
... | ... | |
1208 | 1275 |
provAttributes << *it; |
1209 | 1276 |
} |
1210 | 1277 | |
1211 |
mDataProvider->select( provAttributes, rect, fetchGeometries, useIntersect );
|
|
1278 |
attrToSelect=provAttributes;
|
|
1212 | 1279 |
} |
1213 | 1280 |
else |
1214 |
mDataProvider->select( mFetchAttributes, rect, fetchGeometries, useIntersect );
|
|
1281 |
attrToSelect=mFetchAttributes;
|
|
1215 | 1282 |
} |
1216 | 1283 |
else |
1217 | 1284 |
{ |
1218 |
mDataProvider->select( QgsAttributeList(), rect, fetchGeometries, useIntersect );
|
|
1285 |
attrToSelect=QgsAttributeList();
|
|
1219 | 1286 |
} |
1287 |
|
|
1288 |
bool cacheSelectSuccessful; |
|
1289 |
if ( rect == QgsRectangle() ) |
|
1290 |
{ |
|
1291 |
//we can't give QgsRectangle() to feature cache's select(), as it would only select |
|
1292 |
//extent of cache - instead of extent of the real data |
|
1293 |
cacheSelectSuccessful=mFeatureCache->select( |
|
1294 |
attrToSelect, mDataProvider->extent(), fetchGeometries, useIntersect |
|
1295 |
); |
|
1296 |
} |
|
1297 |
else |
|
1298 |
{ |
|
1299 |
cacheSelectSuccessful=mFeatureCache->select( |
|
1300 |
attrToSelect, rect, fetchGeometries, useIntersect |
|
1301 |
); |
|
1302 |
} |
|
1303 |
|
|
1304 |
if ( ! cacheSelectSuccessful ) |
|
1305 |
mDataProvider->select( attrToSelect, rect, fetchGeometries, useIntersect ); |
|
1220 | 1306 |
} |
1221 | 1307 | |
1222 | 1308 |
bool QgsVectorLayer::nextFeature( QgsFeature &f ) |
1223 | 1309 |
{ |
1224 |
if ( !mFetching ) |
|
1310 |
if ( !mFetching && !mFeatureCache->isSelectValid() )
|
|
1225 | 1311 |
return false; |
1226 | 1312 | |
1227 | 1313 |
if ( mEditable ) |
... | ... | |
1320 | 1406 |
// no more added features |
1321 | 1407 |
} |
1322 | 1408 | |
1323 |
while ( dataProvider()->nextFeature( f ) )
|
|
1409 |
while ( true )
|
|
1324 | 1410 |
{ |
1411 |
if ( mFeatureCache->isSelectValid() ) { |
|
1412 |
if ( !mFeatureCache->nextFeature ( f ) ) |
|
1413 |
break; |
|
1414 |
} |
|
1415 |
else |
|
1416 |
{ |
|
1417 |
if ( !dataProvider()->nextFeature( f ) ) |
|
1418 |
break; |
|
1419 |
} |
|
1420 | ||
1325 | 1421 |
if ( mFetchConsidered.contains( f.id() ) ) |
1326 | 1422 |
continue; |
1327 | 1423 | |
... | ... | |
1400 | 1496 |
} |
1401 | 1497 |
} |
1402 | 1498 | |
1499 |
bool gotFeature; |
|
1500 |
|
|
1403 | 1501 |
// regular features |
1404 | 1502 |
if ( fetchAttributes ) |
1405 | 1503 |
{ |
1406 |
if ( mDataProvider->featureAtId( featureId, f, fetchGeometries, mDataProvider->attributeIndexes() ) ) |
|
1504 |
if ( !( |
|
1505 |
mFeatureCache->isInitialized() && |
|
1506 |
mFeatureCache->areAttributesCached( mDataProvider->attributeIndexes() ) && |
|
1507 |
mFeatureCache->featureAtId( featureId, f, fetchGeometries, true ) |
|
1508 |
) ) |
|
1407 | 1509 |
{ |
1510 |
//feature not in cache, try the provider |
|
1511 |
gotFeature=mDataProvider->featureAtId( featureId, f, fetchGeometries, mDataProvider->attributeIndexes() ); |
|
1512 |
} |
|
1513 |
else |
|
1514 |
{ |
|
1515 |
gotFeature=true; |
|
1516 |
} |
|
1517 |
|
|
1518 |
if ( gotFeature ) |
|
1519 |
{ |
|
1408 | 1520 |
updateFeatureAttributes( f ); |
1409 | 1521 |
return true; |
1410 | 1522 |
} |
1411 | 1523 |
} |
1412 | 1524 |
else |
1413 | 1525 |
{ |
1414 |
if ( mDataProvider->featureAtId( featureId, f, fetchGeometries, QgsAttributeList() ) )
|
|
1526 |
if ( !( mFeatureCache->isInitialized() && mFeatureCache->featureAtId( featureId, f, fetchGeometries, false ) ) )
|
|
1415 | 1527 |
{ |
1528 |
//feature not in cache, try the provider |
|
1529 |
gotFeature=mDataProvider->featureAtId( featureId, f, fetchGeometries, QgsAttributeList() ); |
|
1530 |
} |
|
1531 |
else |
|
1532 |
{ |
|
1533 |
gotFeature=true; |
|
1534 |
} |
|
1535 |
|
|
1536 |
if ( gotFeature ) |
|
1537 |
{ |
|
1416 | 1538 |
return true; |
1417 | 1539 |
} |
1418 | 1540 |
} |
... | ... | |
1448 | 1570 |
// and add to the known added features. |
1449 | 1571 |
f.setFeatureId( addedIdLowWaterMark ); |
1450 | 1572 |
editFeatureAdd( f ); |
1573 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1451 | 1574 |
mCachedGeometries[f.id()] = *f.geometry(); |
1575 |
#endif |
|
1452 | 1576 | |
1453 | 1577 |
setModified( true ); |
1454 | 1578 | |
... | ... | |
1471 | 1595 |
if ( mDataProvider ) |
1472 | 1596 |
{ |
1473 | 1597 |
QgsGeometry geometry; |
1598 | ||
1474 | 1599 |
if ( !mChangedGeometries.contains( atFeatureId ) ) |
1475 | 1600 |
{ |
1476 | 1601 |
// first time this geometry has changed since last commit |
1602 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1477 | 1603 |
if ( !mCachedGeometries.contains( atFeatureId ) ) |
1478 | 1604 |
{ |
1479 | 1605 |
return false; |
1480 | 1606 |
} |
1481 | 1607 |
geometry = mCachedGeometries[atFeatureId]; |
1482 | 1608 |
//mChangedGeometries[atFeatureId] = mCachedGeometries[atFeatureId]; |
1609 |
#else |
|
1610 |
QgsFeature f; |
|
1611 | ||
1612 |
if ( !featureAtId( atFeatureId, f, true, false ) ) |
|
1613 |
{ |
|
1614 |
return false; |
|
1615 |
} |
|
1616 |
geometry=*(f.geometry()); |
|
1617 |
#endif |
|
1483 | 1618 |
} |
1484 | 1619 |
else |
1485 | 1620 |
{ |
1486 | 1621 |
geometry = mChangedGeometries[atFeatureId]; |
1487 | 1622 |
} |
1488 | 1623 |
geometry.insertVertex( x, y, beforeVertex ); |
1624 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1489 | 1625 |
mCachedGeometries[atFeatureId] = geometry; |
1626 |
#endif |
|
1490 | 1627 |
editGeometryChange( atFeatureId, geometry ); |
1491 | 1628 | |
1492 | 1629 |
setModified( true, true ); // only geometry was changed |
... | ... | |
1510 | 1647 |
if ( !mChangedGeometries.contains( atFeatureId ) ) |
1511 | 1648 |
{ |
1512 | 1649 |
// first time this geometry has changed since last commit |
1650 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1513 | 1651 |
if ( !mCachedGeometries.contains( atFeatureId ) ) |
1514 | 1652 |
{ |
1515 | 1653 |
return false; |
1516 | 1654 |
} |
1517 | 1655 |
geometry = mCachedGeometries[atFeatureId]; |
1518 | 1656 |
//mChangedGeometries[atFeatureId] = mCachedGeometries[atFeatureId]; |
1657 |
#else |
|
1658 |
QgsFeature f; |
|
1659 | ||
1660 |
if ( !featureAtId( atFeatureId, f, true, false ) ) |
|
1661 |
{ |
|
1662 |
return false; |
|
1663 |
} |
|
1664 |
geometry=*(f.geometry()); |
|
1665 |
#endif |
|
1519 | 1666 |
} |
1520 | 1667 |
else |
1521 | 1668 |
{ |
... | ... | |
1523 | 1670 |
} |
1524 | 1671 | |
1525 | 1672 |
geometry.moveVertex( x, y, atVertex ); |
1673 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1526 | 1674 |
mCachedGeometries[atFeatureId] = geometry; |
1675 |
#endif |
|
1527 | 1676 |
editGeometryChange( atFeatureId, geometry ); |
1528 | 1677 | |
1529 | 1678 |
setModified( true, true ); // only geometry was changed |
... | ... | |
1547 | 1696 |
if ( !mChangedGeometries.contains( atFeatureId ) ) |
1548 | 1697 |
{ |
1549 | 1698 |
// first time this geometry has changed since last commit |
1699 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1550 | 1700 |
if ( !mCachedGeometries.contains( atFeatureId ) ) |
1551 | 1701 |
{ |
1552 | 1702 |
return false; |
1553 | 1703 |
} |
1554 | 1704 |
geometry = mCachedGeometries[atFeatureId]; |
1705 |
#else |
|
1706 |
QgsFeature f; |
|
1707 | ||
1708 |
if ( !featureAtId( atFeatureId, f, true, false ) ) |
|
1709 |
{ |
|
1710 |
return false; |
|
1711 |
} |
|
1712 |
geometry=*(f.geometry()); |
|
1713 |
#endif |
|
1555 | 1714 |
} |
1556 | 1715 |
else |
1557 | 1716 |
{ |
... | ... | |
1559 | 1718 |
} |
1560 | 1719 | |
1561 | 1720 |
geometry.deleteVertex( atVertex ); |
1721 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1562 | 1722 |
mCachedGeometries[atFeatureId] = geometry; |
1723 |
#endif |
|
1563 | 1724 |
editGeometryChange( atFeatureId, geometry ); |
1564 | 1725 | |
1565 | 1726 |
setModified( true, true ); // only geometry was changed |
... | ... | |
1657 | 1818 |
QgsGeometry geom = *changedIt; |
1658 | 1819 |
int returnValue = geom.addIsland( ring ); |
1659 | 1820 |
editGeometryChange( selectedFeatureId, geom ); |
1821 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1660 | 1822 |
mCachedGeometries[selectedFeatureId] = geom; |
1823 |
#endif |
|
1661 | 1824 |
return returnValue; |
1662 | 1825 |
} |
1663 | 1826 | |
1664 | 1827 |
//look if id of selected feature belongs to an added feature |
1665 |
/* |
|
1666 | 1828 |
for ( QgsFeatureList::iterator addedIt = mAddedFeatures.begin(); addedIt != mAddedFeatures.end(); ++addedIt ) |
1667 | 1829 |
{ |
1668 | 1830 |
if ( addedIt->id() == selectedFeatureId ) |
1669 | 1831 |
{ |
1670 |
return addedIt->geometry()->addIsland( ring ); |
|
1671 |
mCachedGeometries[selectedFeatureId] = *addedIt->geometry(); |
|
1832 |
QgsGeometry translateGeom( *( addedIt->geometry() ) ); |
|
1833 |
|
|
1834 |
int errorCode = translateGeom.addIsland( ring ); |
|
1835 |
if ( errorCode == 0 ) |
|
1836 |
{ |
|
1837 |
editGeometryChange( selectedFeatureId, translateGeom ); |
|
1838 |
setModified( true, true ); |
|
1839 |
} |
|
1840 |
|
|
1841 |
return errorCode; |
|
1672 | 1842 |
} |
1673 | 1843 |
} |
1674 |
*/ |
|
1675 | 1844 | |
1845 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1676 | 1846 |
//is the feature contained in the view extent (mCachedGeometries) ? |
1677 | 1847 |
QgsGeometryMap::iterator cachedIt = mCachedGeometries.find( selectedFeatureId ); |
1678 | 1848 |
if ( cachedIt != mCachedGeometries.end() ) |
... | ... | |
1688 | 1858 |
} |
1689 | 1859 |
else //maybe the selected feature has been moved outside the visible area and therefore is not contained in mCachedGeometries |
1690 | 1860 |
{ |
1861 |
#endif |
|
1691 | 1862 |
QgsFeature f; |
1692 | 1863 |
QgsGeometry* fGeom = 0; |
1693 | 1864 |
if ( featureAtId( selectedFeatureId, f, true, false ) ) |
1694 | 1865 |
{ |
1695 | 1866 |
fGeom = f.geometryAndOwnership(); |
1867 | ||
1696 | 1868 |
if ( fGeom ) |
1697 | 1869 |
{ |
1698 | 1870 |
int errorCode = fGeom->addIsland( ring ); |
1699 | 1871 |
editGeometryChange( selectedFeatureId, *fGeom ); |
1700 | 1872 |
setModified( true, true ); |
1701 | 1873 |
delete fGeom; |
1874 |
|
|
1702 | 1875 |
return errorCode; |
1703 | 1876 |
} |
1704 | 1877 |
} |
1878 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1705 | 1879 |
} |
1880 |
#endif |
|
1706 | 1881 | |
1707 | 1882 |
return 6; //geometry not found |
1708 | 1883 |
} |
... | ... | |
1720 | 1895 |
} |
1721 | 1896 | |
1722 | 1897 |
//look if id of selected feature belongs to an added feature |
1723 |
/* |
|
1724 | 1898 |
for ( QgsFeatureList::iterator addedIt = mAddedFeatures.begin(); addedIt != mAddedFeatures.end(); ++addedIt ) |
1725 | 1899 |
{ |
1726 | 1900 |
if ( addedIt->id() == featureId ) |
1727 | 1901 |
{ |
1728 |
return addedIt->geometry()->translate( dx, dy ); |
|
1902 |
QgsGeometry translateGeom( *( addedIt->geometry() ) ); |
|
1903 |
|
|
1904 |
int errorCode = translateGeom.translate( dx, dy ); |
|
1905 |
if ( errorCode == 0 ) |
|
1906 |
{ |
|
1907 |
editGeometryChange( featureId, translateGeom ); |
|
1908 |
setModified( true, true ); |
|
1909 |
} |
|
1910 |
|
|
1911 |
return errorCode; |
|
1729 | 1912 |
} |
1730 | 1913 |
} |
1731 |
*/ |
|
1732 | 1914 | |
1915 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1733 | 1916 |
//else look in mCachedGeometries to make access faster |
1734 | 1917 |
QgsGeometryMap::iterator cachedIt = mCachedGeometries.find( featureId ); |
1735 | 1918 |
if ( cachedIt != mCachedGeometries.end() ) |
... | ... | |
1742 | 1925 |
} |
1743 | 1926 |
return errorCode; |
1744 | 1927 |
} |
1928 |
#endif |
|
1745 | 1929 | |
1746 | 1930 |
//else get the geometry from provider (may be slow) |
1747 | 1931 |
QgsFeature f; |
... | ... | |
1827 | 2011 |
{ |
1828 | 2012 |
//change this geometry |
1829 | 2013 |
editGeometryChange( select_it->id(), *( select_it->geometry() ) ); |
2014 |
#ifdef ENABLE_GEOMETRYCACHE |
|
1830 | 2015 |
//update of cached geometries is necessary because we use addTopologicalPoints() later |
1831 | 2016 |
mCachedGeometries[select_it->id()] = *( select_it->geometry() ); |
2017 |
#endif |
|
1832 | 2018 | |
1833 | 2019 |
//insert new features |
1834 | 2020 |
for ( int i = 0; i < newGeometries.size(); ++i ) |
... | ... | |
2621 | 2807 |
} |
2622 | 2808 | |
2623 | 2809 |
editGeometryChange( fid, *geom ); |
2810 |
#ifdef ENABLE_GEOMETRYCACHE |
|
2624 | 2811 |
mCachedGeometries[fid] = *geom; |
2812 |
#endif |
|
2813 | ||
2625 | 2814 |
setModified( true, true ); |
2626 | 2815 |
return true; |
2627 | 2816 |
} |
... | ... | |
3030 | 3219 |
} |
3031 | 3220 |
} |
3032 | 3221 | |
3222 |
#ifdef ENABLE_GEOMETRYCACHE |
|
3033 | 3223 |
deleteCachedGeometries(); |
3224 |
#endif |
|
3034 | 3225 | |
3035 | 3226 |
if ( success ) |
3036 | 3227 |
{ |
... | ... | |
3040 | 3231 |
mUpdatedFields.clear(); |
3041 | 3232 |
mMaxUpdatedIndex = -1; |
3042 | 3233 |
undoStack()->clear(); |
3234 |
mFeatureCache->clear(); |
|
3235 | ||
3043 | 3236 |
emit editingStopped(); |
3044 | 3237 |
} |
3045 | 3238 | |
... | ... | |
3100 | 3293 |
mMaxUpdatedIndex = -1; |
3101 | 3294 |
} |
3102 | 3295 | |
3296 |
#ifdef ENABLE_GEOMETRYCACHE |
|
3103 | 3297 |
deleteCachedGeometries(); |
3298 |
#endif |
|
3104 | 3299 | |
3300 |
//TODO: is this needed here? I think not |
|
3301 |
//mFeatureCache->clear(); |
|
3302 |
|
|
3105 | 3303 |
mEditable = false; |
3106 | 3304 |
emit editingStopped(); |
3107 | 3305 | |
... | ... | |
3311 | 3509 |
{ |
3312 | 3510 |
return 1; |
3313 | 3511 |
} |
3314 | ||
3512 |
|
|
3315 | 3513 |
QList<QgsFeature> featureList; |
3316 | 3514 |
QgsRectangle searchRect( startPoint.x() - snappingTolerance, startPoint.y() - snappingTolerance, |
3317 | 3515 |
startPoint.x() + snappingTolerance, startPoint.y() + snappingTolerance ); |
... | ... | |
3320 | 3518 |
int n = 0; |
3321 | 3519 |
QgsFeature f; |
3322 | 3520 | |
3521 |
#ifdef ENABLE_GEOMETRYCACHE |
|
3323 | 3522 |
if ( mCachedGeometriesRect.contains( searchRect ) ) |
3324 | 3523 |
{ |
3325 | 3524 |
QgsDebugMsg( "Using cached geometries for snapping." ); |
... | ... | |
3338 | 3537 |
else |
3339 | 3538 |
{ |
3340 | 3539 |
// snapping outside cached area |
3540 |
#endif |
|
3341 | 3541 | |
3342 | 3542 |
select( QgsAttributeList(), searchRect, true, true ); |
3343 | ||
3543 |
|
|
3544 |
if ( !mFeatureCache->isSelectValid() && ( mDataProvider->extent() == mFeatureCache->getCachedRectangle() ) ) |
|
3545 |
{ |
|
3546 |
//everything is cached already, what ain't in cache, ain't nowhere in layer :-) |
|
3547 |
mFeatureCache->makeSelectValid(); |
|
3548 |
} |
|
3549 |
|
|
3344 | 3550 |
while ( nextFeature( f ) ) |
3345 | 3551 |
{ |
3346 | 3552 |
snapToGeometry( startPoint, f.id(), f.geometry(), sqrSnappingTolerance, snappingResults, snap_to ); |
3347 | 3553 |
++n; |
3348 | 3554 |
} |
3555 |
#ifdef ENABLE_GEOMETRYCACHE |
|
3349 | 3556 |
} |
3557 |
#endif |
|
3350 | 3558 | |
3351 | 3559 |
return n == 0 ? 2 : 0; |
3352 | 3560 |
} |
... | ... | |
4077 | 4285 |
} |
4078 | 4286 |
else |
4079 | 4287 |
{ |
4080 |
// added feature TODO:
|
|
4288 |
// added feature |
|
4081 | 4289 |
for ( int i = 0; i < mAddedFeatures.size(); i++ ) |
4082 | 4290 |
{ |
4083 | 4291 |
if ( mAddedFeatures[i].id() == fid ) |
... | ... | |
4096 | 4304 |
// it's not ideal to trigger refresh from here |
4097 | 4305 |
triggerRepaint(); |
4098 | 4306 |
} |
4307 | ||
4308 |
void QgsVectorLayer::recacheFeature( int featureId, QgsRectangle rectFrom, QgsRectangle rectTo ) |
|
4309 |
{ |
|
4310 |
if ( rectTo==QgsRectangle() ) |
|
4311 |
rectTo=mDataProvider->extent(); |
|
4312 | ||
4313 |
QgsRectangle cachedRect=mFeatureCache->getCachedRectangle(); |
|
4314 | ||
4315 |
rectFrom=rectFrom.intersect( &cachedRect ); |
|
4316 |
rectTo=rectTo.intersect( &cachedRect ); |
|
4317 | ||
4318 |
mFeatureCache->remove( featureId, rectFrom ); |
|
4319 | ||
4320 |
//now reinsert the feature from provider |
|
4321 |
if ( mDataProvider->capabilities() & QgsVectorDataProvider::SelectGeometryAtId ) |
|
4322 |
{ |
|
4323 |
//either using existing method |
|
4324 |
QgsFeature fet; |
|
4325 |
|
|
4326 |
mDataProvider->featureAtId( featureId, fet, true, mRenderer->classificationAttributes() ); |
|
4327 |
mFeatureCache->insert( fet, false ); |
|
4328 |
} |
|
4329 |
else |
|
4330 |
{ |
|
4331 |
//or fallback to search for feature id in specified rectangle |
|
4332 |
mDataProvider->select( mRenderer->classificationAttributes(), rectTo ); |
|
4333 |
|
|
4334 |
QgsFeature fet; |
|
4335 |
while ( mDataProvider->nextFeature( fet ) ) |
|
4336 |
{ |
|
4337 |
if ( fet.id() == featureId ) |
|
4338 |
{ |
|
4339 |
mFeatureCache->insert( fet, false ); |
|
4340 |
mDataProvider->rewind(); |
|
4341 |
break; |
|
4342 |
} |
|
4343 |
} |
|
4344 |
} |
|
4345 |
} |
|
4346 | ||
4347 |
void QgsVectorLayer::recacheFeatures( QgsRectangle rect ) |
|
4348 |
{ |
|
4349 |
QgsRectangle cachedRect=mFeatureCache->getCachedRectangle(); |
|
4350 |
rect=rect.intersect( &cachedRect ); |
|
4351 | ||
4352 |
mFeatureCache->remove( rect ); |
|
4353 |
|
|
4354 |
mDataProvider->select( mRenderer->classificationAttributes(), rect ); |
|
4355 |
|
|
4356 |
QgsFeature fet; |
|
4357 |
while ( mDataProvider->nextFeature( fet ) ) |
|
4358 |
{ |
|
4359 |
mFeatureCache->insert( fet, false ); |
|
4360 |
} |
|
4361 |
} |
src/core/qgsfeaturecache.h (revision 0) | ||
---|---|---|
1 |
/*************************************************************************** |
|
2 |
qgsfeaturecache.h - Spatial Feature Cache Class |
|
3 |
-------------------------------------- |
|
4 |
Date : 19-Jun-2009 |
|
5 |
Copyright : (C) 2009 by Andrej Krutak |
|
6 |
email : andree at andree.sk |
|
7 |
*************************************************************************** |
|
8 |
* * |
|
9 |
* This program is free software; you can redistribute it and/or modify * |
|
10 |
* it under the terms of the GNU General Public License as published by * |
|
11 |
* the Free Software Foundation; either version 2 of the License, or * |
|
12 |
* (at your option) any later version. * |
|
13 |
* * |
|
14 |
***************************************************************************/ |
|
15 | ||
16 |
#ifndef QGSFEATURECACHE_H |
|
17 |
#define QGSFEATURECACHE_H |
|
18 | ||
19 |
#include <QHash> |
|
20 |
#include <QLinkedList> |
|
21 |
#include "qgsfeature.h" |
|
22 |
#include "qgis.h" |
|
23 |
#include "qgsrectangle.h" |
|
24 |
#include "qgslabel.h" |
|
25 |
#include "qgsspatialindex.h" |
|
26 | ||
27 |
/** |
|
28 |
* A feature cache class. Stores inserted features in memory and enables |
|
29 |
* efficient selects and feature retrievals. |
|
30 |
* |
|
31 |
* Usage of the class: |
|
32 |
* |
|
33 |
* After creating the class object, the cache is empty. To fill, one first |
|
34 |
* has to specify, what region will be filled - this is done by calling |
|
35 |
* select() method, where one specifies rectangle and attributes to be cached. |
|
36 |
* The method will obviously fail, because no data are cached yet. |
|
37 |
* (Also, until the cache is filled, isInitialized() will return false.) |
|
38 |
* |
|
39 |
* After that, insert(feature, true) method has to be called to efficiently |
|
40 |
* insert contained features. After all features are inserted, setAsInitialized() |
|
41 |
* has to be called. This method efficiently commits all inserted features into |
|
42 |
* the cache. After this, the cache is initialized and all subsequent calls |
|
43 |
* to select(), using subset of previously specified rectangle and attributes, |
|
44 |
* will be successful. Features may then be retrieved by repetetive calls |
|
45 |
* of nextFeature(). |
|
46 |
* |
|
47 |
* One might want to retrieve features even if the requested rectangle doesn't |
|
48 |
* completely fit into cached area. In case the requested attributes are |
|
49 |
* a subset of cached ones, this is possible - just call makeSelectValid() |
|
50 |
* after select(). By calling this, it's possible to retrieve even partial |
|
51 |
* cache matches. |
|
52 |
* |
|
53 |
* To modify existing cache data, one may use insert(feature, false), and |
|
54 |
* remove() methods, which to immediate changes to the cached data. Note that |
|
55 |
* calling these methods for large ammounts of features may be quite slow - |
|
56 |
* in case a big part of tree has to be 'edited', it might be much faster |
|
57 |
* to clear() the cache and refill it again using previously mentioned method. |
|
58 |
* |
|
59 |
* There are several cache modes, that change the way cache keeps data stored. |
|
60 |
* It's possible to set new mode using setCacheMode(). To apply the mode |
|
61 |
* for a specified select, applyCurrentCacheMode() has to be called. The method |
|
62 |
* tells cache, what rectangle is currently required by user - according to this |
|
63 |
* the cache will determine, what data to discard (if any). |
|
64 |
**/ |
|
65 |
class CORE_EXPORT QgsFeatureCache |
|
66 |
{ |
|
67 |
public: |
|
68 |
QgsFeatureCache(); |
|
69 |
~QgsFeatureCache(); |
|
70 | ||
71 |
/** |
|
72 |
* Inserts the specified feature to cache. |
|
73 |
* @param f feature to be inserted |
|
74 |
* @param deferredInsert if true, feature will only be inserted after calling |
|
75 |
* setAsInitialized(); otherwise the feature is inserted |
|
76 |
* immediately (which is pretty costly in case of inserting |
|
77 |
* more than a few features) |
|
78 |
* @returns whether the operation was successful. |
|
79 |
**/ |
|
80 |
bool insert( QgsFeature &f, bool deferredInsert=true ); |
|
81 | ||
82 |
/** |
|
83 |
* Remove feature with specified id. If rect is specified, it will be |
|
84 |
* used as hint to find the feature (it could be e.g. feture's bounding rect.) |
|
85 |
**/ |
|
86 |
bool remove( int featureId, QgsRectangle rect = QgsRectangle() ); |
|
87 |
|
|
88 |
/** |
|
89 |
* Remove features intersecting specified rectangle. |
|
90 |
**/ |
|
91 |
bool remove( QgsRectangle rect ); |
|
92 | ||
93 |
/** |
|
94 |
* Returns, whether the cache is properly initialized (and thus the |
|
95 |
* nextFeature() and featureAtId() are usable) |
|
96 |
**/ |
|
97 |
inline bool isInitialized() { return mIsInitialized; } |
|
98 |
|
|
99 |
/** |
|
100 |
* Returns, whether the last called select() left the |
|
101 |
* cache in state valid for iteration using nextFeature() |
|
102 |
**/ |
|
103 |
inline bool isSelectValid() { return mSelectValid; } |
|
104 | ||
105 |
/** |
|
106 |
* Sets the cache as valid; to be called after it's filled with |
|
107 |
* features (@see insertFeature()) and only when the cache was empty before. |
|
108 |
* The cache parameters (contained rectangle, attributes) are set to current |
|
109 |
* select parameters. |
|
110 |
**/ |
|
111 |
void setAsInitialized(); |
|
112 | ||
113 |
/** Clear the cache; also sets it as not valid */ |
|
114 |
void clear(); |
|
115 |
|
|
116 |
/** |
|
117 |
* Select features; returns whether the cache is valid for the specified select |
|
118 |
* @param fetchAttributes attributes to fetch |
|
119 |
* @param rect rectangle to fetch the features from. If rect==QgsRectangle(), |
|
120 |
* all features in cache are selected. |
|
121 |
* @param fetchGeometry also fetch geometries |
|
122 |
* @param useIntersect only retrieve features that really (not only their bounding rect) intersect with rectangle |
|
123 |
* @returns True, if select was successful (i.e. nextFeature() will return data). |
|
124 |
**/ |
|
125 |
bool select( QgsAttributeList fetchAttributes, |
|
126 |
QgsRectangle rect = QgsRectangle(), |
|
127 |
bool fetchGeometry = true, |
|
128 |
bool useIntersect = false); |
|
129 | ||
130 |
/** |
|
131 |
* Make the last selected features fetchable using nextFeature(), if at least selected |
|
132 |
* attributes are subset of the the cached ones. |
|
133 |
**/ |
|
134 |
void makeSelectValid(); |
|
135 | ||
136 |
/** Retrieve next feature from the select; Returns false if there are no more features. */ |
|
137 |
bool nextFeature( QgsFeature& feature ); |
|
138 | ||
139 |
/** Returns the feature with specified Id. */ |
|
140 |
bool featureAtId( int featureId, QgsFeature &f, bool fetchGeometries = true, bool fetchAttributes = true ); |
|
141 |
|
|
142 |
/** cache modes */ |
|
143 |
enum CacheMode { |
|
144 |
///store all inserted features |
|
145 |
CACHE_ALL=0, |
|
146 |
|
|
147 |
/** |
|
148 |
* Store features and use heuristics to crop the stored rectangle size at |
|
149 |
* selects - and so optimize size of occupied memory. |
|
150 |
**/ |
|
151 |
CACHE_HEURISTICS, |
|
152 |
|
|
153 |
///don't store any features |
|
154 |
CACHE_NOTHING, |
|
155 |
}; |
|
156 | ||
157 |
/** Sets cache mode. */ |
|
158 |
void setCacheMode(CacheMode mode); |
|
159 | ||
160 |
/** Apply current cache mode to currently cached data (using specified rectangle). */ |
|
161 |
void applyCurrentCacheMode(QgsRectangle const &rect); |
|
162 |
|
|
163 |
/** Returns cached rectangle. This is the biggest selectable rectangle. */ |
|
164 |
QgsRectangle getCachedRectangle() { return mCachedRectangle; } |
|
165 |
|
|
166 |
/** Returns true, if cached attributes are superset of specified attributes list */ |
|
167 |
bool areAttributesCached(QgsAttributeList list); |
|
168 |
|
|
169 |
private: |
|
170 |
/** Filter out cached data outside the specified rectangle */ |
|
171 |
void filterTo(QgsRectangle const &r); |
|
172 |
|
|
173 |
/** Contains, whether the cache has been filled for the current select */ |
|
174 |
bool mIsInitialized; |
|
175 | ||
176 |
/** Holds cached features */ |
|
177 |
QgsSpatialIndex *mCachedFeatures; |
|
178 | ||
179 |
/** Contains features waiting to be really inserted into tree by setAsInitialized() */ |
|
180 |
QLinkedList<QgsFeature*> mCachedFeaturesList; |
|
181 |
|
|
182 |
/** Rectangle that is currently cached */ |
|
183 |
QgsRectangle mCachedRectangle; |
|
184 | ||
185 |
/** Contains attribute list, that are stored in cached features */ |
|
186 |
QgsAttributeList mCachedAttributes; |
|
187 | ||
188 |
/** Holds, whether geometries are also cached (alongside attributes) */ |
|
189 |
bool mCachedIncludingGeometries; |
|
190 | ||
191 |
/** Holds, whether the current select is valid */ |
|
192 |
bool mSelectValid; |
|
193 |
|
|
194 |
/** Select parameters - attributes */ |
|
195 |
QgsAttributeList mSelectedAttributes; |
|
196 | ||
197 |
/** Select parameters - rectangle */ |
|
198 |
QgsRectangle mSelectedRectangle;; |
|
199 | ||
200 |
/** Select parameters - fetch geometries?*/ |
|
201 |
bool mSelectedFetchGeometries; |
|
202 | ||
203 |
/** Select parameters - use intersect?*/ |
|
204 |
bool mSelectedUseIntersect; |
|
205 | ||
206 |
/** List of selected features */ |
|
207 |
QLinkedList<QgsFeature*> mSelectedFeaturesList; |
|
208 | ||
209 |
/** Select parameters - iterator in the mCachedFeatures */ |
|
210 |
QLinkedList<QgsFeature*>::iterator mSelectedFeaturesIterator; |
|
211 |
|
|
212 |
CacheMode mCacheMode; |
|
213 |
}; |
|
214 | ||
215 |
#endif //QGSFEATURECACHE_H |
src/core/qgsvectorlayer.h (working copy) | ||
---|---|---|
44 | 44 |
class QgsUndoCommand; |
45 | 45 |
class QgsVectorDataProvider; |
46 | 46 |
class QgsVectorOverlay; |
47 |
class QgsFeatureCache; |
|
47 | 48 | |
48 | 49 |
class QgsRectangle; |
49 | 50 | |
... | ... | |
511 | 512 |
*/ |
512 | 513 |
virtual void updateExtents(); |
513 | 514 | |
515 |
/** |
|
516 |
* Fetch the specified feature from provider, if it is cached. This |
|
517 |
* must be called every time the provider data change outside vector layer control. |
|
518 |
* @param featureId id of feature that should be reloaded |
|
519 |
* @param rectFrom hint of rectangle, where the feature resided. If not provided, |
|
520 |
* whole cache will be searched for the id. |
|
521 |
* @param rectTo hint of rectangle, where the feature resides now. If not provided, |
|
522 |
* all provider features will be searched for the id. |
|
523 |
**/ |
|
524 |
void recacheFeature( int featureId, QgsRectangle rectFrom=QgsRectangle(), QgsRectangle rectTo=QgsRectangle() ); |
|
525 |
|
|
526 |
/** |
|
527 |
* Fetch features from specified rectangle from provider, if it is cached. This |
|
528 |
* must be called every time the provider data change outside vector layer control. |
|
529 |
* @param rect rectangle containing changed features. All features within |
|
530 |
* the rectangle will be reloaded. |
|
531 |
**/ |
|
532 |
void recacheFeatures( QgsRectangle rect ); |
|
533 |
|
|
514 | 534 |
signals: |
515 | 535 | |
516 | 536 |
/** This signal is emited when selection was changed */ |
... | ... | |
589 | 609 |
/** Goes through all features and finds a free id (e.g. to give it temporarily to a not-commited feature) */ |
590 | 610 |
int findFreeId(); |
591 | 611 | |
612 |
#ifdef ENABLE_GEOMETRYCACHE |
|
592 | 613 |
/**Deletes the geometries in mCachedGeometries*/ |
593 | 614 |
void deleteCachedGeometries(); |
615 |
#endif |
|
594 | 616 | |
595 | 617 |
/** Draws a vertex symbol at (screen) coordinates x, y. (Useful to assist vertex editing.) */ |
596 | 618 |
void drawVertexMarker( int x, int y, QPainter& p, QgsVectorLayer::VertexMarkerType type ); |
... | ... | |
656 | 678 |
/** Flag indicating whether the layer has been modified since the last commit */ |
657 | 679 |
bool mModified; |
658 | 680 | |
681 |
#ifdef ENABLE_GEOMETRYCACHE |
|
659 | 682 |
/** cache of the committed geometries retrieved *for the current display* */ |
660 | 683 |
QgsGeometryMap mCachedGeometries; |
661 | 684 | |
662 | 685 |
/** extent for which there are cached geometries */ |
663 | 686 |
QgsRectangle mCachedGeometriesRect; |
687 |
#endif |
|
664 | 688 | |
689 |
/** Cache of features (used mainly for displaying of features) */ |
|
690 |
QgsFeatureCache *mFeatureCache; |
|
691 | ||
665 | 692 |
/** Set holding the feature IDs that are activated. Note that if a feature |
666 | 693 |
subsequently gets deleted (i.e. by its addition to mDeletedFeatureIds), |
667 | 694 |
it always needs to be removed from mSelectedFeatureIds as well. |
src/core/spatialindex/tools/ExternalSort.h (working copy) | ||
---|---|---|
73 | 73 |
void initializeRuns( std::deque<SmartPointer<TemporaryFile> >& runs ); |
74 | 74 |
void mergeRuns(); |
75 | 75 | |
76 |
#ifndef EXTERNALSORT_SORTUSINGPRIORITYQUEUE |
|
77 |
std::vector<PQEntry*> m_buffer; |
|
78 |
#else |
|
76 | 79 |
std::priority_queue < |
77 | 80 |
PQEntry*, |
78 | 81 |
std::vector<PQEntry*>, |
79 | 82 |
PQEntry::ascendingComparator > m_buffer; |
83 |
#endif |
|
80 | 84 | |
81 | 85 |
unsigned long m_cMaxBufferSize; |
82 | 86 |
bool m_bFitsInBuffer; |
src/core/spatialindex/tools/ExternalSort.cc (working copy) | ||
---|---|---|
27 | 27 |
#include "ExternalSort.h" |
28 | 28 |
#include "qgslogger.h" |
29 | 29 | |
30 |
#include <algorithm> |
|
31 | ||
30 | 32 |
#ifdef _MSC_VER |
31 | 33 |
#define UNUSED(symbol) symbol |
32 | 34 |
#else |
... | ... | |
153 | 155 |
m_pTemplateRecord = o->clone(); |
154 | 156 | |
155 | 157 |
SmartPointer<TemporaryFile> tf; |
158 |
#ifdef EXTERNALSORT_SORTUSINGPRIORITYQUEUE |
|
156 | 159 |
m_buffer.push( new PQEntry( pS, m_pComparator, tf ) ); |
160 |
#else |
|
161 |
m_buffer.push_back( new PQEntry( pS, m_pComparator, tf ) ); |
|
162 |
#endif |
|
157 | 163 |
} |
158 | 164 | |
159 | 165 |
if ( bEOF && runs.size() == 0 ) |
... | ... | |
161 | 167 | |
162 | 168 |
if ( ! m_buffer.empty() ) |
163 | 169 |
{ |
170 |
#ifndef EXTERNALSORT_SORTUSINGPRIORITYQUEUE |
|
171 |
std::sort(m_buffer.begin(), m_buffer.end()); |
|
172 |
#endif |
|
173 | ||
164 | 174 |
TemporaryFile* tf = new TemporaryFile(); |
165 | 175 |
while ( ! m_buffer.empty() ) |
166 | 176 |
{ |
177 |
#ifdef EXTERNALSORT_SORTUSINGPRIORITYQUEUE |
|
167 | 178 |
PQEntry* pqe = m_buffer.top(); m_buffer.pop(); |
179 |
#else |
|
180 |
PQEntry* pqe = m_buffer.back(); m_buffer.pop_back(); |
|
181 |
#endif |
|
168 | 182 |
tf->storeNextObject( pqe->m_pRecord ); |
169 | 183 |
delete pqe; |
170 | 184 |
} |
src/core/spatialindex/qgsspatialindex.cpp (working copy) | ||
---|---|---|
22 | 22 |
#include "qgslogger.h" |
23 | 23 | |
24 | 24 |
#include "SpatialIndex.h" |
25 |
#include "spatialindex/SpatialIndexImpl.h" |
|
25 | 26 | |
27 |
#include "rtree/Node.h" |
|
28 |
#include "rtree/Leaf.h" |
|
29 |
#include "rtree/Index.h" |
|
30 |
#include "rtree/BulkLoader.h" |
|
31 | ||
32 |
#include "rtree/RTree.h" |
|
33 | ||
34 |
#include <stdexcept> |
|
35 | ||
26 | 36 |
using namespace SpatialIndex; |
27 | 37 | |
38 |
/** Convert rectangle to a region suitable for use in the spatial index library */ |
|
39 |
inline Tools::Geometry::Region rectToRegion( QgsRectangle rect ) |
|
40 |
{ |
|
41 |
double pt1[2], pt2[2]; |
|
42 |
pt1[0] = rect.xMinimum(); |
|
43 |
pt1[1] = rect.yMinimum(); |
|
44 |
pt2[0] = rect.xMaximum(); |
|
45 |
pt2[1] = rect.yMaximum(); |
|
46 |
return Tools::Geometry::Region( pt1, pt2, 2 ); |
|
47 |
} |
|
28 | 48 | |
29 |
// custom visitor that adds found features to list |
|
49 |
inline QgsRectangle regionToRect( Tools::Geometry::Region rect ) |
|
50 |
{ |
|
51 |
return QgsRectangle( rect.getLow(0), rect.getLow(1), rect.getHigh(0), rect.getHigh(1) ); |
|
52 |
} |
|
53 | ||
54 | ||
55 |
/** returns, whether the rectBig contains rectSmall (with specified overflow exceptions) */ |
|
56 |
inline bool RectContainsWithOverflow( QgsRectangle rectBig, QgsRectangle rectSmall, |
|
57 |
bool left, bool right, |
|
58 |
bool over, bool under ) |
|
59 |
{ |
|
60 |
bool rv=true; |
|
61 |
|
|
62 |
rv &= left || !left && (rectBig.xMinimum() < rectSmall.xMinimum()); |
|
63 |
rv &= right || !right && (rectBig.xMaximum() > rectSmall.xMaximum()); |
|
64 |
rv &= over || !over && (rectBig.yMinimum() < rectSmall.yMinimum()); |
|
65 |
rv &= under || !under && (rectBig.yMaximum() > rectSmall.yMaximum()); |
|
66 |
|
|
67 |
return rv; |
|
68 |
} |
|
69 | ||
70 | ||
71 | ||
72 |
/** |
|
73 |
* Creates a handle (pointer to pointer...) to specified feature. This |
|
74 |
* is needed for the RTree, as we can't directly change data node's data |
|
75 |
* (without removing and reinserting the node) and also because the data provided |
|
76 |
* to RTree is being memcopied (so we can't place a object there - we wouldn't be |
|
77 |
* able to delete it nicely later). |
|
78 |
* If the target is nonzero, it's an array where address of the |
|
79 |
* pointer to feature will be stored. Otherwise the array will be allocated |
|
80 |
* by the function. |
|
81 |
* @param f Feature to be handled |
|
82 |
* @param target if nonzero, it's considered to be a array of size sizeof(byte*), |
|
83 |
* The return value will be stored in this array. |
|
84 |
* @returns Handle to the feature (newly allocated or ==target, if target!=0). |
|
85 |
* The return value is an array containing the handle's value. Crazy stuff. |
|
86 |
**/ |
|
87 |
inline byte* QgsFeaturePtr2ByteArray(QgsFeature* f, byte *target=0) |
|
88 |
{ |
|
89 |
//alloc a doublepointer to QgsFeature |
|
90 |
//Data (rv) -> fPtrPtr -> QgsFeature (f) |
|
91 |
//need to do this, because we can't change contents of Data, once inserted... |
|
92 |
|
|
93 |
byte* rv; |
|
94 |
|
|
95 |
if (target==0) |
|
96 |
rv=new byte[sizeof(byte*)]; |
|
97 |
else |
|
98 |
rv=target; |
|
99 |
|
|
100 |
//create link: fPtrPtr -> f |
|
101 |
QgsFeature **fPtrPtr=new QgsFeature *; |
|
102 |
*fPtrPtr=f; |
|
103 |
|
|
104 |
//copy fPtrPtr address to rv |
|
105 |
memcpy(rv, &fPtrPtr, sizeof(QgsFeature**)); |
|
106 |
|
|
107 |
return rv; |
|
108 |
} |
|
109 | ||
110 | ||
111 | ||
112 |
/** |
|
113 |
* Inverse operation to QgsFeaturePtr2ByteArray. Also deallocates the specified |
|
114 |
* pointer's array. Returns pointer to pointer to the feature referenced by handle. |
|
115 |
**/ |
|
116 |
inline QgsFeature** ByteArray2QgsFeaturePtr(byte *p) |
|
117 |
{ |
|
118 |
QgsFeature** rv; |
|
119 |
|
|
120 |
//retrieve pointer to fPtrPtr |
|
121 |
memcpy(&rv, p, sizeof(QgsFeature**)); |
|
122 |
|
|
123 |
//release received data copy |
|
124 |
delete []p; |
|
125 |
|
|
126 |
return rv; |
|
127 |
} |
|
128 | ||
129 | ||
130 | ||
131 |
/** Visitor that adds found features to list */ |
|
30 | 132 |
class QgisVisitor : public SpatialIndex::IVisitor |
31 | 133 |
{ |
32 | 134 |
public: |
... | ... | |
47 | 149 |
}; |
48 | 150 | |
49 | 151 | |
50 |
QgsSpatialIndex::QgsSpatialIndex() |
|
152 | ||
153 |
/** Visitor that's freeing QgsFeature** objects stored in the Rtree. */ |
|
154 |
class QgisFreeMemVisitor : public SpatialIndex::IVisitor |
|
51 | 155 |
{ |
156 |
public: |
|
157 |
QgisFreeMemVisitor( bool dontDeletePointer=false ): |
|
158 |
mOnlyFreeSpecifiedId(false), mDontDeletePointer(dontDeletePointer) {} |
|
159 |
|
|
160 |
QgisFreeMemVisitor( long onlyFreeId, bool dontDeletePointer=false ): |
|
161 |
mOnlyFreeId(onlyFreeId), mOnlyFreeSpecifiedId(true), |
|
162 |
mDontDeletePointer(dontDeletePointer) {} |
|
163 | ||
164 |
void visitNode( const INode& n ) {} |
|
165 | ||
166 |
void visitData( const IData& d ) |
|
167 |
{ |
|
168 |
if ( mOnlyFreeSpecifiedId && ( d.getIdentifier() != mOnlyFreeId ) ) |
|
169 |
return; |
|
170 |
|
|
171 |
long unsigned len; |
|
172 |
byte *data; |
|
173 |
d.getData(len, &data); |
|
174 |
|
|
175 |
QgsFeature **ptr=ByteArray2QgsFeaturePtr(data); |
|
176 |
|
|
177 |
if ( !ptr ) |
|
178 |
return; |
|
179 |
|
|
180 |
if (*ptr!=0) |
|
181 |
{ |
|
182 |
delete *ptr; |
|
183 |
*ptr=0; |
|
184 |
} |
|
185 |
|
|
186 |
if ( !mDontDeletePointer ) |
|
187 |
delete ptr; |
|
188 |
} |
|
189 | ||
190 |
void visitData( std::vector<const IData*>& v ) {} |
|
191 | ||
192 |
private: |
|
193 |
bool mDontDeletePointer; |
|
194 |
bool mOnlyFreeSpecifiedId; |
|
195 |
long mOnlyFreeId; |
|
196 |
}; |
|
197 | ||
198 | ||
199 | ||
200 |
/** Visitor storing found features to a QLinkedList. */ |
|
201 |
class QgisFeatureVisitor : public SpatialIndex::IVisitor |
|
202 |
{ |
|
203 |
public: |
|
204 |
/** |
|
205 |
* If unlinkFeatureFromData==true, then the features are also detached from |
|
206 |
* the rtree (used e.g. when some features are extracted from tree and |
|
207 |
* the tree is deleted afterwards (which erases all contained features)) |
|
208 |
*/ |
|
209 |
QgisFeatureVisitor( QLinkedList<QgsFeature *> & list, bool unlinkFeatureFromData = false ) |
|
210 |
: mList( list ), mUnlinkFeatureFromData( unlinkFeatureFromData ) {} |
|
211 | ||
212 |
void visitNode( const INode& n ) {} |
|
213 | ||
214 |
void visitData( const IData& d ) |
|
215 |
{ |
|
216 |
long unsigned len; |
|
217 |
byte *data; |
|
218 |
d.getData(len, &data); |
|
219 |
|
|
220 |
QgsFeature **f=ByteArray2QgsFeaturePtr(data); |
|
221 |
|
|
222 |
mList.push_back( *f ); |
|
223 |
|
|
224 |
if ( mUnlinkFeatureFromData ) |
|
225 |
*f=0; |
|
226 |
} |
|
227 | ||
228 |
void visitData( std::vector<const IData*>& v ) {} |
|
229 | ||
230 |
private: |
|
231 |
QLinkedList<QgsFeature *>& mList; |
|
232 |
|
|
233 |
bool mUnlinkFeatureFromData; |
|
234 |
}; |
|
235 | ||
236 | ||
237 | ||
238 |
/** |
|
239 |
* A trivial data stream providing RTree::Data objects from list to ExternalSort. |
|
240 |
**/ |
|
241 |
class QgisFeatureLinkedListDataStream : public IDataStream |
|
242 |
{ |
|
243 |
public: |
|
244 |
QgisFeatureLinkedListDataStream(QLinkedList<QgsFeature*> &list) : mList(list) |
|
245 |
{ rewind(); } |
|
246 | ||
247 |
virtual ~QgisFeatureLinkedListDataStream() { } |
|
248 | ||
249 |
virtual IData* getNext() |
|
250 |
{ |
|
251 |
if (!hasNext()) |
|
252 |
return 0; |
|
253 |
|
|
254 |
Region r=rectToRegion((*mListIt)->geometry()->boundingBox()); |
|
255 |
|
|
256 |
//We basically create a pointer to feature in list and hand pointer |
|
257 |
//to that pointer (=handle) to the caller (most probably it'll be |
|
258 |
//the Tools::ExternalSort::initializeRuns ...) |
|
259 |
|
|
260 |
byte ptr[sizeof(byte*)]; |
|
261 |
QgsFeaturePtr2ByteArray(*mListIt, ptr); |
|
262 |
|
|
263 |
RTree::Data* ret = new RTree::Data( |
|
264 |
sizeof(byte*), ptr, |
|
265 |
r, (*mListIt)->id() |
|
266 |
); |
|
267 | ||
268 |
mListIt++; |
|
269 |
|
|
270 |
return ret; |
|
271 |
} |
|
272 | ||
273 |
virtual bool hasNext() throw (Tools::NotSupportedException) |
|
274 |
{ return mListIt!=mList.end(); } |
|
275 | ||
276 |
virtual long unsigned size() throw (Tools::NotSupportedException) |
|
277 |
{ throw Tools::NotSupportedException("Operation not supported."); } |
|
278 | ||
279 |
virtual void rewind() throw (Tools::NotSupportedException) |
|
280 |
{ mListIt=mList.begin(); } |
|
281 |
|
|
282 |
private: |
|
283 |
QLinkedList<QgsFeature*> &mList; |
|
284 |
QLinkedList<QgsFeature*>::iterator mListIt; |
|
285 |
}; |
|
286 | ||
287 | ||
288 |
/** A strategy pattern class determining maximal bounding rectangle of a rtree */ |
|
289 |
class DetermineIndexedRectStrategy : public IQueryStrategy |
|
290 |
{ |
|
291 |
public: |
|
292 |
QgsRectangle mIndexedRect; |
|
293 | ||
294 |
public: |
|
295 |
void getNextEntry(const IEntry& entry, long& nextEntry, bool& hasNext) |
|
296 |
{ |
|
297 |
Region region; |
|
298 |
|
|
299 |
//The first time we are called, entry points to the root. We don't need more. |
|
300 |
hasNext = false; |
|
301 | ||
302 |
IShape* ps; |
|
303 |
entry.getShape(&ps); |
|
304 |
ps->getMBR(region); |
|
305 |
delete ps; |
|
306 |
|
|
307 |
mIndexedRect=regionToRect(region); |
|
308 |
} |
|
309 |
}; |
|
310 | ||
311 | ||
312 |
QgsSpatialIndex::QgsSpatialIndex(bool copyFeatures) |
|
313 |
{ |
|
314 |
initialize(QLinkedList<QgsFeature*>(), copyFeatures); |
|
315 |
} |
|
316 | ||
317 | ||
318 | ||
319 |
QgsSpatialIndex::QgsSpatialIndex(QLinkedList<QgsFeature*> features, |
|
320 |
bool copyFeatures ) |
|
321 |
{ |
|
322 |
initialize(features, copyFeatures); |
|
323 |
} |
|
324 | ||
325 | ||
326 | ||
327 |
void QgsSpatialIndex::initialize(QLinkedList<QgsFeature*> features, bool copyFeatures) |
|
328 |
{ |
|
329 |
mCopyFeatures=copyFeatures; |
|
330 |
|
|
52 | 331 |
// for now only memory manager |
53 | 332 |
mStorageManager = StorageManager::createNewMemoryStorageManager(); |
54 | 333 | |
55 | 334 |
// create buffer |
56 | ||
57 | 335 |
unsigned int capacity = 10; |
58 | 336 |
bool writeThrough = false; |
59 | 337 |
mStorage = StorageManager::createNewRandomEvictionsBuffer( *mStorageManager, capacity, writeThrough ); |
60 | ||
338 |
|
|
61 | 339 |
// R-Tree parameters |
62 | 340 |
double fillFactor = 0.7; |
63 | 341 |
unsigned long indexCapacity = 10; |
... | ... | |
67 | 345 | |
68 | 346 |
// create R-tree |
69 | 347 |
long indexId; |
348 |
|
|
70 | 349 |
mRTree = RTree::createNewRTree( *mStorage, fillFactor, indexCapacity, |
71 | 350 |
leafCapacity, dimension, variant, indexId ); |
351 |
|
|
352 |
if (!features.empty()) { |
|
353 |
//Bulk load provided features into the empty tree |
|
354 |
QgisFeatureLinkedListDataStream stream(features); |
|
355 |
|
|
356 |
unsigned long bindex = static_cast<unsigned long>( std::floor( static_cast<double>( indexCapacity * fillFactor ) ) ); |
|
357 |
unsigned long bleaf = static_cast<unsigned long>( std::floor( static_cast<double>( leafCapacity * fillFactor ) ) ); |
|
358 | ||
359 |
SpatialIndex::RTree::BulkLoader bl; |
|
360 | ||
361 |
bl.bulkLoadUsingSTR( static_cast<RTree::RTree*>( mRTree ), stream, bindex, bleaf, 50000000 ); |
|
362 |
|
|
363 |
//all features' pointers were added to index, make sure noone else will |
|
364 |
//use them,.. |
|
365 |
features.clear(); |
|
366 |
} |
|
72 | 367 |
} |
73 | 368 | |
74 |
QgsSpatialIndex:: ~QgsSpatialIndex() |
|
369 | ||
370 | ||
371 |
QgsSpatialIndex::~QgsSpatialIndex() |
|
75 | 372 |
{ |
373 |
uninitialize(); |
|
374 |
} |
|
375 | ||
376 | ||
377 | ||
378 |
void QgsSpatialIndex::uninitialize() |
|
379 |
{ |
|
380 |
QgisFreeMemVisitor freeingVisitor; |
|
381 |
|
|
382 |
//free all feature pointers and features in the tree |
|
383 |
//TODO: what is maximal range? or how to select all features in rtree? |
|
384 |
// |
|
385 |
DetermineIndexedRectStrategy getRectStrategy; |
|
386 |
mRTree->queryStrategy(getRectStrategy); |
|
387 | ||
388 |
mRTree->intersectsWithQuery( |
|
389 |
rectToRegion( getRectStrategy.mIndexedRect ), |
|
390 |
freeingVisitor |
|
391 |
); |
|
392 |
|
|
393 |
//delete the rest of index |
|
76 | 394 |
delete mRTree; |
77 | 395 |
delete mStorage; |
78 | 396 |
delete mStorageManager; |
79 | 397 |
} |
80 | 398 | |
399 | ||
400 | ||
81 | 401 |
Tools::Geometry::Region QgsSpatialIndex::rectToRegion( QgsRectangle rect ) |
82 | 402 |
{ |
83 |
double pt1[2], pt2[2]; |
|
84 |
pt1[0] = rect.xMinimum(); |
|
85 |
pt1[1] = rect.yMinimum(); |
|
86 |
pt2[0] = rect.xMaximum(); |
|
87 |
pt2[1] = rect.yMaximum(); |
|
88 |
return Tools::Geometry::Region( pt1, pt2, 2 ); |
|
403 |
return ::rectToRegion( rect ); |
|
89 | 404 |
} |
90 | 405 | |
406 | ||
407 | ||
91 | 408 |
bool QgsSpatialIndex::featureInfo( QgsFeature& f, Tools::Geometry::Region& r, long& id ) |
92 | 409 |
{ |
93 | 410 |
QgsGeometry *g = f.geometry(); |
... | ... | |
99 | 416 |
return true; |
100 | 417 |
} |
101 | 418 | |
419 | ||
420 | ||
102 | 421 |
bool QgsSpatialIndex::insertFeature( QgsFeature& f ) |
103 | 422 |
{ |
423 |
if (!mCopyFeatures) |
|
424 |
{ |
|
425 |
return insertFeature(&f); |
|
426 |
} |
|
427 |
else |
|
428 |
{ |
|
429 |
QgsFeature *fP=new QgsFeature(f); |
|
430 |
return insertFeature(fP); |
|
431 |
} |
|
432 |
} |
|
433 | ||
434 | ||
435 | ||
436 |
bool QgsSpatialIndex::insertFeature( QgsFeature* f ) |
|
437 |
{ |
|
104 | 438 |
Tools::Geometry::Region r; |
105 | 439 |
long id; |
106 |
if ( !featureInfo( f, r, id ) ) |
|
440 |
if ( !featureInfo( *f, r, id ) )
|
|
107 | 441 |
return false; |
108 | 442 | |
109 | 443 |
// TODO: handle possible exceptions correctly |
110 | 444 |
try |
111 | 445 |
{ |
112 |
mRTree->insertData( 0, 0, r, id ); |
|
446 |
if (!mCopyFeatures) |
|
447 |
{ |
|
448 |
mRTree->insertData( 0, 0, r, id ); |
|
449 |
} |
|
450 |
else |
|
451 |
{ |
|
452 |
mRTree->insertData( sizeof(QgsFeature*), QgsFeaturePtr2ByteArray(f), r, id ); |
|
453 |
} |
|
113 | 454 |
} |
114 | 455 |
catch ( Tools::Exception &e ) |
115 | 456 |
{ |
... | ... | |
129 | 470 |
return true; |
130 | 471 |
} |