from django.core.management.base import BaseCommand
from geo.models import CountyBorders
from django.contrib.gis.geos.collections import MultiPolygon
from django.contrib.gis.geos.polygon import Polygon
from django.contrib.gis.geos.linestring import LinearRing
import time
class Command(BaseCommand):
option_list = BaseCommand.option_list
help = 'Simplify polygons'
args = ''
def handle(self, *app_labels, **options):
simplify()
print 'DONE!'
def simplify():
precision = 75
klasses = [CountyBorders] # change this or parameterize it
max = 2000 # for whole batch make this None
for klass in klasses:
objs = klass.objects.all()
if max:
objs = objs[:max]
for obj in objs:
fix_region(obj, precision)
def fix_region(obj, precision):
new_mpoly = fix_poly(obj.mpoly, precision, obj.id, obj.name)
obj.mpoly = new_mpoly
obj.save()
def fix_poly(mpoly, precision, id, name):
new_polies = []
for poly in mpoly:
new_rings = []
for ring in poly:
points = simplify_points(list(ring), precision, id, name)
new_rings.append(LinearRing(points))
new_polies.append(Polygon(*new_rings))
# new_polies.append(poly) # so we can see diff
new_mpoly = MultiPolygon(*new_polies)
return new_mpoly
def benefit_function(midpt, pt1, pt2):
# This corresponds to the area of the triangle
# that a point adds to a polygon. It favors
# either midpoints that are between two neighbors
# that are far away from each other or midpoints
# that are orthogonally far the neighbors' connecting
# segment.
xm, ym = midpt
x1, y1 = pt1
x2, y2 = pt2
return abs((x2 -x1)* (y1 - ym) - (x1-xm) * (y2 -y1))
def simplify_points(points, max, id, name):
# 338 points takes 0.012 secs to compress
tups = []
t = time.time()
for i, point in enumerate(points):
if i == 0 or i == len(points) - 1:
benefit = None
else:
benefit = benefit_function(point, points[i-1], points[i+1])
tups.append((point, benefit))
removals = 0
while len(tups) > max + 1:
min_benefit = tups[1][1]
min_i = 1
for i, (point, benefit) in enumerate(tups):
# print i, benefit
if benefit and benefit < min_benefit:
min_benefit = benefit
min_i = i
del tups[min_i]
removals += 1
for i in [min_i-1, min_i]:
point = tups[i][0]
if i == 0 or i == len(tups) - 1:
benefit = None
else:
benefit = benefit_function(point, tups[i-1][0], tups[i+1][0])
tups[i] = (point, benefit)
points = [point for point, benefit in tups]
if removals > 600:
print id, name, removals, 'removals took', time.time() - t
return points