Skip to content
Permalink
Browse files

[FEATURE][processing] Topological coloring can have a minimum

distance between features assigned the same color set
  • Loading branch information
nyalldawson committed Feb 22, 2017
1 parent 5c43e0b commit 74abd5bbf63dab4c898c532f0b216050e4139959
@@ -547,6 +547,8 @@ qgis:texttofloat: >
qgis:topologicalcoloring: >
This algorithm assigns a color index to polygon features in such a way that no adjacent polygons share the same color index, whilst minimizing the number of colors required.

An optional minimum distance between features assigned the same color can be set to prevent nearby (but non-touching) features from being assigned equal colors.

The algorithm allows choice of method to use when assigning colors. The default method attempts to assign colors so that the count of features assigned to each individual color index is balanced.

The 'by assigned area' mode instead assigns colors so that the total area of features assigned to each color is balanced. This mode can be useful to help avoid large features resulting in one of the colors appearing more dominant on a colored map.
@@ -52,6 +52,7 @@
class TopoColor(GeoAlgorithm):
INPUT_LAYER = 'INPUT_LAYER'
MIN_COLORS = 'MIN_COLORS'
MIN_DISTANCE = 'MIN_DISTANCE'
BALANCE = 'BALANCE'
OUTPUT_LAYER = 'OUTPUT_LAYER'

@@ -64,6 +65,8 @@ def defineCharacteristics(self):
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(ParameterNumber(self.MIN_COLORS,
self.tr('Minimum number of colors'), 1, 1000, 4))
self.addParameter(ParameterNumber(self.MIN_DISTANCE,
self.tr('Minimum distance between features'), 0.0, 999999999.0, 0.0))
balance_by = [self.tr('By feature count'),
self.tr('By assigned area'),
self.tr('By distance between colors')]
@@ -79,6 +82,7 @@ def processAlgorithm(self, feedback):
self.getParameterValue(self.INPUT_LAYER))
min_colors = self.getParameterValue(self.MIN_COLORS)
balance_by = self.getParameterValue(self.BALANCE)
min_distance = self.getParameterValue(self.MIN_DISTANCE)

fields = layer.fields()
fields.append(QgsField('color_id', QVariant.Int))
@@ -91,7 +95,7 @@ def processAlgorithm(self, feedback):

features = {f.id(): f for f in vector.features(layer)}

