/*
* 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;
}
}