I am working on a map of ethnic groups and I am looking to do two closely-related things by joining attributes from points to polygon.

1) I would like to assign the mode of (string) values of points in a given polygon to that polygon.

2) I would like to weight each point by population to determine the majority point based on the string value with the most population in each polygon.

Example map


1) I'd like the location with more "red" than "green" dots to assign a "red" value to the polygon.

2) For a polygon in which "purple" is only one dot among many "red" and "green" dots, but the "purple" occupy a city of 10,000 and the others only occupy small villages, I would like the polygon to assign a "purple" value.

  • 1
    What does your point data look like? Does it have a population for each ethnicity? or just a dominate ethnicity and city population? Jan 27 '17 at 20:36
  • @JesseMcMillan, thanks for the question. For the purposes of this project, each point is effectively a single ethnicity with a population. This means the question is basically--if "red" has three villages of 100, "green" has two villages of 200, and "purple" has a city of 10,000 I want to know how to assign 1) polygon that is red for most localities and 2) polygon that is purple for most people.
    – arabiccode
    Jan 27 '17 at 20:41
  • So you have attributes for 'ethnicity' and 'population' attached to each point feature then, correct? Jan 27 '17 at 20:46
  • Correct. Each point has population numbers from an old census, but ethnicity is not counted for the area in question so the ethnicity value considers the dominant group. It is more about political power than demographics, so the calculation only needs to consider the differents points within the polygon, but not the differences within the points.
    – arabiccode
    Jan 27 '17 at 21:01
  • did the python solution below work for you? Jan 29 '17 at 1:48

I would use python. There may be a way to accomplish this with a combination of 'join attributes by location' and field calculator, but I don't know of one.

So assuming each point has attributes 'ethnicity' and 'population' and the polygon layer has an attribute field 'main_eth' we can write to:

# add correct names for your layers
pointLyr = QgsMapLayerRegistry.instance().mapLayersByName("LYRNAME")[0]
polyLyr = QgsMapLayerRegistry.instance().mapLayersByName("LYRNAME")[0]

# add correct field names
ethField = pointLyr.fieldNameIndex('ethnicity')
popField = pointLyr.fieldNameIndex('population')
destField = polyLyr.fieldNameIndex('main_eth')

# start editing

for poly in polyLyr.getFeatures():
    # dictionary to store totals for each ethnic group
    ethDict = {}

    # for speed, let's only get points within bounding box first
    request = QgsFeatureRequest().setFilterRect(poly.geometry().boundingBox())

    for pt in pointLyr.getFeatures(request):
        # check if it's actually within feature
        if poly.geometry().contains(pt.geometry()):
            # set value or add to value if exists for given eth.
            if pt[ethField] in ethDict:
                ethDict[pt[ethField]] += pt[popField]
                ethDict[pt[ethField]] = pt[popField]
    # get main eth from dict and save to feature attribute
    # default value in case no result found
    maxEth = 'unknown'
    if ethDict:
        maxEth = max(ethDict.iterkeys(), key=(lambda key: ethDict[key])) 
    polyLyr.changeAttributeValue(poly.id(), destField, maxEth)

# stop editing

Basically this finds all the points in a given polygon, and totals population by ethnicity then saves the ethnicity with the biggest total population to a field in the polygon layer.

Then you can use rule-based styling based on the resulting field.

  • I have worked through this code to take care of some issues, but I'm stuck on this: ethDict[pt[ethField]] += pt[popField] Traceback (most recent call last): File "<input>", line 11, in <module> TypeError: unsupported operand type(s) for +=: 'int' and 'QPyNullVariant' # stop editing
    – arabiccode
    Jan 31 '17 at 19:44
  • You likely have some features with Null in the population column. One option would be to resolve that, or we could add some code to use a default population value when null. If you want to use that option, what would be your default? Jan 31 '17 at 19:48
  • Thanks, I filled in Null as 0. Now I'm on this Traceback (most recent call last): File "<input>", line 15, in <module> ValueError: max() arg is an empty sequence # stop editing
    – arabiccode
    Jan 31 '17 at 19:52
  • I added ` if controlDict.iterkeys > 0: maxCtrl = max(controlDict.iterkeys(), key=(lambda key: controlDict[key])) polyLyr.changeAttributeValue(poly.id(), destField, maxCtrl)` but it still does not seem to be working.
    – arabiccode
    Jan 31 '17 at 20:42
  • Actually just realized the problem is probably you have polygons that don't have any points in them so it's sending an empty dictionary to the max() function. I edited the script above to add a default value. Jan 31 '17 at 21:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.