Skip to content

Commit d1cedbc

Browse files
committedJul 10, 2018
[processing][needs-doc][FEATURE] Sample raster values to point
1 parent 2688a9d commit d1cedbc

File tree

6 files changed

+283
-1
lines changed

6 files changed

+283
-1
lines changed
 

‎python/plugins/processing/algs/help/qgis.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,15 @@ qgis:rasterlayerhistogram: >
423423
qgis:rasterlayerstatistics: >
424424
This algorithm computes basic statistics from the values in a given band of the raster layer.
425425

426+
qgis:rastersampling: >
427+
This algorithm creates a new vector layer with the same attributes of the input layer and the raster values corresponding on the point location.
428+
429+
Many raster layers can be chosen at the same time.
430+
431+
If the raster layer has more than one band, all the band values are sampled.
432+
433+
WARNING: raster layer(s) must be in the same CRS of the source point layer.
434+
426435
qgis:rasterize: >
427436
This algorithm rasterizes map canvas content.
428437

@@ -558,4 +567,3 @@ qgis:definecurrentprojection: >
558567
This algorithm sets an existing layer's projection to the provided CRS. Contrary to the "Assign projection" algorithm, it will not output a new layer.
559568

560569
For shapefile datasets, the .prj and .qpj files will be overwritten - or created if missing - to match the provided CRS.
561-

