/* * Copyright (C) 2010, 2011. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 or * version 2 as published by the Free Software Foundation. * * 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. */ package uk.me.parabola.mkgmap.filters; import java.util.ArrayList; import java.util.List; import uk.me.parabola.log.Logger; import uk.me.parabola.mkgmap.build.MapSplitter; import uk.me.parabola.mkgmap.general.MapElement; import uk.me.parabola.mkgmap.general.MapShape; /** * Split polygon so that it does not exceed the limits of a subdivision. The plan * here is simple, if its too big, then cut it in half. As we always cut the largest * dimension, then we will soon enough have cut it down to be small enough. * * @author WanMil */ public class PolygonSubdivSizeSplitterFilter extends PolygonSplitterBase implements MapFilter { private static final Logger log = Logger.getLogger(PolygonSubdivSizeSplitterFilter.class); private int maxSize; /** * Get the scale factor so that we don't over split. * * @param config configuration information, giving parameters of the map level * that is being produced through this filter. */ public void init(FilterConfig config) { int shift = config.getShift(); if (shift > 15) shift = 16; // allow a size of 0x8000 to avoid splitting of generated precomp-sea polygons maxSize = Math.min((1<<24)-1, Math.max(MAX_SIZE << shift, 0x8000)); } /** * Split up polygons that exceeds the limits of a subdivision. * * @param element A map element, only polygons will be processed. * @param next This is used to pass the possibly transformed element onward. */ public void doFilter(MapElement element, MapFilterChain next) { assert element instanceof MapShape; MapShape shape = (MapShape) element; if (isSizeOk(shape)) { // This is ok let it through and return. next.doFilter(element); return; } List<MapShape> outputs = new ArrayList<MapShape>(); // Do an initial split split(shape, outputs); // Now check that all the resulting parts are also small enough. // NOTE: the end condition is changed from within the loop. for (int i = 0; i < outputs.size(); i++) { MapShape s = outputs.get(i); if (!isSizeOk(s)) { // Not small enough, so remove it and split it again. The resulting // pieces will be placed at the end of the list and will be // picked up later on. outputs.set(i, null); split(s, outputs); } } // Now add all to the chain. for (MapShape s : outputs) { if (s == null) continue; next.doFilter(s); } } private boolean isSizeOk(MapShape shape) { // do not cut the background shape if (shape.getType() == 0x4a) return true; // Estimate the size taken by lines and shapes as a constant plus // a factor based on the number of points. int numPoints = shape.getPoints().size(); int numElements = 1 + ((numPoints - 1) / PolygonSplitterFilter.MAX_POINT_IN_ELEMENT); int size = numElements * 11 + numPoints * 4; if (shape.hasExtendedType()) { if (size > MapSplitter.MAX_XT_SHAPES_SIZE) { log.debug("XTSize larger than", MapSplitter.MAX_XT_SHAPES_SIZE); return false; } } else if (size > MapSplitter.MAX_RGN_SIZE) { log.debug("RGN Size larger than", MapSplitter.MAX_RGN_SIZE); return false; } int maxDim = shape.getBounds().getMaxDimension(); if (maxDim > maxSize){ log.debug("Size ", maxDim," larger than ", maxSize); return false; } return true; } }