Skip to content

Commit a72485c

Browse files
authored
Merge pull request #3380 from nyalldawson/processing
[processing] Offset curve follow ups
2 parents 8a31576 + 5025c82 commit a72485c

18 files changed

+659
-32
lines changed

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

+8
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,14 @@ qgis:fieldcalculator: >
167167
qgis:fixeddistancebuffer: >
168168
This algorithm computes a buffer area for all the features in an input layer, using a fixed distance.
169169

170+
The segments parameter controls the number of line segments to use to approximate a quarter circle when creating rounded offsets.
171+
172+
The end cap style parameter controls how line endings are handled in the buffer.
173+
174+
The join style parameter specifies whether round, mitre or beveled joins should be used when offseting corners in a line.
175+
176+
The mitre limit parameter is only applicable for mitre join styles, and controls the maximum distance from the offset curve to use when creating a mitred join.
177+
170178
qgis:frequencyanalysis: >
171179
This algorithms generates a table with frequency analysis of the values of a selected attribute from an input vector layer
172180

python/plugins/processing/algs/qgis/Buffer.py

+12-18
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,12 @@
3232

3333

3434
def buffering(progress, writer, distance, field, useField, layer, dissolve,
35-
segments):
35+
segments, endCapStyle=1, joinStyle=1, mitreLimit=2):
3636

3737
if useField:
3838
field = layer.fieldNameIndex(field)
3939

4040
outFeat = QgsFeature()
41-
inFeat = QgsFeature()
42-
inGeom = QgsGeometry()
43-
outGeom = QgsGeometry()
4441

4542
current = 0
4643
features = vector.features(layer)
@@ -49,6 +46,7 @@ def buffering(progress, writer, distance, field, useField, layer, dissolve,
4946
# With dissolve
5047
if dissolve:
5148
first = True
49+
buffered_geometries = []
5250
for inFeat in features:
5351
attrs = inFeat.attributes()
5452
if useField:
@@ -57,23 +55,19 @@ def buffering(progress, writer, distance, field, useField, layer, dissolve,
5755
value = distance
5856

5957
inGeom = inFeat.geometry()
60-
if inGeom.isEmpty() or inGeom.isGeosEmpty():
58+
if not inGeom:
6159
ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has empty geometry. Skipping...'.format(inFeat.id()))
6260
continue
6361
if not inGeom.isGeosValid():
6462
ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has invalid geometry. Skipping...'.format(inFeat.id()))
6563
continue
66-
outGeom = inGeom.buffer(float(value), segments)
67-
if first:
68-
tempGeom = QgsGeometry(outGeom)
69-
first = False
70-
else:
71-
tempGeom = tempGeom.combine(outGeom)
64+
buffered_geometries.append(inGeom.buffer(float(value), segments, endCapStyle, joinStyle, mitreLimit))
7265

7366
current += 1
7467
progress.setPercentage(int(current * total))
7568

76-
outFeat.setGeometry(tempGeom)
69+
final_geometry = QgsGeometry.unaryUnion(buffered_geometries)
70+
outFeat.setGeometry(final_geometry)
7771
outFeat.setAttributes(attrs)
7872
writer.addFeature(outFeat)
7973
else:
@@ -85,15 +79,15 @@ def buffering(progress, writer, distance, field, useField, layer, dissolve,
8579
else:
8680
value = distance
8781
inGeom = inFeat.geometry()
82+
outFeat = QgsFeature()
8883
if inGeom.isEmpty() or inGeom.isGeosEmpty():
89-
ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has empty geometry. Skipping...'.format(inFeat.id()))
90-
continue
91-
if not inGeom.isGeosValid():
84+
pass
85+
elif not inGeom.isGeosValid():
9286
ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, 'Feature {} has invalid geometry. Skipping...'.format(inFeat.id()))
9387
continue
94-
95-
outGeom = inGeom.buffer(float(value), segments)
96-
outFeat.setGeometry(outGeom)
88+
else:
89+
outGeom = inGeom.buffer(float(value), segments, endCapStyle, joinStyle, mitreLimit)
90+
outFeat.setGeometry(outGeom)
9791
outFeat.setAttributes(attrs)
9892
writer.addFeature(outFeat)
9993
current += 1

python/plugins/processing/algs/qgis/FixedDistanceBuffer.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
from processing.core.parameters import ParameterVector
3636
from processing.core.parameters import ParameterBoolean
3737
from processing.core.parameters import ParameterNumber
38+
from processing.core.parameters import ParameterSelection
39+
3840
from processing.core.outputs import OutputVector
3941
from . import Buffer as buff
4042
from processing.tools import dataobjects
@@ -50,6 +52,9 @@ class FixedDistanceBuffer(GeoAlgorithm):
5052
DISTANCE = 'DISTANCE'
5153
SEGMENTS = 'SEGMENTS'
5254
DISSOLVE = 'DISSOLVE'
55+
END_CAP_STYLE = 'END_CAP_STYLE'
56+
JOIN_STYLE = 'JOIN_STYLE'
57+
MITRE_LIMIT = 'MITRE_LIMIT'
5358

5459
def getIcon(self):
5560
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'buffer.png'))
@@ -65,6 +70,22 @@ def defineCharacteristics(self):
6570
self.tr('Segments'), 1, default=5))
6671
self.addParameter(ParameterBoolean(self.DISSOLVE,
6772
self.tr('Dissolve result'), False))
73+
self.end_cap_styles = [self.tr('Round'),
74+
'Flat',
75+
'Square']
76+
self.addParameter(ParameterSelection(
77+
self.END_CAP_STYLE,
78+
self.tr('End cap style'),
79+
self.end_cap_styles, default=0))
80+
self.join_styles = [self.tr('Round'),
81+
'Mitre',
82+
'Bevel']
83+
self.addParameter(ParameterSelection(
84+
self.JOIN_STYLE,
85+
self.tr('Join style'),
86+
self.join_styles, default=0))
87+
self.addParameter(ParameterNumber(self.MITRE_LIMIT,
88+
self.tr('Mitre limit'), 1, default=2))
6889

