/* * Copyright 2012, 2013 Hannes Janetzek * * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package org.oscim.utils.geom; import org.oscim.core.GeometryBuffer; import org.oscim.utils.pool.Inlist; import org.oscim.utils.pool.Pool; /** * Visvalingam-Wyatt simplification * * based on: * https://github.com/mbloch/mapshaper/blob/master/src/mapshaper-visvalingam.js * */ public class SimplifyVW { class Item extends Inlist<Item> { int index; int id; float area; Item prev; } Pool<Item> pool = new Pool<Item>() { @Override protected Item createItem() { return new Item(); } }; private Item[] heap = new Item[100]; private int size = 0; public void simplify(GeometryBuffer geom, float minArea) { Item prev = null; Item first = null; Item it; size = 0; if (heap.length < geom.pointPos >> 1) heap = new Item[geom.pointPos >> 1]; first = prev = push(0, Float.MAX_VALUE); for (int i = 2; i < geom.pointPos - 2; i += 2) { it = push(i, area(geom.points, i - 2, i, i + 2)); prev.next = it; it.prev = prev; prev = it; } Item last = push(geom.pointPos - 2, Float.MAX_VALUE); // sorter.doSort(heap, DistanceComparator, 0, size); // for (int i = 0; i < size; i++) // heap[i].index = i; last.prev = prev; prev.next = last; last.next = first; first.prev = last; while ((it = pop()) != null) { if (it.area > minArea) break; if (it.prev == it.next) break; it.prev.next = it.next; it.next.prev = it.prev; if (it.prev != first) update(geom, it.prev); if (it.next != first) update(geom, it.next); it = pool.release(it); } first.prev.next = null; first.prev = null; it = first; float[] points = new float[geom.pointPos]; System.arraycopy(geom.points, 0, points, 0, geom.pointPos); geom.clear(); geom.startPolygon(); while (it != null) { float x = points[it.id]; float y = points[it.id + 1]; geom.addPoint(x, y); it = it.next; } first = pool.release(first); } public static float area(float[] a, int p1, int p2, int p3) { float area = GeometryUtils.area(a, p1, p2, p3); double dotp = GeometryUtils.dotProduct(a, p1, p2, p3); //return (float) (area * (0.5 + 0.5 * (1 - dotp * dotp))); dotp = Math.abs(dotp); double weight = dotp < 0.5 ? 0.1 : dotp < 1 ? 0.3 : 1; return (float) (area * weight); } private void update(GeometryBuffer geom, Item it) { float area = area(geom.points, it.prev.id, it.id, it.next.id); update(it, area); //remove(it); //it.area = area(geom.points, it.prev.id, it.id, it.next.id); //push(it); } public void push(Item it) { heap[size] = it; it.index = size; up(size++); } public Item push(int id, float area) { Item it = pool.get(); heap[size] = it; it.index = size; it.area = area; it.id = id; up(size++); return it; } public Item pop() { if (size == 0) return null; Item removed = heap[0]; Item obj = heap[--size]; heap[size] = null; if (size > 0) { heap[obj.index = 0] = obj; down(0); } return removed; } public void update(Item it, float area) { if (area < it.area) { it.area = area; up(it.index); } else { it.area = area; down(it.index); } } public int remove(Item removed) { if (size == 0) throw new IllegalStateException("size == 0"); int i = removed.index; Item obj = heap[--size]; heap[size] = null; /* if min obj was popped */ if (i == size) return i; /* else put min obj in place of the removed item */ obj.index = i; heap[i] = obj; if (obj.area < removed.area) { up(i); } else down(i); return i; }; private void up(int i) { Item it = heap[i]; while (i > 0) { int up = ((i + 1) >> 1) - 1; Item parent = heap[up]; if (it.area >= parent.area) break; parent.index = i; heap[i] = parent; it.index = i = up; heap[i] = it; } } private void down(int i) { Item it = heap[i]; while (true) { int right = (i + 1) << 1; int left = right - 1; int down = i; Item child = heap[down]; if (left < size && heap[left].area < child.area) child = heap[down = left]; if (right < size && heap[right].area < child.area) child = heap[down = right]; if (down == i) break; heap[child.index = i] = child; heap[it.index = i = down] = it; } } }