Skip to content

Commit

Permalink
added basic reloading of auxilliary map layers
Browse files Browse the repository at this point in the history
addresses #12
  • Loading branch information
jakimowb committed Dec 8, 2024
1 parent bbf0b21 commit df01ef2
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 18 deletions.
10 changes: 8 additions & 2 deletions eotimeseriesviewer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -692,8 +692,14 @@ def onReadProject(self, doc: QDomDocument) -> bool:
if not isinstance(doc, QDomDocument):
return False

sender = self.sender()

root = doc.documentElement()
node = root.firstChildElement('EOTSV')

if isinstance(sender, QgsProject):
print(f'# READ_PROJECT: {id(sender)} {sender.fileName()}')

if node.nodeName() == 'EOTSV':

class MyProgress(QgsProcessingFeedback):
Expand Down Expand Up @@ -732,13 +738,13 @@ def onProgress(v):
jsonText = node.firstChildElement('jsonSettings').text()
self.fromJson(jsonText, feedback=feedback)
except Exception as ex:
if False:
raise ex
print(ex, file=sys.stderr)

dialog.setValue(100)
dialog.close()

return

return True

def reloadProject(self, *args):
Expand Down
85 changes: 83 additions & 2 deletions eotimeseriesviewer/mapvisualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,9 @@ def layers(self) -> List[QgsMapLayer]:
nodes = self.mLayerTree.findLayers()
return [n.layer() for n in nodes if isinstance(n.layer(), QgsMapLayer)]

def layerTree(self) -> QgsLayerTree:
return self.mLayerTree

