Skip to content

Commit

Permalink
Merge pull request #52 from fwiesweg/master
Browse files Browse the repository at this point in the history
Allow joining external data using tag-transform
  • Loading branch information
migurski authored May 5, 2020
2 parents 0dafee5 + b0a722f commit 4a706f8
Show file tree
Hide file tree
Showing 18 changed files with 706 additions and 27 deletions.
2 changes: 2 additions & 0 deletions osmosis-tagtransform/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
dependencies {
compile project(':osmosis-core')
compile project(':osmosis-xml')
compile group: 'org.apache.commons', name: 'commons-csv', version: 1.7

testCompile project(':osmosis-testutil')
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.tagtransform;

public interface DataSource {

String[] transform(String[] matches);

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@

public interface Output {

void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches);
void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches, Map<String, DataSource> dataSources);

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public interface Translation {
Collection<Match> match(Map<String, String> tags, TTEntityType entityType, String uname, int uid);


Map<String, DataSource> getDataSources();


boolean isDropOnMatch();


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

import java.util.Collection;
import java.util.Map;

import org.openstreetmap.osmosis.tagtransform.DataSource;
import org.openstreetmap.osmosis.tagtransform.Match;
import org.openstreetmap.osmosis.tagtransform.Output;


public class CopyAll implements Output {

@Override
public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches) {
public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches, Map<String, DataSource> dataSources) {
// copy all tags
tags.putAll(originalTags);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

import java.util.Collection;
import java.util.Map;

import org.openstreetmap.osmosis.tagtransform.DataSource;
import org.openstreetmap.osmosis.tagtransform.Match;
import org.openstreetmap.osmosis.tagtransform.Output;


public class CopyMatched implements Output {

@Override
public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches) {
public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches, Map<String, DataSource> dataSources) {
// put any matches directly
for (Match match : matches) {
if (match.getKeyGroupCount() > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.openstreetmap.osmosis.tagtransform.DataSource;
import org.openstreetmap.osmosis.tagtransform.Match;
import org.openstreetmap.osmosis.tagtransform.Output;


public class CopyUnmatched implements Output {

@Override
public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches) {
public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches, Map<String, DataSource> dataSources) {
// copy the original, then remove the matches
Map<String, String> toCopy = new HashMap<String, String>(originalTags);
for (Match match : matches) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// This software is released into the Public Domain. See copying.txt for details.

package org.openstreetmap.osmosis.tagtransform.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.csv.QuoteMode;
import org.w3c.dom.NamedNodeMap;

import org.openstreetmap.osmosis.tagtransform.DataSource;

/**
*
* @author fwiesweg
*/
public class DataSourceCSV implements DataSource {

private Map<String, String[]> data = Collections.emptyMap();
private String[] fallback;

public DataSourceCSV(File parentDirectory, NamedNodeMap attributes) {
String file = attributes.getNamedItem("file").getTextContent();
String csvFormat = attributes.getNamedItem("csvFormat").getTextContent();


List<Integer> lookupIndices = Stream.of(attributes.getNamedItem("lookup").getTextContent().split(","))
.map(idx -> Integer.parseInt(idx))
.collect(Collectors.toList());
List<Integer> returnIndices = Stream.of(attributes.getNamedItem("return").getTextContent().split(","))
.map(idx -> Integer.parseInt(idx))
.collect(Collectors.toList());

try(InputStreamReader fis = new InputStreamReader(new FileInputStream(new File(parentDirectory, file)))) {
CSVRecord fallbackRecord = CSVParser.parse(attributes.getNamedItem("fallback").getTextContent(), CSVFormat.newFormat(',')
.withDelimiter(',')
.withQuote('"')
.withQuoteMode(QuoteMode.MINIMAL))
.iterator().next();
this.fallback = new String[fallbackRecord.size()];
for(int i = 0; i < fallback.length; i++) {
this.fallback[i] = fallbackRecord.get(i);
}

CSVParser parser = CSVParser.parse(fis, CSVFormat.valueOf(csvFormat));
this.data = parser.getRecords().stream().collect(Collectors.toMap(
r -> {
return lookupIndices.stream().map(i -> r.get(i)).collect(Collectors.joining("\0"));
},
r -> {
return returnIndices.stream().map(i -> r.get(i)).toArray(i -> new String[i]);
}));


} catch (IOException ex) {
Logger.getLogger(DataSourceCSV.class.getName()).log(Level.SEVERE, null, ex);
}
}

@Override
public String[] transform(String[] matches) {
return data.getOrDefault(String.join("\0", matches), fallback);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.tagtransform.impl;

import java.io.File;
import java.util.function.BiFunction;
import org.w3c.dom.NamedNodeMap;

import org.openstreetmap.osmosis.tagtransform.DataSource;

/**
*
* @author fwiesweg
*/
public enum DataSources {
CSV(DataSourceCSV::new);

private final BiFunction<File, NamedNodeMap, DataSource> factory;

private DataSources(BiFunction<File, NamedNodeMap, DataSource> factory) {
this.factory = factory;
}

public DataSource create(File parentDir, NamedNodeMap attributes) {
return this.factory.apply(parentDir, attributes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Map;

import org.openstreetmap.osmosis.tagtransform.DataSource;
import org.openstreetmap.osmosis.tagtransform.Match;
import org.openstreetmap.osmosis.tagtransform.Output;

Expand All @@ -14,14 +14,24 @@ public class TagOutput implements Output {
private MessageFormat keyFormat;
private MessageFormat valueFormat;
private String fromMatch;
private String keyDataSource;
private String valueDataSource;


public TagOutput(String key, String value, String fromMatch) {
public TagOutput(String key, String value, String fromMatch, String keyDataSource, String valueDataSource) {
keyFormat = new MessageFormat(santitise(key));
valueFormat = new MessageFormat(santitise(value));
if ((fromMatch != null && fromMatch.length() > 0)) {
if (fromMatch != null && fromMatch.length() > 0) {
this.fromMatch = fromMatch;
}

if (keyDataSource != null && keyDataSource.length() > 0) {
this.keyDataSource = keyDataSource;
}

if (valueDataSource != null && valueDataSource.length() > 0) {
this.valueDataSource = valueDataSource;
}
}


Expand All @@ -31,13 +41,19 @@ private String santitise(String str) {
}
return str;
}


private final DataSource dummyDataSource = matches -> matches;

@Override
public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches) {
public void apply(Map<String, String> originalTags, Map<String, String> tags, Collection<Match> matches, Map<String, DataSource> dataSources) {
// if we have a fromMatch field we apply our format to
// each and every matching match
if (fromMatch != null) {
DataSource keyDataSourceImpl = this.keyDataSource != null && dataSources.containsKey(this.keyDataSource)
? dataSources.get(this.keyDataSource) : dummyDataSource;
DataSource valueDataSourceImpl = this.valueDataSource != null && dataSources.containsKey(this.valueDataSource)
? dataSources.get(this.valueDataSource) : dummyDataSource;

for (Match match : matches) {
String matchID = match.getMatchID();
if (matchID != null && matchID.equals(fromMatch)) {
Expand All @@ -46,14 +62,14 @@ public void apply(Map<String, String> originalTags, Map<String, String> tags, Co
for (int i = 0; i < args.length; i++) {
args[i] = match.getKey(i);
}
String key = keyFormat.format(args);
String key = keyFormat.format(keyDataSourceImpl.transform(args));

// process value args
args = new String[match.getValueGroupCount()];
for (int i = 0; i < args.length; i++) {
args[i] = match.getValue(i);
}
String value = valueFormat.format(args);
String value = valueFormat.format(valueDataSourceImpl.transform(args));

// put the tag
tags.put(key, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.openstreetmap.osmosis.core.container.v0_6.EntityContainer;
import org.openstreetmap.osmosis.core.domain.common.TimestampFormat;
import org.openstreetmap.osmosis.core.domain.v0_6.Entity;
Expand Down Expand Up @@ -137,7 +136,7 @@ protected EntityContainer processEntityContainer(EntityContainer entityContainer

Map<String, String> newTags = new HashMap<String, String>();
for (Output output : translation.getOutputs()) {
output.apply(tagMap, newTags, matches);
output.apply(tagMap, newTags, matches, translation.getDataSources());
}
tagMap = newTags;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.openstreetmap.osmosis.tagtransform.DataSource;
import org.openstreetmap.osmosis.tagtransform.Matcher;
import org.openstreetmap.osmosis.tagtransform.Output;
import org.openstreetmap.osmosis.tagtransform.TTEntityType;
Expand All @@ -32,7 +35,7 @@ public List<Translation> load(String configFile) {

NodeList translationElements = doc.getDocumentElement().getElementsByTagName("translation");
for (int i = 0; i < translationElements.getLength(); i++) {
Translation t = parseTranslation((Element) translationElements.item(i));
Translation t = parseTranslation(file.getParentFile(), (Element) translationElements.item(i));
if (t != null) {
translations.add(t);
}
Expand All @@ -44,12 +47,13 @@ public List<Translation> load(String configFile) {
}


private Translation parseTranslation(Element element) {
private Translation parseTranslation(File parentDir, Element element) {
String name = "";
String description = "";
Matcher matcher = null;
Matcher finder = null;
List<Output> output = new ArrayList<Output>();
List<Output> output = new ArrayList<>();
Map<String, DataSource> dataSources = Collections.emptyMap();

NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Expand All @@ -66,6 +70,8 @@ private Translation parseTranslation(Element element) {
matcher = parseMatcher(child);
} else if (nodeName.equals("find")) {
finder = parseMatcher(child);
} else if(nodeName.equals("data")) {
dataSources = parseDataSources(parentDir, child);
} else if (nodeName.equals("output")) {
NodeList outputs = child.getChildNodes();
for (int j = 0; j < outputs.getLength(); j++) {
Expand All @@ -82,7 +88,7 @@ private Translation parseTranslation(Element element) {

if (matcher != null) {
LOG.info("New translation: " + name);
return new TranslationImpl(name, description, matcher, finder, output);
return new TranslationImpl(name, description, matcher, finder, dataSources, output);
} else {
return null;
}
Expand All @@ -101,7 +107,9 @@ private Output parseOutput(Element child) {
String k = child.getAttribute("k");
String v = child.getAttribute("v");
String m = child.getAttribute("from_match");
return new TagOutput(k, v, m);
String kd = child.getAttribute("key_datasource");
String vd = child.getAttribute("value_datasource");
return new TagOutput(k, v, m, kd, vd);
}
return null;
}
Expand All @@ -127,10 +135,10 @@ private Matcher parseMatcher(Element matcher) {
}

TTEntityType type = getType(matcher.getAttribute("type"));
if (matcher.getAttribute("user") != "") {
if (matcher.getAttribute("user") != null && !matcher.getAttribute("user").isEmpty()) {
uname = matcher.getAttribute("user");
}
if (matcher.getAttribute("uid") != "") {
if (matcher.getAttribute("uid") != null && !matcher.getAttribute("uid").isEmpty()) {
uid = Integer.parseInt(matcher.getAttribute("uid"));
}
String mode;
Expand Down Expand Up @@ -158,6 +166,28 @@ private Matcher parseMatcher(Element matcher) {
return null;
}

private Map<String, DataSource> parseDataSources(File parentDir, Element parent) {
NodeList children = parent.getChildNodes();
Map<String, DataSource> dataSources = new HashMap<>();
for (int i = 0; i < children.getLength(); i++) {
if (!(children.item(i) instanceof Element)) {
continue;
}
Element child = (Element) children.item(i);
String name = child.getNodeName();
if (name.equals("source")) {
DataSource ds;
String id = child.getAttribute("source_id");
try {
ds = DataSources.valueOf(child.getAttribute("type")).create(parentDir, child.getAttributes());
dataSources.put(id, ds);
} catch(IllegalArgumentException ex) {
Logger.getLogger(DataSourceCSV.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
return dataSources;
}

private TTEntityType getType(String type) {
if (type == null || type.isEmpty() || type.equals("all")) {
Expand Down
Loading

0 comments on commit 4a706f8

Please sign in to comment.