Skip to content

Commit

Permalink
[FEATURE][processing] Add overlay option to GridLine and GridPolygon
Browse files Browse the repository at this point in the history
  • Loading branch information
lbartoletti authored and nyalldawson committed Jan 19, 2017
1 parent 1f3c67f commit 01cd784
Show file tree
Hide file tree
Showing 11 changed files with 1,443 additions and 41 deletions.
26 changes: 24 additions & 2 deletions python/plugins/processing/algs/qgis/GridLine.py
Expand Up @@ -54,6 +54,8 @@ class GridLine(GeoAlgorithm):
EXTENT = 'EXTENT'
HSPACING = 'HSPACING'
VSPACING = 'VSPACING'
HOVERLAY = 'HOVERLAY'
VOVERLAY = 'VOVERLAY'
CRS = 'CRS'
OUTPUT = 'OUTPUT'

Expand All @@ -71,6 +73,10 @@ def defineCharacteristics(self):
self.tr('Horizontal spacing'), 0.0, 1000000000.0, default=0.0001))
self.addParameter(ParameterNumber(self.VSPACING,
self.tr('Vertical spacing'), 0.0, 1000000000.0, default=0.0001))
self.addParameter(ParameterNumber(self.HOVERLAY,
self.tr('Horizontal overlay'), 0.0, 1000000000.0, default=0.0))
self.addParameter(ParameterNumber(self.VOVERLAY,
self.tr('Vertical overlay'), 0.0, 1000000000.0, default=0.0))
self.addParameter(ParameterCrs(self.CRS, 'Grid CRS', 'EPSG:4326'))

self.addOutput(OutputVector(self.OUTPUT, self.tr('Grid'), datatype=[dataobjects.TYPE_VECTOR_LINE]))
Expand All @@ -79,6 +85,8 @@ def processAlgorithm(self, feedback):
extent = self.getParameterValue(self.EXTENT).split(',')
hSpacing = self.getParameterValue(self.HSPACING)
vSpacing = self.getParameterValue(self.VSPACING)
hOverlay = self.getParameterValue(self.HOVERLAY)
vOverlay = self.getParameterValue(self.VOVERLAY)
crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS))

bbox = QgsRectangle(float(extent[0]), float(extent[2]),
Expand All @@ -91,6 +99,10 @@ def processAlgorithm(self, feedback):
raise GeoAlgorithmExecutionException(
self.tr('Invalid grid spacing: %s/%s' % (hSpacing, vSpacing)))

if hSpacing <= hOverlay or vSpacing <= vOverlay:
raise GeoAlgorithmExecutionException(
self.tr('Invalid overlay: %s/%s' % (hOverlay, vOverlay)))

if width < hSpacing:
raise GeoAlgorithmExecutionException(
self.tr('Horizontal spacing is too small for the covered area'))
Expand All @@ -110,6 +122,16 @@ def processAlgorithm(self, feedback):
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields,
QgsWkbTypes.LineString, crs)

if hOverlay > 0:
hSpace = [hSpacing - hOverlay, hOverlay]
else:
hSpace = [hSpacing, hSpacing]

if vOverlay > 0:
vSpace = [vSpacing - vOverlay, vOverlay]
else:
vSpace = [vSpacing, vSpacing]

feat = QgsFeature()
feat.initAttributes(len(fields))

Expand All @@ -133,7 +155,7 @@ def processAlgorithm(self, feedback):
id,
y])
writer.addFeature(feat)
y = y - vSpacing
y = y - vSpace[count % 2]
id += 1
count += 1
if int(math.fmod(count, count_update)) == 0:
Expand All @@ -160,7 +182,7 @@ def processAlgorithm(self, feedback):
id,
x])
writer.addFeature(feat)
x = x + hSpacing
x = x + hSpace[count % 2]
id += 1
count += 1
if int(math.fmod(count, count_update)) == 0:
Expand Down
103 changes: 64 additions & 39 deletions python/plugins/processing/algs/qgis/GridPolygon.py
Expand Up @@ -48,6 +48,8 @@ class GridPolygon(GeoAlgorithm):
EXTENT = 'EXTENT'
HSPACING = 'HSPACING'
VSPACING = 'VSPACING'
HOVERLAY = 'HOVERLAY'
VOVERLAY = 'VOVERLAY'
CRS = 'CRS'
OUTPUT = 'OUTPUT'

Expand All @@ -71,6 +73,10 @@ def defineCharacteristics(self):
self.tr('Horizontal spacing'), 0.0, 1000000000.0, 0.0001))
self.addParameter(ParameterNumber(self.VSPACING,
self.tr('Vertical spacing'), 0.0, 1000000000.0, 0.0001))
self.addParameter(ParameterNumber(self.HOVERLAY,
self.tr('Horizontal overlay'), 0.0, 1000000000.0, 0.0))
self.addParameter(ParameterNumber(self.VOVERLAY,
self.tr('Vertical overlay'), 0.0, 1000000000.0, 0.0))
self.addParameter(ParameterCrs(self.CRS, 'Grid CRS', 'EPSG:4326'))