def title(self, maskNewLines=True) -> str:
"""
Returns the MapView title
Expand Down Expand Up @@ -1499,9 +1502,13 @@ def setCurrentMapView(self, mapView: MapView):
MKeyMapViews = 'map_views'
MKeyCurrentDate = 'current_date'
MKeyCurrentExtent = 'extent'
MKeyAuxMapLayers = '_aux_map_layers'

def asMap(self) -> dict:

"""
Returns the current visualisation settings as dict, to be serialized in a JSON dump.
:return: dict
"""
d = {self.MKeyMapSize: [self.mapSize().width(), self.mapSize().height()],
self.MKeyCrs: self.crs().toWkt(),
self.MKeyMapsPerView: self.mapsPerMapView(),
Expand All @@ -1510,6 +1517,30 @@ def asMap(self) -> dict:
self.MKeyCurrentExtent: self.spatialExtent().asWktPolygon(),
}

# basic storing of aux map layers. relates to https://github.com/jakimowb/eo-time-series-viewer/issues/12
# to be replaced by proper layer-tree reconstruction
aux_map_layers = {}
aux_mapview_layers = []
for mv in self.mapViews():
mv_layer_info = []
for lyr in mv.layers():
if not has_sensor_id(lyr):
if lyr.id() not in aux_map_layers:
info_source = {
'id': lyr.id(),
'source': lyr.source(),
'provider': lyr.dataProvider().name(),
'class': lyr.__class__.__name__,
'name': lyr.name(),
'style': layerStyleString(lyr, QgsMapLayer.AllStyleCategories)}
aux_map_layers[lyr.id()] = info_source
mv_layer_info.append(lyr.id())
aux_mapview_layers.append(mv_layer_info)

d[self.MKeyAuxMapLayers] = {
'sources': aux_map_layers,
'map_views': aux_mapview_layers
}
return d

def allProxyLayers(self) -> List[QgsRasterLayer]:
Expand Down Expand Up @@ -1575,7 +1606,57 @@ def fromMap(self, data: dict, feedback: QgsProcessingFeedback = QgsProcessingFee
extent = QgsRectangle.fromWkt(extent)
self.setSpatialExtent(SpatialExtent(self.crs(), extent))

s = ""
CLASS2INIT = {c.__name__: c for c in [QgsVectorLayer, QgsRasterLayer]}

if aux_layers := data.get(self.MKeyAuxMapLayers):
new_layers = dict()
if sources := aux_layers.get('sources'):

for oldId, sourceInfo in sources.items():
clsName = sourceInfo.get('class')
source = sourceInfo.get('source')
name = sourceInfo.get('name')
provider = sourceInfo.get('provider')

if not (source and name and provider):
continue

lyr = None
# check for layers that already exist, e.g. because we
# call load project within the same session
# check QGIS and internal layers
for store in [self.mMapLayerStore, QgsProject.instance()]:
if existingLayer := store.mapLayer(oldId):
if existingLayer.isValid():
lyr = existingLayer
break

if lyr is None:
if clsName == QgsVectorLayer.__name__:
lyr = QgsVectorLayer(source, name, providerLib=provider)
elif clsName == QgsRasterLayer.__name__:
lyr = QgsRasterLayer(source, name, providerType=provider)
if isinstance(lyr, QgsMapLayer) and lyr.isValid():
if styleXml := sourceInfo.get('style'):
setLayerStyleString(lyr, styleXml)

new_layers[oldId] = lyr

self.mMapLayerStore.addMapLayers(new_layers.values())

for i_mv, mv_layer_ids in enumerate(aux_layers.get('map_views', [])):
if i_mv >= len(self.mapViews()):
break
new_mv: MapView = self.mapViews()[i_mv]
new_mv_layers = []
for oldId in mv_layer_ids:
lyrNew = new_layers.get(oldId)
if isinstance(lyrNew, QgsMapLayer):
new_mv_layers.append(lyrNew)
existing_layers = new_mv.layers()
for lyr in reversed(new_mv_layers):
if lyr not in existing_layers:
new_mv.addLayer(lyr)

def usedLayers(self) -> List[QgsMapLayer]:
layers = set()
Expand Down
59 changes: 45 additions & 14 deletions tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,36 @@ def assertEqualSetting(self, a: dict, b: dict):
s = ""
s = ""

def assertEqualElements(self, a, b, prefix: str = ''):
self.assertEqual(type(a), type(b), msg=f'{prefix}\n\tUnequal types: {a} vs {b}'.strip())
def assertEqualElements(self, a, b,
prefix: str = '',
sort_lists: bool = False):
self.assertEqual(type(a), type(b),
msg=f'{prefix}\n\tUnequal types: {a} vs {b}'.strip())

if isinstance(a, dict):
self.assertEqual(a.keys(), b.keys())
for k in a.keys():
self.assertEqualElements(a[k], b[k], prefix=f'{prefix}["{k}"]'.strip())
if k.startswith('_'):
continue
self.assertEqualElements(a[k], b[k],
prefix=f'{prefix}["{k}"]'.strip(),
sort_lists=sort_lists)
elif isinstance(a, set):
self.assertEqualElements(list(a), list(b),
sort_lists=sort_lists,
prefix=prefix)
elif isinstance(a, list):
self.assertEqual(len(a), len(b), msg=f'{prefix}')
self.assertEqual(len(a), len(b),
msg=f'{prefix}:\n\tLists differ in number of elements: {len(a)} != {len(b)}'
f'\n\tExpected: {a}\n\t Actual: {b}')
if sort_lists:
a, b = sorted(a), sorted(b)

for i, (k1, k2) in enumerate(zip(a, b)):
self.assertEqualElements(k1, k2, prefix=f'{prefix}[{i}]'.strip())
else:
self.assertEqual(a, b, msg=f'{prefix}:\n\tUnequal elements: {a} vs. {b}'.strip())
self.assertEqual(a, b,
msg=f'{prefix}:\n\tUnequal elements: {a} vs. {b}'.strip())

def test_force(self):

Expand Down Expand Up @@ -159,26 +176,30 @@ def test_write_read(self):
reds0c = TSV.mapWidget()._allReds()

assert len(QgsProject.instance().mapLayers()) == 0
reds0d = TSV.mapWidget()._allReds()
self.taskManagerProcessEvents()
reds0e = TSV.mapWidget()._allReds()
if len(TSV.timeSeries()) > 0:
tsd = TSV.timeSeries()[-1]
TSV.setCurrentDate(tsd)

from example import exampleEvents
lyr = QgsVectorLayer(exampleEvents)
lyr.setName('MyTestLayer')
TSV.mapViews()[0].setName('True Color')
TSV.addMapLayers([lyr])
assert len(QgsProject.instance().mapLayers()) == 0
TSV.createMapView('My 2nd View')
TSV.applyAllVisualChanges()

def mapViewNoneSensorLayers() -> dict:
return {mv.name(): [lyr.name() for lyr in mv.layers() if isinstance(lyr, QgsVectorLayer)] for mv in
TSV.mapViews()}

nslayers1 = mapViewNoneSensorLayers()
self.assertTrue('MyTestLayer' in nslayers1['True Color'])

map_views = TSV.mapViews()
self.assertTrue(len(map_views) == 2)

reds = TSV.mapWidget()._allReds()

ext = SpatialExtent.fromLayer(lyr)
TSV.setCrs(ext.crs())
self.assertEqual(TSV.crs(), ext.crs())
Expand All @@ -189,31 +210,36 @@ def test_write_read(self):
self.assertValidSettings(settings2)
TSV.applyAllVisualChanges()

reds0_b = TSV.mapWidget()._allReds()

settings1 = TSV.asMap()

# save settings
path = self.createTestOutputDirectory() / 'test.qgs'
QgsProject.instance().write(path.as_posix())

TSV.applyAllVisualChanges()

# Ensure that writing does not change the configuration
# This test is inspired by MS Word's PDF export, that modified my dissertation docx
# several times without even telling me! (I know I should have used LaTeX and have been doing so since.)
settings2 = TSV.asMap()
self.assertEqualElements(settings1, settings2)
nslayers2 = mapViewNoneSensorLayers()
self.assertEqualElements(nslayers1, nslayers2)

self.assertTrue(QgsProject.instance().read(path.as_posix()))
TSV.applyAllVisualChanges()
settings2 = TSV.asMap()
nslayers2 = mapViewNoneSensorLayers()
self.assertEqualElements(settings1, settings2)

self.assertEqualElements(nslayers1, nslayers2, sort_lists=True)

# do some changes
new_map_size = QSize(300, 250)
new_maps_per_view = (2, 2)
new_crs = QgsCoordinateReferenceSystem('EPSG:4326')
TSV.setMapSize(new_map_size)
TSV.createMapView('My New MapView')
TSV.setCrs(new_crs)

new_ns_layers = mapViewNoneSensorLayers()
new_map_view_names = [mv.name() for mv in TSV.mapViews()]

new_tss_visibility = dict()
Expand Down Expand Up @@ -254,7 +280,12 @@ def test_write_read(self):
self.assertEqual(tss.isVisible(), tss_vis.pop(tss.uri()))
self.assertTrue(len(tss_vis) == 0)

ns_layers3 = mapViewNoneSensorLayers()

self.assertEqualElements(new_ns_layers, ns_layers3)

TSV.setMapsPerMapView(5, 1)

self.showGui([TSV.ui]) #

TSV.close()
Expand Down

0 comments on commit df01ef2

Please sign in to comment.