/* Copyright (c) 2013 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Victor Olaya (Boundless) - initial implementation
*/
package org.locationtech.geogig.osm.internal;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.Expose;
/**
* A function that transforms a feature representing an OSM entity into a feature with a given
* feature type, according to a set of mapping rules.
*
*/
public class Mapping {
@Expose
private List<MappingRule> rules;
public Mapping(List<MappingRule> rules) {
this.rules = rules;
}
/**
* Transforms the passed feature according to the mapping rules of this mapping. If several
* rules can be applied, only the first one found is used.
*
* If no rule can be applied, Optional.absent is returned
*
* @param feature the feature to transform
* @return
*/
public List<MappedFeature> map(Feature feature) {
if (feature == null) {
return ImmutableList.of();
}
String tagsString = (String) ((SimpleFeature) feature).getAttribute("tags");
Collection<Tag> tags = OSMUtils.buildTagsCollectionFromString(tagsString);
ImmutableList.Builder<MappedFeature> builder = ImmutableList.<MappedFeature> builder();
for (MappingRule rule : rules) {
Optional<Feature> newFeature = rule.apply(feature, tags);
if (newFeature.isPresent()) {
builder.add(new MappedFeature(rule.getName(), newFeature.get()));
}
}
return builder.build();
}
/**
* Returns true if this any of the rules in this mapping can be used to convert the passed
* feature
*
* @param feature
* @return
*/
public boolean canBeApplied(Feature feature) {
String tagsString = (String) ((SimpleFeature) feature).getAttribute("tags");
Collection<Tag> tags = OSMUtils.buildTagsCollectionFromString(tagsString);
if (tags.isEmpty()) {
return false;
}
for (MappingRule rule : rules) {
if (rule.canBeApplied(feature, tags)) {
return true;
}
}
return false;
}
/**
* Loads a mapping from a file that contains a JSON representation of it
*
* @param filepath the path of the mapping file
* @return
*/
public static Mapping fromFile(String filepath) {
File mappingFile = new File(filepath);
Preconditions.checkArgument(mappingFile.exists(),
"The specified mapping file does not exist");
String mappingJson;
try {
mappingJson = readFile(mappingFile);
} catch (IOException e) {
throw new IllegalArgumentException("Error reading mapping file:" + e.getMessage(), e);
}
return fromString(mappingJson);
}
/**
* Creates a Mapping object from its JSON representation
*
* @param s the JSON representation
* @return the created mapping
*/
public static Mapping fromString(String s) {
GsonBuilder gsonBuilder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation();
Gson gson = gsonBuilder.create();
Mapping mapping;
try {
mapping = gson.fromJson(s, Mapping.class);
} catch (JsonSyntaxException e) {
throw new IllegalArgumentException("Error parsing mapping definition: "
+ e.getMessage(), e);
}
return mapping;
}
/**
* Returns the JSON representation of this mapping
*/
public String toString() {
GsonBuilder gsonBuilder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation();
Gson gson = gsonBuilder.create();
return gson.toJson(this);
}
private static String readFile(File file) throws IOException {
List<String> lines = Files.readLines(file, Charsets.UTF_8);
return Joiner.on("\n").join(lines);
}
public List<MappingRule> getRules() {
return rules;
}
/**
* Returns true if any of the rules in this mapping generates lines or polygons, so it needs
* ways as inputs
*
* @return
*/
public boolean canUseWays() {
for (MappingRule rule : rules) {
if (rule.canUseWays()) {
return true;
}
}
return false;
}
/**
* Returns true if any of the rules in this mapping generates points, so it nodes ways as inputs
*
* @return
*/
public boolean canUseNodes() {
for (MappingRule rule : rules) {
if (rule.canUseNodes()) {
return true;
}
}
return false;
}
/**
* Resolves the original tag name based on the name of a field in a mapped path (an alias for a
* tag name)
*
* @param path the path to the mapped data
* @param alias the name of the field
* @return the name of the tag from which the passed field was created in the specified mapped
* tree. If the current mapping does not generate the given mapped tree or the alias
* cannot be resolved, that passed alias itself is returned
*/
public String getTagNameFromAlias(String path, String alias) {
for (MappingRule rule : rules) {
if (rule.getName().equals(path)) {
return rule.getTagNameFromAlias(alias);
}
}
return alias;
}
public boolean equals(Object o) {
if (o instanceof Mapping) {
Mapping m = (Mapping) o;
return rules.equals(m.rules);
} else {
return false;
}
}
}