self.addOutput(OutputVector(self.OUTPUT, self.tr('Grid'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
Expand All @@ -80,6 +86,8 @@ def processAlgorithm(self, feedback):
extent = self.getParameterValue(self.EXTENT).split(',')
hSpacing = self.getParameterValue(self.HSPACING)
vSpacing = self.getParameterValue(self.VSPACING)
hOverlay = self.getParameterValue(self.HOVERLAY)
vOverlay = self.getParameterValue(self.VOVERLAY)
crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS))

bbox = QgsRectangle(float(extent[0]), float(extent[2]),
Expand All @@ -98,6 +106,10 @@ def processAlgorithm(self, feedback):
raise GeoAlgorithmExecutionException(
self.tr('Horizontal spacing is too small for the covered area'))

if hSpacing <= hOverlay or vSpacing <= vOverlay:
raise GeoAlgorithmExecutionException(
self.tr('Invalid overlay: %s/%s' % (hOverlay, vOverlay)))

if height < vSpacing:
raise GeoAlgorithmExecutionException(
self.tr('Vertical spacing is too small for the covered area'))
Expand All @@ -114,37 +126,36 @@ def processAlgorithm(self, feedback):

if idx == 0:
self._rectangleGrid(
writer, width, height, originX, originY, hSpacing, vSpacing, feedback)
writer, width, height, originX, originY, hSpacing, vSpacing, hOverlay, vOverlay, feedback)
elif idx == 1:
self._diamondGrid(
writer, width, height, originX, originY, hSpacing, vSpacing, feedback)
writer, width, height, originX, originY, hSpacing, vSpacing, hOverlay, vOverlay, feedback)
elif idx == 2:
self._hexagonGrid(
writer, width, height, originX, originY, hSpacing, vSpacing, feedback)
writer, width, height, originX, originY, hSpacing, vSpacing, hOverlay, vOverlay, feedback)

del writer

def _rectangleGrid(self, writer, width, height, originX, originY,
hSpacing, vSpacing, feedback):
hSpacing, vSpacing, hOverlay, vOverlay, feedback):
ft = QgsFeature()

columns = int(math.ceil(float(width) / hSpacing))
rows = int(math.ceil(float(height) / vSpacing))
columns = int(math.ceil(float(width) / (hSpacing-hOverlay)))
rows = int(math.ceil(float(height) / (vSpacing-vOverlay)))

cells = rows * columns
count_update = cells * 0.05

id = 1
count = 0

for col in range(columns):
# (column + 1) and (row + 1) calculation is used to maintain
# topology between adjacent shapes and avoid overlaps/holes
# due to rounding errors
x1 = originX + (col * hSpacing)
x2 = originX + ((col + 1) * hSpacing)
x1 = originX + (col * hSpacing - col * hOverlay)
x2 = x1 + hSpacing

for row in range(rows):
y1 = originY - (row * vSpacing)
y2 = originY - ((row + 1) * vSpacing)
y1 = originY - (row * vSpacing - row * vOverlay)
y2 = y1 - vSpacing

polyline = []
polyline.append(QgsPoint(x1, y1))
Expand All @@ -156,20 +167,24 @@ def _rectangleGrid(self, writer, width, height, originX, originY,
ft.setGeometry(QgsGeometry.fromPolygon([polyline]))
ft.setAttributes([x1, y1, x2, y2, id])
writer.addFeature(ft)

id += 1
count += 1
if int(math.fmod(count, count_update)) == 0:
feedback.setProgress(int(count / cells * 100))

def _diamondGrid(self, writer, width, height, originX, originY,
hSpacing, vSpacing, feedback):
hSpacing, vSpacing, hOverlay, vOverlay, feedback):
ft = QgsFeature()

halfHSpacing = hSpacing / 2
halfVSpacing = vSpacing / 2

columns = int(math.ceil(float(width) / halfHSpacing))
rows = int(math.ceil(float(height) / vSpacing))
halfHOverlay = hOverlay / 2
halfVOverlay = vOverlay / 2

columns = int(math.ceil(float(width) / (halfHSpacing - halfHOverlay)))
rows = int(math.ceil(float(height) / (vSpacing - halfVOverlay)))