topology, id_graph = self.compute_graph(features, feedback)
topology, id_graph = self.compute_graph(features, feedback, min_distance=min_distance)
feature_colors = ColoringAlgorithm.balanced(features,
balance=balance_by,
graph=topology,
@@ -119,7 +123,7 @@ def processAlgorithm(self, feedback):
del writer

@staticmethod
def compute_graph(features, feedback, create_id_graph=False):
def compute_graph(features, feedback, create_id_graph=False, min_distance=0):
""" compute topology from a layer/field """
s = Graph(sort_graph=False)
id_graph = None
@@ -134,10 +138,14 @@ def compute_graph(features, feedback, create_id_graph=False):

i = 0
for feature_id, f in features_with_geometry.items():
engine = QgsGeometry.createGeometryEngine(f.geometry().geometry())
g = f.geometry()
if min_distance > 0:
g = g.buffer(min_distance, 5)

engine = QgsGeometry.createGeometryEngine(g.geometry())
engine.prepareGeometry()

feature_bounds = f.geometry().boundingBox()
feature_bounds = g.boundingBox()
# grow bounds a little so we get touching features
feature_bounds.grow(feature_bounds.width() * 0.01)
intersections = index.intersects(feature_bounds)
@@ -0,0 +1,46 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>topocolor_polys_min_dist</Name>
<ElementPath>topocolor_polys_min_dist</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>11</FeatureCount>
<ExtentXMin>-0.76065</ExtentXMin>
<ExtentXMax>14.23935</ExtentXMax>
<ExtentYMin>-6.11331</ExtentYMin>
<ExtentYMax>5.88669</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>
<PropertyDefn>
<Name>color_id</Name>
<ElementPath>color_id</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-0.760650357995228</gml:X><gml:Y>-6.11330548926014</gml:Y></gml:coord>
<gml:coord><gml:X>14.2393496420048</gml:X><gml:Y>5.88669451073986</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-0.760650357995228,-0.113305489260142 2.23934964200477,-0.113305489260142 2.23934964200477,-3.11330548926014 -0.760650357995228,-3.11330548926014 -0.760650357995228,-0.113305489260142</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>-0.76065</ogr:left>
<ogr:top>-0.11331</ogr:top>
<ogr:right>2.23935</ogr:right>
<ogr:bottom>-3.11331</ogr:bottom>
<ogr:id>3</ogr:id>
<ogr:color_id>7</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.1">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-0.760650357995228,-3.11330548926014 2.23934964200477,-3.11330548926014 2.23934964200477,-6.11330548926014 -0.760650357995228,-6.11330548926014 -0.760650357995228,-3.11330548926014</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>-0.76065</ogr:left>
<ogr:top>-3.11331</ogr:top>
<ogr:right>2.23935</ogr:right>
<ogr:bottom>-6.11331</ogr:bottom>
<ogr:id>4</ogr:id>
<ogr:color_id>2</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.2">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5.23934964200477,-0.113305489260142 8.23934964200477,-0.113305489260142 8.23934964200477,-3.11330548926014 5.23934964200477,-3.11330548926014 5.23934964200477,-0.113305489260142</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>5.23935</ogr:left>
<ogr:top>-0.11331</ogr:top>
<ogr:right>8.23935</ogr:right>
<ogr:bottom>-3.11331</ogr:bottom>
<ogr:id>11</ogr:id>
<ogr:color_id>1</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5.23934964200477,-3.11330548926014 8.23934964200477,-3.11330548926014 8.23934964200477,-6.11330548926014 5.23934964200477,-6.11330548926014 5.23934964200477,-3.11330548926014</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>5.23935</ogr:left>
<ogr:top>-3.11331</ogr:top>
<ogr:right>8.23935</ogr:right>
<ogr:bottom>-6.11331</ogr:bottom>
<ogr:id>12</ogr:id>
<ogr:color_id>3</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.4">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>8.23934964200477,-3.11330548926014 11.2393496420048,-3.11330548926014 11.2393496420048,-6.11330548926014 8.23934964200477,-6.11330548926014 8.23934964200477,-3.11330548926014</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>8.23935</ogr:left>
<ogr:top>-3.11331</ogr:top>
<ogr:right>11.23935</ogr:right>
<ogr:bottom>-6.11331</ogr:bottom>
<ogr:id>16</ogr:id>
<ogr:color_id>5</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>11.2393496420048,-0.113305489260142 14.2393496420048,-0.113305489260142 14.2393496420048,-3.11330548926014 11.2393496420048,-3.11330548926014 11.2393496420048,-0.113305489260142</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>11.23935</ogr:left>
<ogr:top>-0.11331</ogr:top>
<ogr:right>14.23935</ogr:right>
<ogr:bottom>-3.11331</ogr:bottom>
<ogr:id>19</ogr:id>
<ogr:color_id>4</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.6">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>11.2393496420048,-3.11330548926014 14.2393496420048,-3.11330548926014 14.2393496420048,-6.11330548926014 11.2393496420048,-6.11330548926014 11.2393496420048,-3.11330548926014</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>11.23935</ogr:left>
<ogr:top>-3.11331</ogr:top>
<ogr:right>14.23935</ogr:right>
<ogr:bottom>-6.11331</ogr:bottom>
<ogr:id>20</ogr:id>
<ogr:color_id>7</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.7">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2.23934964200477,5.88669451073986 5.23934964200477,5.88669451073986 5.23934964200477,2.88669451073986 2.23934964200477,2.88669451073986 2.23934964200477,-0.113305489260142 -0.760650357995228,-0.113305489260142 -0.760650357995228,2.88669451073986 -0.760650357995228,5.88669451073986 2.23934964200477,5.88669451073986</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>2.23935</ogr:left>
<ogr:top>5.88669</ogr:top>
<ogr:right>5.23935</ogr:right>
<ogr:bottom>2.88669</ogr:bottom>
<ogr:id>5</ogr:id>
<ogr:color_id>3</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.8">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5.23934964200477,2.88669451073986 8.23934964200477,2.88669451073986 11.2393496420048,2.88669451073986 11.2393496420048,-0.113305489260142 11.2393496420048,-3.11330548926014 8.23934964200477,-3.11330548926014 8.23934964200477,-0.113305489260142 5.23934964200477,-0.113305489260142 5.23934964200477,2.88669451073986</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>5.23935</ogr:left>
<ogr:top>2.88669</ogr:top>
<ogr:right>8.23935</ogr:right>
<ogr:id>10</ogr:id>
<ogr:color_id>2</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.9">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2.23934964200477,2.88669451073986 5.23934964200477,2.88669451073986 5.23934964200477,-0.113305489260142 5.23934964200477,-3.11330548926014 5.23934964200477,-6.11330548926014 2.23934964200477,-6.11330548926014 2.23934964200477,-3.11330548926014 2.23934964200477,-0.113305489260142 2.23934964200477,2.88669451073986</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>2.23935</ogr:left>
<ogr:top>2.88669</ogr:top>
<ogr:right>5.23935</ogr:right>
<ogr:id>6</ogr:id>
<ogr:color_id>4</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
<gml:featureMember>
<ogr:topocolor_polys_min_dist fid="adjacent_polys.10">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5.23934964200477,5.88669451073986 8.23934964200477,5.88669451073986 11.2393496420048,5.88669451073986 14.2393496420048,5.88669451073986 14.2393496420048,2.88669451073986 14.2393496420048,-0.113305489260142 11.2393496420048,-0.113305489260142 11.2393496420048,2.88669451073986 8.23934964200477,2.88669451073986 5.23934964200477,2.88669451073986 5.23934964200477,5.88669451073986</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:left>5.23935</ogr:left>
<ogr:top>5.88669</ogr:top>
<ogr:right>8.23935</ogr:right>
<ogr:bottom>2.88669</ogr:bottom>
<ogr:id>9</ogr:id>
<ogr:color_id>6</ogr:color_id>
</ogr:topocolor_polys_min_dist>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -2385,3 +2385,16 @@ tests:
name: expected/topocolor_polys.gml
type: vector

- algorithm: qgis:topologicalcoloring
name: Topological coloring w/ min distance
params:
BALANCE: '0'
INPUT_LAYER:
name: custom/adjacent_polys.gml
type: vector
MIN_COLORS: 4
MIN_DISTANCE: 4.0
results:
OUTPUT_LAYER:
name: expected/topocolor_polys_min_dist.gml
type: vector

0 comments on commit 74abd5b

Please sign in to comment.
You can’t perform that action at this time.