6990
self.addOutput(OutputVector(self.OUTPUT, self.tr('Buffer')))
7091

@@ -73,10 +94,13 @@ def processAlgorithm(self, progress):
7394
distance = self.getParameterValue(self.DISTANCE)
7495
dissolve = self.getParameterValue(self.DISSOLVE)
7596
segments = int(self.getParameterValue(self.SEGMENTS))
97+
end_cap_style = self.getParameterValue(self.END_CAP_STYLE) + 1
98+
join_style = self.getParameterValue(self.JOIN_STYLE) + 1
99+
miter_limit = self.getParameterValue(self.MITRE_LIMIT)
76100

77101
writer = self.getOutputFromName(
78102
self.OUTPUT).getVectorWriter(layer.fields().toList(),
79103
QgsWkbTypes.Polygon, layer.crs())
80104

81105
buff.buffering(progress, writer, distance, None, False, layer,
82-
dissolve, segments)
106+
dissolve, segments, end_cap_style, join_style, miter_limit)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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/ buffer_lines.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>-2</gml:X><gml:Y>-4</gml:Y></gml:coord>
10+
<gml:coord><gml:X>11.98768834059514</gml:X><gml:Y>5.987688340595138</gml:Y></gml:coord>
11+
</gml:Box>
12+
</gml:boundedBy>
13+
14+
<gml:featureMember>
15+
<ogr:buffer_lines fid="lines.0">
16+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>8,3 8.034074173710932,3.258819045102521 8.133974596215561,3.5 8.292893218813452,3.707106781186547 10.292893218813452,5.707106781186548 10.546009500260457,5.891006524188368 10.843565534959772,5.987688340595138 11.156434465040231,5.987688340595137 11.453990499739549,5.891006524188367 11.707106781186548,5.707106781186548 11.891006524188368,5.453990499739547 11.987688340595138,5.156434465040232 11.987688340595138,4.843565534959771 11.891006524188368,4.546009500260455 11.70710678118655,4.292893218813454 10.0,2.585786437626905 10,2 9.951056516295154,1.690983005625053 9.809016994374948,1.412214747707527 9.587785252292472,1.190983005625053 9.309016994374948,1.048943483704846 9,1 6,1 5.690983005625051,1.048943483704847 5.412214747707526,1.190983005625053 5.190983005625052,1.412214747707528 5.048943483704846,1.690983005625053 5,2 5.048943483704846,2.309016994374947 5.190983005625052,2.587785252292472 5.412214747707526,2.809016994374947 5.690983005625051,2.951056516295153 6,3 8,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
17+
</ogr:buffer_lines>
18+
</gml:featureMember>
19+
<gml:featureMember>
20+
<ogr:buffer_lines fid="lines.1">
21+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,0 1.309016994374949,-0.048943483704847 1.587785252292474,-0.190983005625053 1.809016994374948,-0.412214747707527 1.951056516295154,-0.690983005625053 2,-1 1.951056516295154,-1.309016994374947 1.809016994374948,-1.587785252292472 1.587785252292474,-1.809016994374947 1.309016994374949,-1.951056516295153 1,-2 -1,-2 -1.309016994374949,-1.951056516295153 -1.587785252292474,-1.809016994374947 -1.809016994374948,-1.587785252292472 -1.951056516295154,-1.309016994374947 -2,-1 -1.951056516295154,-0.690983005625053 -1.809016994374948,-0.412214747707528 -1.587785252292474,-0.190983005625053 -1.309016994374949,-0.048943483704847 -1,0 1,0</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
22+
</ogr:buffer_lines>
23+
</gml:featureMember>
24+
<gml:featureMember>
25+
<ogr:buffer_lines fid="lines.2">
26+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,2 1.048943483704846,2.309016994374947 1.190983005625053,2.587785252292473 1.412214747707527,2.809016994374947 1.690983005625053,2.951056516295154 2,3 2.048943483704847,3.309016994374949 2.190983005625053,3.587785252292474 2.412214747707528,3.809016994374948 2.690983005625053,3.951056516295154 3,4 3.309016994374947,3.951056516295154 3.587785252292472,3.809016994374948 3.809016994374947,3.587785252292474 3.951056516295153,3.309016994374949 4,3 4,2 3.951056516295154,1.690983005625053 3.809016994374947,1.412214747707527 3.587785252292473,1.190983005625053 3.309016994374947,1.048943483704846 3,1 3,0 2.951056516295153,-0.309016994374949 2.809016994374947,-0.587785252292474 2.587785252292472,-0.809016994374948 2.309016994374947,-0.951056516295154 2,-1 1.690983005625053,-0.951056516295154 1.412214747707528,-0.809016994374948 1.190983005625054,-0.587785252292475 1.048943483704847,-0.309016994374949 1,0 1,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
27+
</ogr:buffer_lines>
28+
</gml:featureMember>
29+
<gml:featureMember>
30+
<ogr:buffer_lines fid="lines.3">
31+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,2 5.309016994374948,1.951056516295153 5.587785252292474,1.809016994374947 5.809016994374947,1.587785252292473 5.951056516295154,1.309016994374947 6,1 5.951056516295154,0.690983005625053 5.809016994374948,0.412214747707528 5.587785252292474,0.190983005625054 5.309016994374949,0.048943483704847 5,0 3,0 2.690983005625051,0.048943483704847 2.412214747707526,0.190983005625053 2.190983005625052,0.412214747707528 2.048943483704846,0.690983005625053 2,1 2.048943483704846,1.309016994374947 2.190983005625052,1.587785252292472 2.412214747707526,1.809016994374947 2.690983005625051,1.951056516295153 3,2 5,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
32+
</ogr:buffer_lines>
33+
</gml:featureMember>
34+
<gml:featureMember>
35+
<ogr:buffer_lines fid="lines.4">
36+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>10,-2 10.309016994374948,-2.048943483704847 10.587785252292472,-2.190983005625053 10.809016994374948,-2.412214747707528 10.951056516295154,-2.690983005625053 11,-3 10.951056516295154,-3.309016994374947 10.809016994374948,-3.587785252292472 10.587785252292475,-3.809016994374947 10.30901699437495,-3.951056516295153 10,-4 7,-4 6.690983005625051,-3.951056516295153 6.412214747707526,-3.809016994374947 6.190983005625052,-3.587785252292472 6.048943483704846,-3.309016994374947 6,-3 6.048943483704846,-2.690983005625053 6.190983005625052,-2.412214747707528 6.412214747707526,-2.190983005625053 6.690983005625051,-2.048943483704847 7,-2 10,-2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
37+
</ogr:buffer_lines>
38+
</gml:featureMember>
39+
<gml:featureMember>
40+
<ogr:buffer_lines fid="lines.5">
41+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>9.292893218813452,1.707106781186547 9.546009500260457,1.891006524188368 9.843565534959772,1.987688340595138 10.156434465040231,1.987688340595138 10.453990499739549,1.891006524188368 10.707106781186548,1.707106781186547 10.891006524188368,1.453990499739547 10.987688340595138,1.156434465040232 10.987688340595138,0.84356553495977 10.891006524188368,0.546009500260455 10.70710678118655,0.292893218813454 6.707106781186548,-3.707106781186547 6.453990499739546,-3.891006524188368 6.15643446504023,-3.987688340595138 5.843565534959769,-3.987688340595138 5.546009500260453,-3.891006524188368 5.292893218813452,-3.707106781186547 5.108993475811633,-3.453990499739548 5.012311659404863,-3.156434465040232 5.012311659404862,-2.843565534959771 5.108993475811631,-2.546009500260455 5.29289321881345,-2.292893218813454 9.292893218813452,1.707106781186547</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
42+
</ogr:buffer_lines>
43+
</gml:featureMember>
44+
<gml:featureMember>
45+
<ogr:buffer_lines fid="lines.6">
46+
</ogr:buffer_lines>
47+
</gml:featureMember>
48+
</ogr:FeatureCollection>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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="buffer_lines" type="ogr:buffer_lines_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="buffer_lines_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="geometryProperty" type="gml:PolygonPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
19+
</xs:sequence>
20+
</xs:extension>
21+
</xs:complexContent>
22+
</xs:complexType>
23+
</xs:schema>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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/ buffer_lines_flat.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>-1</gml:X><gml:Y>-4</gml:Y></gml:coord>
10+
<gml:coord><gml:X>11.70710678118655</gml:X><gml:Y>5.707106781186548</gml:Y></gml:coord>
11+
</gml:Box>
12+
</gml:boundedBy>
13+
14+
<gml:featureMember>
15+
<ogr:buffer_lines_flat fid="lines.0">
16+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>8,3 8.034074173710932,3.258819045102521 8.133974596215561,3.5 8.292893218813452,3.707106781186547 10.292893218813452,5.707106781186548 11.707106781186548,4.292893218813452 10.0,2.585786437626905 10,2 9.951056516295154,1.690983005625053 9.809016994374948,1.412214747707527 9.587785252292472,1.190983005625053 9.309016994374948,1.048943483704846 9,1 6,1 6,3 8,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
17+
</ogr:buffer_lines_flat>
18+
</gml:featureMember>
19+
<gml:featureMember>
20+
<ogr:buffer_lines_flat fid="lines.1">
21+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,0 1,-2 -1,-2 -1,0 1,0</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
22+
</ogr:buffer_lines_flat>
23+
</gml:featureMember>
24+
<gml:featureMember>
25+
<ogr:buffer_lines_flat fid="lines.2">
26+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,2 1.048943483704846,2.309016994374947 1.190983005625053,2.587785252292473 1.412214747707527,2.809016994374947 1.690983005625053,2.951056516295154 2,3 4,3 4,2 3.951056516295154,1.690983005625053 3.809016994374947,1.412214747707527 3.587785252292473,1.190983005625053 3.309016994374947,1.048943483704846 3,1 3,0 1,0 1,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
27+
</ogr:buffer_lines_flat>
28+
</gml:featureMember>
29+
<gml:featureMember>
30+
<ogr:buffer_lines_flat fid="lines.3">
31+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,2 5,0 3,0 3,2 5,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
32+
</ogr:buffer_lines_flat>
33+
</gml:featureMember>
34+
<gml:featureMember>
35+
<ogr:buffer_lines_flat fid="lines.4">
36+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>10,-2 10,-4 7,-4 7,-2 10,-2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
37+
</ogr:buffer_lines_flat>
38+
</gml:featureMember>
39+
<gml:featureMember>
40+
<ogr:buffer_lines_flat fid="lines.5">
41+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>9.292893218813452,1.707106781186547 10.707106781186548,0.292893218813453 6.707106781186548,-3.707106781186547 5.292893218813452,-2.292893218813453 9.292893218813452,1.707106781186547</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
42+
</ogr:buffer_lines_flat>
43+
</gml:featureMember>
44+
<gml:featureMember>
45+
<ogr:buffer_lines_flat fid="lines.6">
46+
</ogr:buffer_lines_flat>
47+
</gml:featureMember>
48+
</ogr:FeatureCollection>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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="buffer_lines_flat" type="ogr:buffer_lines_flat_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="buffer_lines_flat_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="geometryProperty" type="gml:PolygonPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
19+
</xs:sequence>
20+
</xs:extension>
21+
</xs:complexContent>
22+
</xs:complexType>
23+
</xs:schema>

0 commit comments

Comments
 (0)