Rev 4548 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Copyright (C) 2006 - 2012.
*
* 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.reader.osm;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import uk.me.parabola.imgfmt.ExitException;
import uk.me.parabola.imgfmt.FormatException;
import uk.me.parabola.imgfmt.app.Area;
import uk.me.parabola.imgfmt.app.Coord;
/**
* Base class for OSM file handlers.
*
* @author Steve Ratcliffe
*/
public abstract class OsmHandler
{
// Elements that are read are saved/further processed by these two classes.
protected ElementSaver saver
;
protected OsmReadingHooks hooks
;
private Map<String,
Set<String>> deletedTags
;
private Map<String,
String> usedTags
;
/** Pattern for values containing fixme, fix_me etc. */
private static final Pattern FIXME_PATTERN =
Pattern.
compile("(?i)fix[ _]?+me");
private boolean removeFixme
;
// Options
private boolean ignoreBounds
;
// Node references within a way
private long firstNodeRef
;
private long lastNodeRef
;
private boolean missingNodeRef
;
private boolean removeEmptyValue =
true;
/**
* Set a set of tags with values that are to be deleted on input.
* For each key there is a set of values. If the value set is empty then
* all tags with the given key are deleted. If the value set is not empty
* then only tags with the given key that has one of the given values are
* deleted.
*
* @param deletedTags A map of tag key, to a set of values to be deleted.
*/
public void setTagsToDelete
(Map<String,
Set<String>> deletedTags
) {
this.
deletedTags = deletedTags
;
}
/**
* This sets a list of all the tags that are used in the system.
*
* Assuming this list is complete, no other tag can have an effect on the output
* and can therefore be dropped on input. This reduces memory usage, sometimes
* dramatically if there are many useless tags in the input.
*
* We keep a map of tag-name to tag-name. This allows us to keep only a single
* copy of each string. This also results in a reasonable reduction in memory usage.
*
* @param used The complete set of tags that are used to form the output.
*/
public void setUsedTags
(Set<String> used
) {
if (used ==
null || used.
isEmpty()) {
usedTags =
null;
return;
}
usedTags =
new HashMap<>();
for (String s : used
) {
if (s ==
null) {
continue;
}
// intern the keys
s = s.
intern();
usedTags.
put(s, s
);
}
}
/**
* Some tags are dropped at the input stage. We drop tags that are not going
* to be used and there is also an option to provide a file containing tags to
* be dropped.
*
* @param key The tag key.
* @param val The tag value.
* @return Returns the tag key if this tag should be kept. Returns null if the tag
* should be discarded.
*/
protected String keepTag
(String key,
String val
) {
if (removeEmptyValue
&& val.
isEmpty())
return null;
if(deletedTags
!=
null) {
Set<String> vals = deletedTags.
get(key
);
if(vals
!=
null && (vals.
isEmpty() || vals.
contains(val
))) {
return null;
}
}
// By returning the value stored in usedTags, instead of the key, we ensure
// that the same string is always used so saving some memory.
if (usedTags
!=
null)
key = usedTags.
get(key
);
if (key
!=
null && removeFixme
&& val.
length() >=
5) {
// remove tags with value fixme if the key is NOT fixme
if ("fixme".
equals(key
) ||
"FIXME".
equals(key
)) {
// keep fixme no matter what value it has
} else if (FIXME_PATTERN.
matcher(val
).
matches()) {
return null;
}
}
return key
;
}
/**
* Actually set the bounding box. The boundary values are given.
*/
protected void setBBox
(double minlat,
double minlong,
double maxlat,
double maxlong
) {
if (minlat == maxlat || minlong == maxlong
) {
return; // silently ignore bounds with dim 0
}
Area bbox =
new Area(minlat, minlong, maxlat, maxlong
);
saver.
setBoundingBox(bbox
);
}
public void setElementSaver
(ElementSaver elementSaver
) {
this.
saver = elementSaver
;
}
public void setHooks
(OsmReadingHooks plugin
) {
this.
hooks = plugin
;
}
/**
* Common actions to take when creating a new way.
* Reset some state and create the Way object.
* @param id The osm id of the new way.
* @return The new Way itself.
*/
protected Way startWay
(long id
) {
firstNodeRef =
0;
lastNodeRef =
0;
missingNodeRef =
false;
return new Way
(id
);
}
/**
* Common actions to take when a way has been completely read by the parser.
* It is saved
* @param way The way that was read.
*/
protected final void endWay
(Way way
) {
way.
setClosedInOSM(firstNodeRef == lastNodeRef
);
way.
setComplete(!missingNodeRef
);
saver.
addWay(way
);
hooks.
onAddWay(way
);
}
/**
* Add a coordinate point to the way.
* @param way The Way.
* @param id The coordinate id.
*/
protected final void addCoordToWay
(Way way,
long id
) {
lastNodeRef = id
;
if (firstNodeRef ==
0) firstNodeRef = id
;
Coord co = saver.
getCoord(id
);
if (co
!=
null) {
Node node = saver.
getNode(id
);
if (node
!=
null && node.
getTagCount() > 0) {
hooks.
onNodeAddedToWay(way, id
);
// hooks can change the node and the coord object associated with the id
co = saver.
getCoord(id
);
if (co ==
null) {
throw new ExitException
("Internal error: hooks removed coord with id " + id
);
}
}
way.
addPoint(co
);
} else {
missingNodeRef =
true;
}
}
/**
* Enable removal of tags / value pairs where value matches the fixme pattern.
* @param b true: enable the filter
*/
public void setDeleteFixmeValues
(boolean b
) {
this.
removeFixme = b
;
}
/**
* Enable removal of tags / value pairs where value is empty.
* @param b true: enable the filter
*/
public void setDeleteEmptyValueTag
(boolean b
) {
this.
removeEmptyValue= b
;
}
public boolean isIgnoreBounds
() {
return ignoreBounds
;
}
public void setIgnoreBounds
(boolean ignoreBounds
) {
this.
ignoreBounds = ignoreBounds
;
}
/**
* Determines if the file (or other resource) is supported by this map
* data source. The implementation may do this however it likes, eg
* by extension or by opening up the file and reading part of it.
*
* @param name The file (or other resource) to check.
* @return True if the OSM handler supports that file.
*/
public abstract boolean isFileSupported
(String name
);
/**
* Load osm data from open stream.
* You would implement this interface to allow reading data from
* zipped files.
*
* @param is the already opened stream.
* @throws FormatException For any kind of malformed input.
*/
public abstract void parse
(InputStream is
) throws FormatException
;
}