‎python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
from .Rasterize import RasterizeAlgorithm
114114
from .RasterCalculator import RasterCalculator
115115
from .RasterLayerStatistics import RasterLayerStatistics
116+
from .RasterSampling import RasterSampling
116117
from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed
117118
from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable
118119
from .RegularPoints import RegularPoints
@@ -229,6 +230,7 @@ def getAlgs(self):
229230
RasterCalculator(),
230231
RasterizeAlgorithm(),
231232
RasterLayerStatistics(),
233+
RasterSampling(),
232234
RectanglesOvalsDiamondsFixed(),
233235
RectanglesOvalsDiamondsVariable(),
234236
RegularPoints(),
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
RasterSampling.py
6+
-----------------------
7+
Date : July 2018
8+
Copyright : (C) 2018 by Matteo Ghetta
9+
Email : matteo dot ghetta at gmail dot com
10+
***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************
18+
"""
19+
20+
__author__ = 'Matteo Ghetta'
21+
__date__ = 'July 2018'
22+
__copyright__ = '(C) 2018, Matteo Ghetta'
23+
24+
# This will get replaced with a git SHA1 when you do a git archive
25+
26+
__revision__ = '$Format:%H$'
27+
28+
29+
import os
30+
31+
from qgis.PyQt.QtGui import QIcon
32+
from qgis.PyQt.QtCore import QVariant
33+
34+
from qgis.core import (QgsApplication,
35+
QgsField,
36+
QgsFeatureSink,
37+
QgsRaster,
38+
QgsProcessing,
39+
QgsProcessingParameterMultipleLayers,
40+
QgsFields,
41+
QgsProcessingUtils,
42+
QgsProcessingException,
43+
QgsProcessingParameterFeatureSource,
44+
QgsProcessingParameterFeatureSink)
45+
46+
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
47+
48+
49+
class RasterSampling(QgisAlgorithm):
50+
51+
INPUT = 'INPUT'
52+
RASTERCOPY = 'RASTERCOPY'
53+
OUTPUT = 'OUTPUT'
54+
55+
def name(self):
56+
return 'rastersampling'
57+
58+
def displayName(self):
59+
return self.tr('Sample Raster Values')
60+
61+
def group(self):
62+
return self.tr('Raster analysis')
63+
64+
def groupId(self):
65+
return 'rasteranalysis'
66+
67+
def __init__(self):
68+
super().__init__()
69+
70+
def initAlgorithm(self, config=None):
71+
self.addParameter(
72+
QgsProcessingParameterFeatureSource(
73+
self.INPUT,
74+
self.tr('Input Point Layer'),
75+
[QgsProcessing.TypeVectorPoint]
76+
)
77+
)
78+
79+
80+
self.addParameter(
81+
QgsProcessingParameterMultipleLayers(
82+
self.RASTERCOPY,
83+
self.tr('Raster Layer to sample'),
84+
QgsProcessing.TypeRaster
85+
)
86+
)
87+
88+
self.addParameter(
89+
QgsProcessingParameterFeatureSink(
90+
self.OUTPUT,
91+
self.tr('Sampled Points')
92+
)
93+
)
94+
95+
def processAlgorithm(self, parameters, context, feedback):
96+
97+
source = self.parameterAsSource(
98+
parameters,
99+
self.INPUT,
100+
context
101+
)
102+
103+
sampled_rasters = self.parameterAsLayerList(
104+
parameters,
105+
self.RASTERCOPY,
106+
context
107+
)
108+
109+
if source is None:
110+
raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))
111+
112+
source_fields = source.fields()
113+
raster_fields = QgsFields()
114+
115+
# append field to vector as rasterName_bandCount
116+
for i in sampled_rasters:
117+
for b in range(i.bandCount()):
118+
raster_fields.append(QgsField(i.name() + str('_{}'.format(b+1)), QVariant.Double))
119+
120+
121+
# combine all the vector fields
122+
out_fields = QgsProcessingUtils.combineFields(source_fields, raster_fields)
123+
124+
125+
(sink, dest_id) = self.parameterAsSink(
126+
parameters,
127+
self.OUTPUT,
128+
context,
129+
out_fields,
130+
source.wkbType(),
131+
source.sourceCrs()
132+
)
133+
134+
135+
if sink is None:
136+
raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))
137+
138+
total = 100.0 / source.featureCount() if source.featureCount() else 0
139+
features = source.getFeatures()
140+
141+
for n, i in enumerate(source.getFeatures()):
142+
143+
for rr in sampled_rasters:
144+
145+
attrs = i.attributes()
146+
147+
if rr.bandCount() >1:
148+
149+
for b in range(rr.bandCount()):
150+
attrs.append(rr.dataProvider().identify(i.geometry().asPoint(),
151+
QgsRaster.IdentifyFormatValue).results()[b+1])
152+
153+
154+
attrs.append(rr.dataProvider().identify(i.geometry().asPoint(), QgsRaster.IdentifyFormatValue).results()[1])
155+
156+
157+
i.setAttributes(attrs)
158+
159+
sink.addFeature(i, QgsFeatureSink.FastInsert)
160+
feedback.setProgress(int(n * total))
161+
162+
163+
return {self.OUTPUT: dest_id}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ raster_sampling.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy>
8+
<gml:Box>
9+
<gml:coord><gml:X>18.67356158120081</gml:X><gml:Y>45.78707029401009</gml:Y></gml:coord>
10+
<gml:coord><gml:X>18.6923582796333</gml:X><gml:Y>45.80603482010714</gml:Y></gml:coord>
11+
</gml:Box>
12+
</gml:boundedBy>
13+
14+
<gml:featureMember>
15+
<ogr:raster_sampling fid="raster_sampling.0">
16+
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>18.6735615812008,45.8060348201071</gml:coordinates></gml:Point></ogr:geometryProperty>
17+
<ogr:id>1</ogr:id>
18+
<ogr:dem_1>91.9273986816406</ogr:dem_1>
19+
</ogr:raster_sampling>
20+
</gml:featureMember>
21+
<gml:featureMember>
22+
<ogr:raster_sampling fid="raster_sampling.1">
23+
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>18.6891695540064,45.8035174051385</gml:coordinates></gml:Point></ogr:geometryProperty>
24+
<ogr:id>2</ogr:id>
25+
<ogr:dem_1>122.21053314209</ogr:dem_1>
26+
</ogr:raster_sampling>
27+
</gml:featureMember>
28+
<gml:featureMember>
29+
<ogr:raster_sampling fid="raster_sampling.2">
30+
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>18.6923582796333,45.7875737770038</gml:coordinates></gml:Point></ogr:geometryProperty>
31+
<ogr:id>3</ogr:id>
32+
<ogr:dem_1>129.417877197266</ogr:dem_1>
33+
</ogr:raster_sampling>
34+
</gml:featureMember>
35+
<gml:featureMember>
36+
<ogr:raster_sampling fid="raster_sampling.3">
37+
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>18.6796033771255,45.7870702940101</gml:coordinates></gml:Point></ogr:geometryProperty>
38+
<ogr:id>4</ogr:id>
39+
<ogr:dem_1>109.654373168945</ogr:dem_1>
40+
</ogr:raster_sampling>
41+
</gml:featureMember>
42+
<gml:featureMember>
43+
<ogr:raster_sampling fid="raster_sampling.4">
44+
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>18.6812816537713,45.7964686432263</gml:coordinates></gml:Point></ogr:geometryProperty>
45+
<ogr:id>5</ogr:id>
46+
<ogr:dem_1>174.289291381836</ogr:dem_1>
47+
</ogr:raster_sampling>
48+
</gml:featureMember>
49+
<gml:featureMember>
50+
<ogr:raster_sampling fid="raster_sampling.5">
51+
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>18.6735615812008,45.7926086069411</gml:coordinates></gml:Point></ogr:geometryProperty>
52+
<ogr:id>6</ogr:id>
53+
<ogr:dem_1>93.1188354492188</ogr:dem_1>
54+
</ogr:raster_sampling>
55+
</gml:featureMember>
56+
</ogr:FeatureCollection>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="raster_sampling" type="ogr:raster_sampling_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="raster_sampling_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="geometryProperty" type="gml:PointPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
19+
<xs:element name="id" nillable="true" minOccurs="0" maxOccurs="1">
20+
<xs:simpleType>
21+
<xs:restriction base="xs:long">
22+
<xs:totalDigits value="10"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
<xs:element name="dem_1" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:decimal">
29+
</xs:restriction>
30+
</xs:simpleType>
31+
</xs:element>
32+
</xs:sequence>
33+
</xs:extension>
34+
</xs:complexContent>
35+
</xs:complexType>
36+
</xs:schema>

‎python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5728,4 +5728,21 @@ tests:
57285728
name: expected/kmeans_polys.gml
57295729
type: vector
57305730

5731+
- algorithm: qgis:rastersampling
5732+
name: Raster sampling, points single band
5733+
params:
5734+
INPUT:
5735+
name: custom/points_over.shp
5736+
type: vector
5737+
RASTERCOPY:
5738+
params:
5739+
- name: dem.tif
5740+
type: raster
5741+
type: multi
5742+
results:
5743+
OUTPUT:
5744+
name: expected/raster_sampling.gml
5745+
type: vector
5746+
5747+
57315748
# See ../README.md for a description of the file format

0 commit comments

Comments
 (0)
Please sign in to comment.