/*
* Copyright (C) 2010.
*
* 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.combiners;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import uk.me.parabola.imgfmt.ExitException;
import uk.me.parabola.imgfmt.FileExistsException;
import uk.me.parabola.imgfmt.FileNotWritableException;
import uk.me.parabola.imgfmt.FileSystemParam;
import uk.me.parabola.imgfmt.MapFailedException;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.imgfmt.app.map.Map;
import uk.me.parabola.imgfmt.app.map.MapReader;
import uk.me.parabola.imgfmt.app.srt.Sort;
import uk.me.parabola.imgfmt.app.trergn.Point;
import uk.me.parabola.imgfmt.app.trergn.Polygon;
import uk.me.parabola.imgfmt.app.trergn.Polyline;
import uk.me.parabola.imgfmt.app.trergn.Zoom;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.CommandArgs;
import uk.me.parabola.mkgmap.build.MapBuilder;
import uk.me.parabola.mkgmap.general.LevelInfo;
import uk.me.parabola.mkgmap.general.MapLine;
import uk.me.parabola.mkgmap.general.MapPoint;
import uk.me.parabola.mkgmap.general.MapShape;
import uk.me.parabola.mkgmap.srt.SrtTextReader;
/**
* Build the overview map. This is a low resolution map that covers the whole
* of a map set. It also contains polygons that correspond to the areas
* covered by the individual map tiles.
*
* @author Steve Ratcliffe
*/
public class OverviewBuilder
implements Combiner
{
Logger log =
Logger.
getLogger(OverviewBuilder.
class);
public static final String OVERVIEW_PREFIX =
"ovm_";
private final OverviewMap overviewSource
;
private String areaName
;
private String overviewMapname
;
private String overviewMapnumber
;
private Zoom
[] levels
;
private String outputDir
;
private Integer codepage
;
private Integer encodingType
;
private List<String[]> copyrightMsgs =
new ArrayList<String[]>();
private List<String[]> licenseInfos =
new ArrayList<String[]>();
public OverviewBuilder
(OverviewMap overviewSource
) {
this.
overviewSource = overviewSource
;
}
public void init
(CommandArgs args
) {
areaName = args.
get("area-name",
"Overview Map");
overviewMapname = args.
get("overview-mapname",
"osmmap");
overviewMapnumber = args.
get("overview-mapnumber",
"63240000");
outputDir = args.
getOutputDir();
}
public void onMapEnd
(FileInfo finfo
) {
if (!finfo.
isImg())
return;
try {
readFileIntoOverview
(finfo
);
} catch (FileNotFoundException e
) {
throw new MapFailedException
("Could not read detail map " + finfo.
getFilename(), e
);
}
}
public void onFinish
() {
addBackground
();
writeOverviewMap
();
}
/**
* Add background polygon that covers the whole area of the overview map.
*/
private void addBackground
() {
MapShape background =
new MapShape
();
background.
setType(0x4b
); // background type
background.
setMinResolution(0); // On all levels
background.
setPoints(overviewSource.
getBounds().
toCoords());
overviewSource.
addShape(background
);
}
/**
* Write out the overview map.
*/
private void writeOverviewMap
() {
if (overviewSource.
mapLevels() ==
null)
return;
MapBuilder mb =
new MapBuilder
();
mb.
setEnableLineCleanFilters(false);
FileSystemParam params =
new FileSystemParam
();
params.
setBlockSize(512);
params.
setMapDescription(areaName
);
mb.
setCopyrights(creMsgList
(copyrightMsgs
));
mb.
setMapInfo(creMsgList
(licenseInfos
));
try {
if (codepage ==
null){
codepage =
0; // should not happen
}
Sort sort = SrtTextReader.
sortForCodepage(codepage
);
Map map =
Map.
createMap(overviewMapname, outputDir, params, overviewMapnumber, sort
);
if (encodingType
!=
null){
map.
getLblFile().
setEncoder(encodingType, codepage
);
}
mb.
makeMap(map, overviewSource
);
map.
close();
} catch (FileExistsException e
) {
throw new ExitException
("Could not create overview map", e
);
} catch (FileNotWritableException e
) {
throw new ExitException
("Could not write to overview map", e
);
}
}
/**
* Add an individual .img file to the overview map.
*
* @param finfo Information about an individual map.
*/
private void readFileIntoOverview
(FileInfo finfo
) throws FileNotFoundException {
addMapCoverageArea
(finfo
);
MapReader mapReader =
null;
String filename = finfo.
getFilename();
if (codepage ==
null){
codepage = finfo.
getCodePage();
}
if (codepage
!= finfo.
getCodePage()){
System.
err.
println("WARNING: input file " + filename +
" has different code page " + finfo.
getCodePage());
}
try{
mapReader =
new MapReader
(filename
);
if (encodingType ==
null){
encodingType = mapReader.
getEncodingType();
}
if (encodingType
!= mapReader.
getEncodingType()){
System.
err.
println("WARNING: input file " + filename +
" has different charset type " + encodingType
);
}
String[] msgs = mapReader.
getCopyrights();
boolean found =
false;
for (String[] block : copyrightMsgs
) {
if (Arrays.
deepEquals(block, msgs
)){
found =
true;
break;
}
}
if (!found
)
copyrightMsgs.
add(msgs
);
msgs = finfo.
getLicenseInfo();
found =
false;
for (String[] block : licenseInfos
) {
if (Arrays.
deepEquals(block, msgs
)){
found =
true;
break;
}
}
if (!found
)
licenseInfos.
add(msgs
);
levels = mapReader.
getLevels();
if (overviewSource.
mapLevels() ==
null){
LevelInfo
[] mapLevels
;
if (isOverviewImg
(filename
)){
mapLevels =
new LevelInfo
[levels.
length-
1];
for (int i =
1; i
< levels.
length; i++
){
mapLevels
[i-
1] =
new LevelInfo
(levels
[i
].
getLevel(), levels
[i
].
getResolution());
}
} else {
mapLevels =
new LevelInfo
[1];
mapLevels
[0] =
new LevelInfo
(levels
[1].
getLevel(), levels
[1].
getResolution());
}
overviewSource.
setMapLevels(mapLevels
);
}
if (isOverviewImg
(filename
)){
readPoints
(mapReader
);
readLines
(mapReader
);
readShapes
(mapReader
);
}
} catch (FileNotFoundException e
) {
throw new ExitException
("Could not open " + filename +
" when creating overview file");
} finally {
Utils.
closeFile(mapReader
);
}
}
/**
* Read the points from the .img file and add them to the overview map.
* We read from the least detailed level (apart from the empty one).
*
* @param mapReader Map reader on the detailed .img file.
*/
private void readPoints
(MapReader mapReader
) {
Area bounds = overviewSource.
getBounds();
for (int l =
1; l
< levels.
length; l++
){
int min = levels
[l
].
getLevel();
int res = levels
[l
].
getResolution();
List<Point> pointList = mapReader.
pointsForLevel(min, MapReader.
WITH_EXT_TYPE_DATA);
for (Point point: pointList
) {
if (log.
isDebugEnabled())
log.
debug("got point", point
);
if (bounds.
contains(point.
getLocation()) ==
false){
if (log.
isDebugEnabled())
log.
debug(point,
"dropped, is outside of tile boundary");
continue;
}
MapPoint mp =
new MapPoint
();
mp.
setType(point.
getType());
mp.
setName(point.
getLabel().
getText());
mp.
setMaxResolution(res
);
mp.
setMinResolution(res
);
mp.
setLocation(point.
getLocation());
overviewSource.
addPoint(mp
);
}
}
}
/**
* Read the lines from the .img file and add them to the overview map.
* We read from the least detailed level (apart from the empty one).
*
* @param mapReader Map reader on the detailed .img file.
*/
private void readLines
(MapReader mapReader
) {
for (int l =
1; l
< levels.
length; l++
){
int min = levels
[l
].
getLevel();
int res = levels
[l
].
getResolution();
List<Polyline
> lineList = mapReader.
linesForLevel(min
);
//System.out.println(lineList.size() + " lines in lowest resolution " + levels[1].getResolution());
for (Polyline line : lineList
) {
if (log.
isDebugEnabled())
log.
debug("got line", line
);
MapLine ml =
new MapLine
();
List<Coord
> points = line.
getPoints();
if (log.
isDebugEnabled())
log.
debug("line point list", points
);
if (points.
size() < 2)
continue;
ml.
setType(line.
getType());
if (line.
getLabel() !=
null)
ml.
setName(line.
getLabel().
getText());
ml.
setMaxResolution(res
);
ml.
setMinResolution(res
);
ml.
setPoints(points
);
overviewSource.
addLine(ml
);
}
}
}
/**
* Read the polygons from the .img file and add them to the overview map.
* We read from the least detailed level (apart from the empty one).
*
* @param mapReader Map reader on the detailed .img file.
*/
private void readShapes
(MapReader mapReader
) {
for (int l =
1; l
< levels.
length; l++
){
int min = levels
[l
].
getLevel();
int res = levels
[l
].
getResolution();
List<Polygon> list = mapReader.
shapesForLevel(min
);
for (Polygon shape : list
) {
if (log.
isDebugEnabled())
log.
debug("got polygon", shape
);
if (shape.
getType() == 0x4b
){
// ignore existing background polygons as we will add our own
continue;
}
MapShape ms =
new MapShape
();
List<Coord
> points = shape.
getPoints();
if (log.
isDebugEnabled())
log.
debug("polygon point list", points
);
if (points.
size() < 3)
continue;
ms.
setType(shape.
getType());
if (shape.
getLabel() !=
null)
ms.
setName(shape.
getLabel().
getText());
ms.
setMaxResolution(res
);
ms.
setMinResolution(res
);
ms.
setPoints(points
);
overviewSource.
addShape(ms
);
}
}
}
/**
* Add an area that shows the area covered by a detailed map. This can
* be an arbitary shape, although at the current time we only support
* rectangles.
*
* @param finfo Information about a detail map.
*/
private void addMapCoverageArea
(FileInfo finfo
) {
Area bounds = finfo.
getBounds();
List<Coord
> points = bounds.
toCoords();
for (Coord co: points
){
overviewSource.
addToBounds(co
);
}
// Create the background rectangle
MapShape bg =
new MapShape
();
bg.
setType(0x4a
);
bg.
setPoints(points
);
bg.
setMinResolution(0);
bg.
setName(finfo.
getDescription() +
'\u001d' + finfo.
getMapname());
overviewSource.
addShape(bg
);
}
public Area getBounds
() {
return overviewSource.
getBounds();
}
/**
* Check if the the file name points to a partly overview img file
* @param name full path or just a name
* @return true if the name points to a partly overview img file
*/
public static boolean isOverviewImg
(String name
){
return new File(name
).
getName().
startsWith(OVERVIEW_PREFIX
);
}
/**
* Add the prefix to the file name.
* @param name filename
* @return filename of the corresponding overview img file
*/
public static String getOverviewImgName
(String name
){
File f =
new File(name
);
return new File(f.
getParent(),OverviewBuilder.
OVERVIEW_PREFIX + f.
getName()).
getAbsolutePath();
}
public static String getMapName
(String name
) {
String fname =
new File(name
).
getName();
if (fname.
startsWith(OVERVIEW_PREFIX
))
return fname.
substring(OVERVIEW_PREFIX.
length());
else return name
;
}
private List<String> creMsgList
(List<String[]> msgs
){
ArrayList< String> list =
new ArrayList<String>();
for (int i =
0; i
< msgs.
size(); i++
){
String[] block = msgs.
get(i
);
for (String s : block
){
list.
add(s
);
}
if (i
< msgs.
size()-
1){
// separate blocks
list.
add("");
}
}
return list
;
}
}