cells = rows * columns
count_update = cells * 0.05
Expand All @@ -178,19 +193,21 @@ def _diamondGrid(self, writer, width, height, originX, originY,
count = 0

for col in range(columns):
x1 = originX + ((col + 0) * halfHSpacing)
x2 = originX + ((col + 1) * halfHSpacing)
x3 = originX + ((col + 2) * halfHSpacing)
x = originX - (col * halfHOverlay)
x1 = x + ((col + 0) * halfHSpacing)
x2 = x + ((col + 1) * halfHSpacing)
x3 = x + ((col + 2) * halfHSpacing)

for row in range(rows):
y = originY + (row * halfVOverlay)
if (col % 2) == 0:
y1 = originY - (((row * 2) + 0) * halfVSpacing)
y2 = originY - (((row * 2) + 1) * halfVSpacing)
y3 = originY - (((row * 2) + 2) * halfVSpacing)
y1 = y - (((row * 2) + 0) * halfVSpacing)
y2 = y - (((row * 2) + 1) * halfVSpacing)
y3 = y - (((row * 2) + 2) * halfVSpacing)
else:
y1 = originY - (((row * 2) + 1) * halfVSpacing)
y2 = originY - (((row * 2) + 2) * halfVSpacing)
y3 = originY - (((row * 2) + 3) * halfVSpacing)
y1 = y - (((row * 2) + 1) * halfVSpacing)
y2 = y - (((row * 2) + 2) * halfVSpacing)
y3 = y - (((row * 2) + 3) * halfVSpacing)

polyline = []
polyline.append(QgsPoint(x1, y2))
Expand All @@ -208,18 +225,26 @@ def _diamondGrid(self, writer, width, height, originX, originY,
feedback.setProgress(int(count / cells * 100))

def _hexagonGrid(self, writer, width, height, originX, originY,
hSpacing, vSpacing, feedback):
hSpacing, vSpacing, hOverlay, vOverlay, feedback):
ft = QgsFeature()

# To preserve symmetry, hspacing is fixed relative to vspacing
xVertexLo = 0.288675134594813 * vSpacing
xVertexHi = 0.577350269189626 * vSpacing
hSpacing = xVertexLo + xVertexHi

halfVSpacing = vSpacing / 2
hOverlay = hSpacing - hOverlay
if hOverlay < 0:
raise GeoAlgorithmExecutionException(
self.tr('To preserve symmetry, hspacing is fixed relative to vspacing\n \
hspacing is fixed at: %s and hoverlay is fixed at: %s\n \
hoverlay cannot be negative. Increase hoverlay.' % (hSpacing, hOverlay)))

halfHSpacing = hSpacing / 2.0
halfVSpacing = vSpacing / 2.0

columns = int(math.ceil(float(width) / hSpacing))
rows = int(math.ceil(float(height) / vSpacing))
columns = int(math.ceil(float(width) / hOverlay))
rows = int(math.ceil(float(height) / (vSpacing - vOverlay)))

cells = rows * columns
count_update = cells * 0.05
Expand All @@ -231,20 +256,20 @@ def _hexagonGrid(self, writer, width, height, originX, originY,
# (column + 1) and (row + 1) calculation is used to maintain
# topology between adjacent shapes and avoid overlaps/holes
# due to rounding errors
x1 = originX + (col * hSpacing) # far left
x2 = x1 + (xVertexHi - xVertexLo) # left
x3 = originX + ((col + 1) * hSpacing) # right
x4 = x3 + (xVertexHi - xVertexLo) # far right
x1 = originX + (col * hOverlay) # far left
x2 = x1 + (xVertexHi - xVertexLo) # left
x3 = originX + (col * hOverlay) + hSpacing # right
x4 = x3 + (xVertexHi - xVertexLo) # far right

for row in range(rows):
if (col % 2) == 0:
y1 = originY - (((row * 2) + 0) * halfVSpacing) # hi
y2 = originY - (((row * 2) + 1) * halfVSpacing) # mid
y3 = originY - (((row * 2) + 2) * halfVSpacing) # lo
y1 = originY + (row * vOverlay) - (((row * 2) + 0) * halfVSpacing) # hi
y2 = originY + (row * vOverlay) - (((row * 2) + 1) * halfVSpacing) # mid
y3 = originY + (row * vOverlay) - (((row * 2) + 2) * halfVSpacing) # lo
else:
y1 = originY - (((row * 2) + 1) * halfVSpacing) # hi
y2 = originY - (((row * 2) + 2) * halfVSpacing) # mid
y3 = originY - (((row * 2) + 3) * halfVSpacing) # lo
y1 = originY + (row * vOverlay) - (((row * 2) + 1) * halfVSpacing) # hi
y2 = originY + (row * vOverlay) - (((row * 2) + 2) * halfVSpacing) # mid
y3 = originY + (row * vOverlay) - (((row * 2) + 3) * halfVSpacing) # lo

polyline = []
polyline.append(QgsPoint(x1, y2))
Expand Down
@@ -0,0 +1,41 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>grid_diamond_overlay</Name>
<ElementPath>grid_diamond_overlay</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>45</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>16.00000</ExtentXMax>
<ExtentYMin>-8.00000</ExtentYMin>
<ExtentYMax>6.50000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>left</Name>
<ElementPath>left</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>top</Name>
<ElementPath>top</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>right</Name>
<ElementPath>right</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>bottom</Name>
<ElementPath>bottom</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>id</Name>
<ElementPath>id</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>

0 comments on commit 01cd784

Please sign in to comment.