Mapping surrounding shapes

Business logic may require plotting a shape and all surrounding shapes. This cannot be done directly in QlikView or Qlik Sense without special data modeling done int external tools.

The following document by Ujaval Gandhi was referenced for this document:

http://www.qgistutorials.com/en/docs/find_neighbor_polygons.html

The accompanying QVD can be found on the QlikMaps download site in the Neighbors.qvw.

The example below assumes the following business case:

One state needs to be selected. Since that state shares a border, I need to see it in the context of the states that border it.

Find the SHP

Open QGIS and load the layer by going to Layer -> Create Layer ->New Shape File Layer.

Be sure that WGS 84 is selected as the Coordinate Reference System in the box that follows.

Find the feature that neighbors will be evaluated from.

  1. Select the 'identity' tool and select a polygon. In the example below, the state of Wyoming has been selected.
  2. Take note of the top level Feature. In the example below, State is the top level feature.

Find polygon neighbors.

A python script is used to find polygon neighbors.

The following code is sourced from Ujaval Gandhi and the site listed in the article description. Slight modifications were made to the script to remove a feature initially used to Sum() a value in the attribute table. Since we are only interested in the neighbor information, all text for the Sum() function were removed.

  1. Copy the code below into a text editor and replace the NAME_FIELD line with the Top Level Feature from the previous step. In our example, NAME_FIELD is set to 'State'.
  2. In QGIS, go to Plugins --> Python console.
  3. Paste the contents of the updated text in to the bottom box of the python console after the '>>>'

The code will execute. Once you see 'Processing complete', right click on the layer in the Layer Dialogue Box to see Attribute Table with the new label 'Neighbor'.

################################################################################
# Copyright 2014 Ujaval Gandhi
# 
#This program is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public License
#as published by the Free Software Foundation; either version 2
#of the License, or (at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
################################################################################
from qgis.utils import iface
from PyQt4.QtCore import QVariant
# Replace the values below with values from your layer.
# For example, if your identifier field is called 'XYZ', then change the line
# below to _NAME_FIELD = 'XYZ'
_NAME_FIELD = 'State'
# Replace the value below with the field name that you want to sum up.
# For example, if the # field that you want to sum up is called 'VALUES', then
# change the line below to _SUM_FIELD = 'VALUES'
#_SUM_FIELD = 'ALAND'
# Names of the new fields to be added to the layer
_NEW_NEIGHBORS_FIELD = 'NEIGHBORS'
#_NEW_SUM_FIELD = 'SUM_POP'
layer = iface.activeLayer()
# Create 2 new fields in the layer that will hold the list of neighbors and sum
# of the chosen field.
layer.startEditing()
layer.dataProvider().addAttributes(
       [QgsField(_NEW_NEIGHBORS_FIELD, QVariant.String)])
  #       QgsField(_NEW_SUM_FIELD, QVariant.Int)])
layer.updateFields()
# Create a dictionary of all features
feature_dict = {f.id(): f for f in layer.getFeatures()}
# Build a spatial index
index = QgsSpatialIndex()
for f in feature_dict.values():
    index.insertFeature(f)
# Loop through all features and find features that touch each feature
for f in feature_dict.values():
    print 'Working on %s' % f[_NAME_FIELD]
    geom = f.geometry()
    # Find all features that intersect the bounding box of the current feature.
    # We use spatial index to find the features intersecting the bounding box
    # of the current feature. This will narrow down the features that we need
    # to check neighboring features.
    intersecting_ids = index.intersects(geom.boundingBox())
    # Initalize neighbors list and sum
    neighbors = []
   # neighbors_sum = 0
    for intersecting_id in intersecting_ids:
        # Look up the feature from the dictionary
        intersecting_f = feature_dict[intersecting_id]
        # For our purpose we consider a feature as 'neighbor' if it touches or
        # intersects a feature. We use the 'disjoint' predicate to satisfy
        # these conditions. So if a feature is not disjoint, it is a neighbor.
        if (f != intersecting_f and
            not intersecting_f.geometry().disjoint(geom)):
            neighbors.append(intersecting_f[_NAME_FIELD])
           # neighbors_sum += intersecting_f[_SUM_FIELD]
    f[_NEW_NEIGHBORS_FIELD] = ','.join(neighbors)
  #  f[_NEW_SUM_FIELD] = neighbors_sum
    # Update the layer with new attribute values.
    layer.updateFeature(f)
layer.commitChanges()
print 'Processing complete.'

Copy Attribute table to CSV

The Attribute Table now contains a comma separated list in the attribute table. This list can be exported and used as a mapping table in QlikView or Sense to build the associations between a selected Zip and neighboring Zips.

  1. Right click on the layer in the layers box and select Save As...
  2. Change the format to CSV.
  3. Change the separator to Semicolon. (Otherwise the ',' separated values in the Neighbor field will be considered new fields.)