def download(feature_type, output_base_path, extent, progress_dialog=None):
"""Download shapefiles from Kartoza server.
.. versionadded:: 3.2
:param feature_type: What kind of features should be downloaded.
Currently 'buildings', 'building-points' or 'roads' are supported.
:type feature_type: str
:param output_base_path: The base path of the shape file.
:type output_base_path: str
:param extent: A list in the form [xmin, ymin, xmax, ymax] where all
coordinates provided are in Geographic / EPSG:4326.
:type extent: list
:param progress_dialog: A progress dialog.
:type progress_dialog: QProgressDialog
:raises: ImportDialogError, CanceledImportDialogError
"""
# preparing necessary data
min_longitude = extent[0]
min_latitude = extent[1]
max_longitude = extent[2]
max_latitude = extent[3]
box = (
'{min_longitude},{min_latitude},{max_longitude},'
'{max_latitude}').format(
min_longitude=min_longitude,
min_latitude=min_latitude,
max_longitude=max_longitude,
max_latitude=max_latitude
)
url = (
'{url_osm_prefix}'
'{feature_type}'
'{url_osm_suffix}?'
'bbox={box}&'
'qgis_version={qgis}&'
'lang={lang}&'
'inasafe_version={inasafe_version}'.format(
url_osm_prefix=URL_OSM_PREFIX,
feature_type=feature_type,
url_osm_suffix=URL_OSM_SUFFIX,
box=box,
qgis=qgis_version(),
lang=locale(),
inasafe_version=get_version()))
path = tempfile.mktemp('.shp.zip')
# download and extract it
fetch_zip(url, path, feature_type, progress_dialog)
extract_zip(path, output_base_path)
if progress_dialog:
progress_dialog.done(QDialog.Accepted)
def test_issue71(self):
"""Test issue #71 in github - cbo changes should update ok button."""
# See https://github.com/AIFDR/inasafe/issues/71
# Push OK with the left mouse button
print 'Using QGIS: %s' % qgis_version()
self.tearDown()
button = DOCK.pbnRunStop
# First part of scenario should have enabled run
file_list = [
join(HAZDATA, 'Flood_Current_Depth_Jakarta_geographic.asc'),
join(TESTDATA, 'Population_Jakarta_geographic.asc')]
hazard_layer_count, exposure_layer_count = load_layers(file_list)
message = (
'Incorrect number of Hazard layers: expected 1 got %s'
% hazard_layer_count)
self.assertTrue(hazard_layer_count == 1, message)
message = (
'Incorrect number of Exposure layers: expected 1 got %s'
% exposure_layer_count)
self.assertTrue(exposure_layer_count == 1, message)
message = 'Run button was not enabled'
self.assertTrue(button.isEnabled(), message)
# Second part of scenario - run disabled when adding invalid layer
# and select it - run should be disabled
path = os.path.join(TESTDATA, 'issue71.tif')
file_list = [path] # This layer has incorrect keywords
clear_flag = False
_, _ = load_layers(file_list, clear_flag)
# set exposure to : Population Count (5kmx5km)
# by moving one down
DOCK.cboExposure.setCurrentIndex(DOCK.cboExposure.currentIndex() + 1)
actual_dict = get_ui_state(DOCK)
expected_dict = {
'Run Button Enabled': False,
'Impact Function Id': '',
'Impact Function Title': '',
'Hazard': 'A flood in Jakarta like in 2007',
'Exposure': 'Population Count (5kmx5km)'}
message = ((
'Run button was not disabled when exposure set to \n%s'
'\nUI State: \n%s\nExpected State:\n%s\n%s') % (
DOCK.cboExposure.currentText(),
actual_dict,
expected_dict,
combos_to_string(DOCK)))
self.assertTrue(expected_dict == actual_dict, message)
# Now select again a valid layer and the run button
# should be enabled
DOCK.cboExposure.setCurrentIndex(DOCK.cboExposure.currentIndex() - 1)
message = (
'Run button was not enabled when exposure set to \n%s' %
DOCK.cboExposure.currentText())
self.assertTrue(button.isEnabled(), message)
def download(feature_type, output_base_path, extent, progress_dialog=None):
"""Download shapefiles from Kartoza server.
.. versionadded:: 3.2
:param feature_type: What kind of features should be downloaded.
Currently 'buildings', 'building-points' or 'roads' are supported.
:type feature_type: str
:param output_base_path: The base path of the shape file.
:type output_base_path: str
:param extent: A list in the form [xmin, ymin, xmax, ymax] where all
coordinates provided are in Geographic / EPSG:4326.
:type extent: list
:param progress_dialog: A progress dialog.
:type progress_dialog: QProgressDialog
:raises: ImportDialogError, CanceledImportDialogError
"""
# preparing necessary data
min_longitude = extent[0]
min_latitude = extent[1]
max_longitude = extent[2]
max_latitude = extent[3]
box = (
'{min_longitude},{min_latitude},{max_longitude},'
'{max_latitude}').format(
min_longitude=min_longitude,
min_latitude=min_latitude,
max_longitude=max_longitude,
max_latitude=max_latitude
)
url = URL_OSM_PREFIX + feature_type + URL_OSM_SUFFIX
url = '{url}?bbox={box}&qgis_version={qgis}'.format(
url=url, box=box, qgis=qgis_version())
url += '&inasafe_version=%s' % get_version()
if 'LANG' in os.environ:
# Get the language only : eg 'en_US' -> 'en'
env_lang = os.environ['LANG'].split('_')[0]
url += '&lang=%s' % env_lang
path = tempfile.mktemp('.shp.zip')
# download and extract it
fetch_zip(url, path, feature_type, progress_dialog)
extract_zip(path, output_base_path)
if progress_dialog:
progress_dialog.done(QDialog.Accepted)
def add_impact_layers_to_canvas(impact_function, group=None, iface=None):
"""Helper method to add impact layer to QGIS from impact function.
:param impact_function: The impact function used.
:type impact_function: ImpactFunction
:param group: An existing group as a parent, optional.
:type group: QgsLayerTreeGroup
:param iface: QGIS QgisAppInterface instance.
:type iface: QgisAppInterface
"""
layers = impact_function.outputs
name = impact_function.name
if group:
group_analysis = group
else:
# noinspection PyArgumentList
root = QgsProject.instance().layerTreeRoot()
group_analysis = root.insertGroup(0, name)
group_analysis.setItemVisibilityChecked(True)
group_analysis.setExpanded(group is None)
for layer in layers:
# noinspection PyArgumentList
QgsProject.instance().addMapLayer(layer, False)
layer_node = group_analysis.addLayer(layer)
# set layer title if any
try:
title = layer.keywords['title']
if qgis_version() >= 21800:
layer.setName(title)
else:
layer.setLayerName(title)
except KeyError:
pass
visible_layers = [impact_function.impact.id()]
print_atlas = setting('print_atlas_report', False, bool)
if print_atlas:
visible_layers.append(impact_function.aggregation_summary.id())
# Let's enable only the more detailed layer. See #2925
if layer.id() in visible_layers:
layer_node.setItemVisibilityChecked(True)
else:
layer_node.setItemVisibilityChecked(False)
# we need to set analysis_impacted as an active layer because we need
# to get all qgis variables that we need from this layer for
# infographic.
if iface:
iface.setActiveLayer(impact_function.analysis_impacted)
def test_clip_geometry(self):
"""Test that we can clip a geometry using another geometry."""
geometry = QgsGeometry.fromPolyline([
QgsPoint(10, 10), QgsPoint(20, 20), QgsPoint(30, 30),
QgsPoint(40, 40)])
clip_polygon = QgsGeometry.fromPolygon([
[QgsPoint(20, 20),
QgsPoint(20, 30),
QgsPoint(30, 30),
QgsPoint(30, 20),
QgsPoint(20, 20)]])
result = clip_geometry(clip_polygon, geometry)
expected_wkt = 'LINESTRING(20.0 20.0, 30.0 30.0)'
# There should only be one feature that intersects this clip
# poly so this assertion should work.
assert compare_wkt(expected_wkt, str(result.exportToWkt()))
# Now poly on poly clip test
clip_polygon = QgsGeometry.fromWkt(
'POLYGON((106.8218 -6.1842,106.8232 -6.1842,'
'106.82304963 -6.18317148,106.82179736 -6.18314774,'
'106.8218 -6.1842))')
geometry = QgsGeometry.fromWkt(
'POLYGON((106.8216869 -6.1852067,106.8213233 -6.1843051,'
'106.8212891 -6.1835559,106.8222566 -6.1835184,'
'106.8227557 -6.1835076,106.8228588 -6.1851204,'
'106.8216869 -6.1852067))')
result = clip_geometry(clip_polygon, geometry)
expected_wkt = (
'POLYGON((106.82179833 -6.18353616,106.8222566 -6.1835184,'
'106.8227557 -6.1835076,106.82279996 -6.1842,'
'106.8218 -6.1842,106.82179833 -6.18353616))')
# There should only be one feature that intersects this clip
# poly so this assertion should work.
assert compare_wkt(expected_wkt, str(result.exportToWkt()))
# Now point on poly test clip
geometry = QgsGeometry.fromWkt('POINT(106.82241 -6.18369)')
result = clip_geometry(clip_polygon, geometry)
if qgis_version() > 10800:
expected_wkt = 'POINT(106.82241 -6.18369)'
else:
expected_wkt = 'POINT(106.822410 -6.183690)'
# There should only be one feature that intersects this clip
# poly so this assertion should work.
assert compare_wkt(expected_wkt, str(result.exportToWkt()))
def load_shapefile(self, feature_type, base_path):
"""Load downloaded shape file to QGIS Main Window.
:param feature_type: What kind of features should be downloaded.
Currently 'buildings', 'building-points' or 'roads' are supported.
:type feature_type: str
:param base_path: The base path of the shape file (without extension).
:type base_path: str
:raises: FileMissingError - when buildings.shp not exist
"""
path = '%s.shp' % base_path
if not os.path.exists(path):
message = self.tr(
'%s does not exist. The server does not have any data for '
'this extent.' % path)
raise FileMissingError(message)
layer = self.iface.addVectorLayer(path, feature_type, 'ogr')
# Check if it's a building layer and if it's QGIS 2.14 about the 2.5D
if qgis_version() >= 21400 and feature_type == 'buildings':
layer_scope = QgsExpressionContextUtils.layerScope(layer)
if not layer_scope.variable('qgis_25d_height'):
QgsExpressionContextUtils.setLayerVariable(
layer, 'qgis_25d_height', 0.0002)
if not layer_scope.variable('qgis_25d_angle'):
QgsExpressionContextUtils.setLayerVariable(
layer, 'qgis_25d_angle', 70)
canvas_srid = self.canvas.mapSettings().destinationCrs().srsid()
on_the_fly_projection = self.canvas.hasCrsTransformEnabled()
if canvas_srid != 4326 and not on_the_fly_projection:
if QGis.QGIS_VERSION_INT >= 20400:
self.canvas.setCrsTransformEnabled(True)
else:
display_warning_message_bar(
self.iface,
self.tr('Enable \'on the fly\''),
self.tr(
'Your current projection is different than EPSG:4326. '
'You should enable \'on the fly\' to display '
'correctly your layers')
)
def add_layers_to_canvas_with_custom_orders(
order, impact_function, iface=None):
"""Helper to add layers to the map canvas following a specific order.
From top to bottom in the legend:
[
('FromCanvas', layer name, full layer URI, QML),
('FromAnalysis', layer purpose, layer group, None),
...
]
The full layer URI is coming from our helper.
:param order: Special structure the list of layers to add.
:type order: list
:param impact_function: The multi exposure impact function used.
:type impact_function: MultiExposureImpactFunction
:param iface: QGIS QgisAppInterface instance.
:type iface: QgisAppInterface
"""
root = QgsProject.instance().layerTreeRoot()
# Make all layers hidden.
for child in root.children():
child.setItemVisibilityChecked(False)
group_analysis = root.insertGroup(0, impact_function.name)
group_analysis.setItemVisibilityChecked(True)
group_analysis.setCustomProperty(MULTI_EXPOSURE_ANALYSIS_FLAG, True)
# Insert layers in the good order in the group.
for layer_definition in order:
if layer_definition[0] == FROM_CANVAS['key']:
style = QDomDocument()
style.setContent(layer_definition[3])
layer = load_layer(layer_definition[2], layer_definition[1])[0]
layer.importNamedStyle(style)
QgsProject.instance().addMapLayer(layer, False)
layer_node = group_analysis.addLayer(layer)
layer_node.setItemVisibilityChecked(True)
else:
if layer_definition[2] == impact_function.name:
for layer in impact_function.outputs:
if layer.keywords['layer_purpose'] == layer_definition[1]:
QgsProject.instance().addMapLayer(
layer, False)
layer_node = group_analysis.addLayer(layer)
layer_node.setItemVisibilityChecked(True)
try:
title = layer.keywords['title']
if qgis_version() >= 21800:
layer.setName(title)
else:
layer.setLayerName(title)
except KeyError:
pass
break
else:
for sub_impact_function in impact_function.impact_functions:
# Iterate over each sub impact function used in the
# multi exposure analysis.
if sub_impact_function.name == layer_definition[2]:
for layer in sub_impact_function.outputs:
purpose = layer_definition[1]
if layer.keywords['layer_purpose'] == purpose:
QgsProject.instance().addMapLayer(
layer, False)
layer_node = group_analysis.addLayer(
layer)
layer_node.setItemVisibilityChecked(True)
try:
title = layer.keywords['title']
if qgis_version() >= 21800:
layer.setName(title)
else:
layer.setLayerName(title)
except KeyError:
pass
break
if iface:
iface.setActiveLayer(impact_function.analysis_impacted)
def get_layer_description_from_browser(self, category):
"""Obtain the description of the browser layer selected by user.
:param category: The category of the layer to get the description.
:type category: string
:returns: Tuple of boolean and string. Boolean is true if layer is
validated as compatible for current role (impact function and
category) and false otherwise. String contains a description
of the selected layer or an error message.
:rtype: tuple
"""
if category == 'hazard':
browser = self.tvBrowserHazard
elif category == 'exposure':
browser = self.tvBrowserExposure
elif category == 'aggregation':
browser = self.tvBrowserAggregation
else:
raise InaSAFEError
index = browser.selectionModel().currentIndex()
if not index:
return False, ''
# Map the proxy model index to the source model index
index = browser.model().mapToSource(index)
item = browser.model().sourceModel().dataItem(index)
if not item:
return False, ''
item_class_name = item.metaObject().className()
# if not itemClassName.endswith('LayerItem'):
if not item.type() == QgsDataItem.Layer:
if item_class_name == 'QgsPGRootItem' and not item.children():
return False, create_postGIS_connection_first
else:
return False, ''
if item_class_name not in [
'QgsOgrLayerItem', 'QgsGdalLayerItem', 'QgsPGLayerItem',
'QgsLayerItem', ]:
return False, ''
path = item.path()
if item_class_name in ['QgsOgrLayerItem', 'QgsGdalLayerItem',
'QgsLayerItem'] and not os.path.exists(path):
return False, ''
# try to create the layer
if item_class_name == 'QgsOgrLayerItem':
layer = QgsVectorLayer(path, '', 'ogr')
elif item_class_name == 'QgsPGLayerItem':
uri = self.postgis_path_to_uri(path)
if uri:
layer = QgsVectorLayer(uri.uri(), uri.table(), 'postgres')
else:
layer = None
else:
layer = QgsRasterLayer(path, '', 'gdal')
if not layer or not layer.isValid():
return False, self.tr('Not a valid layer.')
try:
keywords = self.keyword_io.read_keywords(layer)
if ('layer_purpose' not in keywords and
'impact_summary' not in keywords):
keywords = None
except (HashNotFoundError,
OperationalError,
NoKeywordsFoundError,
KeywordNotFoundError,
InvalidParameterError,
UnsupportedProviderError,
MissingMetadata):
keywords = None
# set the layer name for further use in the step_fc_summary
if keywords:
if qgis_version() >= 21800:
layer.setName(keywords.get('title'))
else:
layer.setLayerName(keywords.get('title'))
if not self.parent.is_layer_compatible(layer, category, keywords):
label_text = '%s<br/>%s' % (
self.tr(
'This layer\'s keywords or type are not suitable:'),
self.unsuitable_layer_description_html(
layer, category, keywords))
return False, label_text
# set the current layer (e.g. for the keyword creation sub-thread
# or for adding the layer to mapCanvas)
self.parent.layer = layer
if category == 'hazard':
self.parent.hazard_layer = layer
#.........这里部分代码省略.........
请发表评论