// This software is released into the Public Domain. See copying.txt for details. package org.openstreetmap.osmosis.tagfilter.v0_6; import java.util.Set; import java.util.Map; import java.util.logging.Logger; import org.openstreetmap.osmosis.core.OsmosisRuntimeException; import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer; import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer; import org.openstreetmap.osmosis.core.container.v0_6.WayContainer; import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer; import org.openstreetmap.osmosis.core.domain.v0_6.Tag; import org.openstreetmap.osmosis.core.task.v0_6.Sink; import org.openstreetmap.osmosis.core.task.v0_6.SinkSource; /** * A simple class to filter node, way, and relation entities by their tag keys and/or values. * * @author Andrew Byrd */ public class TagFilter implements SinkSource { private Sink sink; private Set<String> tagKeys; private Map<String, Set<String>> tagKeyValues; private Class<? extends EntityContainer> filterClass; private boolean reject; private boolean matchesEverything; private static final Logger LOG = Logger.getLogger(TagFilter.class.getName()); /** * Creates a new instance. * * @param filterMode * A 2-field dash-separated string specifying: * 1. Whether the filter accepts or rejects entities * 2. The entity type upon which it operates * * @param tagKeys * A Set of tag key Strings. The filter will match these tags irrespective of tag values. * * @param tagKeyValues * A map of tag key Strings to Sets of tag key values. These key-value pairs are checked * against each entity's tags to determine whether or not it matches the filter. */ public TagFilter(String filterMode, Set<String> tagKeys, Map<String, Set<String>> tagKeyValues) { String[] filterModeSplit = filterMode.toLowerCase().split("-"); if (filterModeSplit.length != 2) { throw new OsmosisRuntimeException( "The TagFilter task's default parameter must consist of an action and an entity type separated by '-'."); } String action = filterModeSplit[0]; if (action.equals("accept")) { reject = false; } else if (action.equals("reject")) { reject = true; } else { throw new OsmosisRuntimeException( "The TagFilter action must be either 'accept' or 'reject'. '" + action + "' is not a supported mode."); } String entity = filterModeSplit[1]; if (entity.endsWith("s")) { entity = entity.substring(0, entity.length() - 1); } if (entity.equals("node")) { filterClass = NodeContainer.class; } else if (entity.equals("way")) { filterClass = WayContainer.class; } else if (entity.equals("relation")) { filterClass = RelationContainer.class; } else { throw new OsmosisRuntimeException( "The TagFilter entity type must be one of 'node', 'way', or 'relation'. '" + entity + "' is not a supported entity type."); } matchesEverything = (tagKeys.size() == 0 && tagKeyValues.size() == 0); this.tagKeys = tagKeys; this.tagKeyValues = tagKeyValues; String logString = "New TagFilter "; if (reject) { logString += "rejects "; } else { logString += "accepts "; } logString += filterClass; if (matchesEverything) { logString += " (no tag-based filtering)."; } else { logString += " having tag keys " + tagKeys + " or tag key-value pairs " + tagKeyValues + "."; } LOG.finer(logString); } /** * Checks whether the Entity in a container has tags that match this filter. * * @param container * The container holding the entity whose tags shall be examined. */ private boolean matches(EntityContainer container) { boolean matched = false; for (Tag tag : container.getEntity().getTags()) { String key = tag.getKey(); if (tagKeys.contains(key)) { matched = true; break; } Set<String> values = tagKeyValues.get(key); if ((values != null) && values.contains(tag.getValue())) { matched = true; break; } } return matched; } /** * {@inheritDoc} */ public void initialize(Map<String, Object> metaData) { sink.initialize(metaData); } /** * {@inheritDoc} */ public void process(EntityContainer container) { if (filterClass.isInstance(container)) { if (reject ^ (matchesEverything || matches(container))) { sink.process(container); } } else { sink.process(container); } } /** * {@inheritDoc} */ public void complete() { sink.complete(); } /** * {@inheritDoc} */ public void close() { sink.close(); } /** * {@inheritDoc} */ public void setSink(Sink sink) { this.sink = sink; } }