/* * Copyright 2010, 2011, 2012 mapsforge.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.mapsforge.map.writer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.mapsforge.map.writer.model.Encoding; import org.mapsforge.map.writer.model.WayDataBlock; /** * Provides delta or double delta encoding of lists of integers. * * @author bross */ public final class DeltaEncoder { private DeltaEncoder() { } /** * Encodes a list of WayDataBlock objects with the given encoding scheme. * * @param blocks * List of WayDataBlock objects to be encoded. * @param encoding * The Encoding which is used. * @return A new list of new WayDataBlock objects encoded with the given encoding. The original list is returned in * case the encoding equals NONE. */ public static List<WayDataBlock> encode(List<WayDataBlock> blocks, Encoding encoding) { if (blocks == null) { return null; } if (encoding == Encoding.NONE) { return blocks; } List<WayDataBlock> results = new ArrayList<WayDataBlock>(); for (WayDataBlock wayDataBlock : blocks) { List<Integer> outer = mEncode(wayDataBlock.getOuterWay(), encoding); List<List<Integer>> inner = null; if (wayDataBlock.getInnerWays() != null) { inner = new ArrayList<List<Integer>>(); for (List<Integer> list : wayDataBlock.getInnerWays()) { inner.add(mEncode(list, encoding)); } } results.add(new WayDataBlock(outer, inner, encoding)); } return results; } /** * Computes the size in bytes for storing a list of WayDataBlock objects as unsigned var-bytes. * * @param blocks * the blocks which should be encoded * @return the number of bytes needed for the encoding */ public static int simulateSerialization(List<WayDataBlock> blocks) { int sum = 0; for (WayDataBlock wayDataBlock : blocks) { sum += mSimulateSerialization(wayDataBlock.getOuterWay()); if (wayDataBlock.getInnerWays() != null) { for (List<Integer> list : wayDataBlock.getInnerWays()) { sum += mSimulateSerialization(list); } } } return sum; } private static List<Integer> mEncode(List<Integer> list, Encoding encoding) { switch (encoding) { case DELTA: return deltaEncode(list); case DOUBLE_DELTA: return doubleDeltaEncode(list); case NONE: return list; } throw new IllegalArgumentException("unknown encoding value: " + encoding); } private static int mSimulateSerialization(List<Integer> list) { int sum = 0; for (Integer coordinate : list) { sum += Serializer.getVariableByteSigned(coordinate.intValue()).length; } return sum; } static List<Integer> deltaEncode(List<Integer> list) { if (list == null) { return null; } ArrayList<Integer> result = new ArrayList<Integer>(); if (list.isEmpty()) { return result; } Iterator<Integer> it = list.iterator(); // add the first way node to the result list Integer prevLat = it.next(); Integer prevLon = it.next(); result.add(prevLat); result.add(prevLon); while (it.hasNext()) { Integer currentLat = it.next(); Integer currentLon = it.next(); result.add(Integer.valueOf(currentLat.intValue() - prevLat.intValue())); result.add(Integer.valueOf(currentLon.intValue() - prevLon.intValue())); prevLat = currentLat; prevLon = currentLon; } return result; } static List<Integer> doubleDeltaEncode(List<Integer> list) { if (list == null) { return null; } ArrayList<Integer> result = new ArrayList<Integer>(); if (list.isEmpty()) { return result; } Iterator<Integer> it = list.iterator(); // add the first way node to the result list Integer prevLat = it.next(); Integer prevLon = it.next(); Integer prevLatDelta = Integer.valueOf(0); Integer prevLonDelta = Integer.valueOf(0); result.add(prevLat); result.add(prevLon); while (it.hasNext()) { Integer currentLat = it.next(); Integer currentLon = it.next(); Integer deltaLat = Integer.valueOf(currentLat.intValue() - prevLat.intValue()); Integer deltaLon = Integer.valueOf(currentLon.intValue() - prevLon.intValue()); result.add(Integer.valueOf(deltaLat.intValue() - prevLatDelta.intValue())); result.add(Integer.valueOf(deltaLon.intValue() - prevLonDelta.intValue())); prevLat = currentLat; prevLon = currentLon; prevLatDelta = deltaLat; prevLonDelta = deltaLon; } return result; } }