From 6fd5e347fa4dc6028674f8738425af22c184cb44 Mon Sep 17 00:00:00 2001 From: LO Kam Tao Leo Date: Fri, 23 Sep 2022 08:25:45 +0100 Subject: [PATCH 1/4] Skeleton for performing search --- .../leolo/map/osm/extract/model/ActionFile.java | 16 +++++++++++ .../leolo/map/osm/extract/model/SearchItem.java | 32 ++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/main/java/org/leolo/map/osm/extract/model/SearchItem.java diff --git a/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java b/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java index e5d9f34..bf5c3ac 100644 --- a/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java +++ b/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java @@ -24,6 +24,10 @@ public class ActionFile { private Map way = new Hashtable<>(); private Map node = new Hashtable<>(); + + private String baseLanguage; + private HashSet otherLanguage = new HashSet<>(); + public ActionFile(InputFile path) throws IOException, SAXException, ParserConfigurationException { log.atInfo().log("Parsing action file"); DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); @@ -86,6 +90,18 @@ public class ActionFile { continue; } } + NodeList languages = rootElement.getElementsByTagName("language"); + if(languages.getLength()>0){ + NodeList children = languages.item(0).getChildNodes(); + for(int i=0;i Date: Fri, 23 Sep 2022 21:22:21 +0100 Subject: [PATCH 2/4] Skeleton to provide search function --- .../org/leolo/map/osm/extract/SearchProvider.java | 12 +++ .../map/osm/extract/SearchProviderManager.java | 112 +++++++++++++++++++++ .../leolo/map/osm/extract/model/ActionFile.java | 27 +++++ .../leolo/map/osm/extract/model/SearchItem.java | 1 + 4 files changed, 152 insertions(+) create mode 100644 src/main/java/org/leolo/map/osm/extract/SearchProvider.java create mode 100644 src/main/java/org/leolo/map/osm/extract/SearchProviderManager.java diff --git a/src/main/java/org/leolo/map/osm/extract/SearchProvider.java b/src/main/java/org/leolo/map/osm/extract/SearchProvider.java new file mode 100644 index 0000000..acbe27b --- /dev/null +++ b/src/main/java/org/leolo/map/osm/extract/SearchProvider.java @@ -0,0 +1,12 @@ +package org.leolo.map.osm.extract; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface SearchProvider { + String[] searchKey(); +} diff --git a/src/main/java/org/leolo/map/osm/extract/SearchProviderManager.java b/src/main/java/org/leolo/map/osm/extract/SearchProviderManager.java new file mode 100644 index 0000000..c4820c8 --- /dev/null +++ b/src/main/java/org/leolo/map/osm/extract/SearchProviderManager.java @@ -0,0 +1,112 @@ +package org.leolo.map.osm.extract; + +import eu.infomas.annotation.AnnotationDetector; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.leolo.map.osm.extract.model.SearchItem; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.*; +import java.util.function.BiConsumer; + +public class SearchProviderManager { + + private static SearchProviderManager instance; + private Logger log = LogManager.getLogger(); + private Hashtable items = new Hashtable<>(); + + public static synchronized SearchProviderManager getInstance(){ + if(instance==null){ + instance = new SearchProviderManager(); + } + return instance; + } + + private SearchProviderManager(){ + AnnotationDetector.TypeReporter reporter = new AnnotationDetector.TypeReporter() { + @Override + public void reportTypeAnnotation(Class annoClass, String clazz) { + log.atDebug().log("Class {} has annotation {}", clazz, annoClass.getName()); + try { + Class typeClass = Class.forName(clazz); + if(SearchItem.class.isAssignableFrom(typeClass)){ + log.atDebug().log("Class {} is an output formatter", clazz); + SearchProvider of = (SearchProvider) typeClass.getAnnotation(SearchProvider.class); + for(String format: of.searchKey()){ + if(items.containsKey(format)){ + log.atWarn().log("Duplicated search type {}. Shared by (at least) {} and {}", + format, + typeClass.getName(), + items.get(format).getName() + ); + }else{ + items.put(format, typeClass); + log.atInfo().log("Registering class {} for search type {}", typeClass, format); + } + } + }else{ + log.atError().log("Class {} does not implements OutputFormat but have annotation @OutputFormatter", clazz); + } + } catch (ClassNotFoundException e) { + log.atError().withThrowable(e).log(e.getMessage()); + } + } + + @Override + public Class[] annotations() { + return new Class[]{SearchProvider.class}; + } + }; + final AnnotationDetector cf = new AnnotationDetector(reporter); + try { + cf.detect(); + } catch (IOException e) { + log.atError().withThrowable(e).log(e.getMessage()); + } + } + + public int size() { + return items.size(); + } + + public boolean isEmpty() { + return items.isEmpty(); + } + + public Enumeration keys() { + return items.keys(); + } + + public boolean containsKey(Object key) { + return items.containsKey(key); + } + + public Class get(Object key) { + return items.get(key); + } + + public Set keySet() { + return items.keySet(); + } + + public void forEach(BiConsumer action) { + items.forEach(action); + } + + public int getOutputFormatterCount(){ + HashSet set = new HashSet<>(); + for(Class clazz:items.values()){ + set.add(clazz); + } + return set.size(); + } + + public int getSearchProviderCount(){ + HashSet set = new HashSet<>(); + for(Class clazz:items.values()){ + set.add(clazz); + } + return set.size(); + } +} diff --git a/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java b/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java index bf5c3ac..831b588 100644 --- a/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java +++ b/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java @@ -2,6 +2,7 @@ package org.leolo.map.osm.extract.model; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.leolo.map.osm.extract.SearchProviderManager; import org.leolo.map.osm.extract.util.InputFile; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -9,11 +10,13 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; +import javax.management.ReflectionException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.util.*; public class ActionFile { @@ -27,6 +30,7 @@ public class ActionFile { private String baseLanguage; private HashSet otherLanguage = new HashSet<>(); + private Vector searches = new Vector<>(); public ActionFile(InputFile path) throws IOException, SAXException, ParserConfigurationException { log.atInfo().log("Parsing action file"); @@ -102,6 +106,29 @@ public class ActionFile { } } } + NodeList searchesNodeList = rootElement.getElementsByTagName("searches"); + if(searchesNodeList.getLength()>0){ + NodeList searches = searchesNodeList.item(0).getChildNodes(); + for(int i=0;i Date: Fri, 23 Sep 2022 21:24:38 +0100 Subject: [PATCH 3/4] A simple way to perform the search --- .../extract/format/SimpleStringSearchProvider.java | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/org/leolo/map/osm/extract/format/SimpleStringSearchProvider.java diff --git a/src/main/java/org/leolo/map/osm/extract/format/SimpleStringSearchProvider.java b/src/main/java/org/leolo/map/osm/extract/format/SimpleStringSearchProvider.java new file mode 100644 index 0000000..a27eacb --- /dev/null +++ b/src/main/java/org/leolo/map/osm/extract/format/SimpleStringSearchProvider.java @@ -0,0 +1,20 @@ +package org.leolo.map.osm.extract.format; + +import org.leolo.map.osm.extract.SearchProvider; +import org.leolo.map.osm.extract.model.SearchItem; + +@SearchProvider(searchKey = "name") +public class SimpleStringSearchProvider extends SearchItem { + + private String searchKey; + + @Override + protected boolean matchString(String target) { + return searchKey.equalsIgnoreCase(target.strip()); + } + + @Override + public void setSearchKey(String searchKey) { + this.searchKey = searchKey.strip(); + } +} From 9be4e2aadcc3e7c3b2a14c4d0f76d87826cfe8ee Mon Sep 17 00:00:00 2001 From: LO Kam Tao Leo Date: Sun, 25 Sep 2022 11:31:46 +0100 Subject: [PATCH 4/4] Performing search --- .../org/leolo/map/osm/extract/ExtractElement.java | 64 ++++++++++++++++++++-- .../extract/format/SimpleStringSearchProvider.java | 6 +- .../leolo/map/osm/extract/model/ActionFile.java | 13 +++++ .../leolo/map/osm/extract/model/SearchItem.java | 42 ++++++++++++-- 4 files changed, 114 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/leolo/map/osm/extract/ExtractElement.java b/src/main/java/org/leolo/map/osm/extract/ExtractElement.java index 358e846..739f6ab 100644 --- a/src/main/java/org/leolo/map/osm/extract/ExtractElement.java +++ b/src/main/java/org/leolo/map/osm/extract/ExtractElement.java @@ -1,9 +1,6 @@ package org.leolo.map.osm.extract; -import de.topobyte.osm4j.core.access.DefaultOsmHandler; -import de.topobyte.osm4j.core.access.OsmInputException; -import de.topobyte.osm4j.core.access.OsmIterator; -import de.topobyte.osm4j.core.access.OsmReader; +import de.topobyte.osm4j.core.access.*; import de.topobyte.osm4j.core.model.iface.*; import de.topobyte.osm4j.pbf.seq.PbfReader; import org.apache.commons.cli.*; @@ -168,6 +165,7 @@ public class ExtractElement { System.exit(3); return; } + doSearch(dbFile, af); for(ExtractItem ei:af.getRelation()){ pendingRelations.add(ei.getId()); } @@ -177,7 +175,6 @@ public class ExtractElement { for(ExtractItem ei:af.getNode()){ pendingNodes.add(ei.getId()); } - doSearch(dbFile, af); expandRelations(dbFile, 1); processWay(dbFile); processNode(dbFile); @@ -190,7 +187,62 @@ public class ExtractElement { * @param af The file which defines the action to be performed */ private void doSearch(InputFile dbFile, ActionFile af) throws IOException{ - //Placeholder + if(af.getSearches().isEmpty()){ + return; + } + OsmReader reader = new PbfReader(dbFile.getInputStream(), true); + reader.setHandler(new OsmHandler() { + @Override + public void handle(OsmBounds osmBounds) throws IOException { + + } + + @Override + public void handle(OsmNode osmNode) throws IOException { + for(SearchItem si:af.getSearches()){ + if(si.matchNode(osmNode)){ + log.atInfo().log("Matched Node {}", osmNode.getId()); + ExtractItem ei = new ExtractItem(); + ei.setId(osmNode.getId()); + af.addNode(ei); + } + } + } + + @Override + public void handle(OsmWay osmWay) throws IOException { + for(SearchItem si:af.getSearches()){ + if(si.matchWay(osmWay)){ + log.atInfo().log("Matched Way {}", osmWay.getId()); + ExtractItem ei = new ExtractItem(); + ei.setId(osmWay.getId()); + af.addWay(ei); + } + } + } + + @Override + public void handle(OsmRelation osmRelation) throws IOException { + for(SearchItem si:af.getSearches()){ + if(si.matchRelation(osmRelation)){ + log.atInfo().log("Matched Relation {}", osmRelation.getId()); + ExtractItem ei = new ExtractItem(); + ei.setId(osmRelation.getId()); + af.addRelation(ei); + } + } + } + + @Override + public void complete() throws IOException { + log.atInfo().log("Finish reading the DB file"); + } + }); + try { + reader.read(); + } catch (OsmInputException e) { + log.atError().withThrowable(e).log("Error when reading the input!"); + } } private void processNode(InputFile dbFile) throws IOException{ diff --git a/src/main/java/org/leolo/map/osm/extract/format/SimpleStringSearchProvider.java b/src/main/java/org/leolo/map/osm/extract/format/SimpleStringSearchProvider.java index a27eacb..6a6f166 100644 --- a/src/main/java/org/leolo/map/osm/extract/format/SimpleStringSearchProvider.java +++ b/src/main/java/org/leolo/map/osm/extract/format/SimpleStringSearchProvider.java @@ -9,7 +9,7 @@ public class SimpleStringSearchProvider extends SearchItem { private String searchKey; @Override - protected boolean matchString(String target) { + public boolean matchString(String target) { return searchKey.equalsIgnoreCase(target.strip()); } @@ -17,4 +17,8 @@ public class SimpleStringSearchProvider extends SearchItem { public void setSearchKey(String searchKey) { this.searchKey = searchKey.strip(); } + + @Override public String toString(){ + return "SimpleString["+searchKey+"]"; + } } diff --git a/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java b/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java index 831b588..4cbea25 100644 --- a/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java +++ b/src/main/java/org/leolo/map/osm/extract/model/ActionFile.java @@ -122,6 +122,7 @@ public class ActionFile { try { SearchItem si = (SearchItem) clazz.getConstructor().newInstance(); si.setSearchKey(child.getTextContent()); + si.setActionFile(this); this.searches.add(si); } catch (Exception e) { throw new RuntimeException(e); @@ -162,4 +163,16 @@ public class ActionFile { if(!way.containsKey(ei.getId())) way.put(ei.getId(), ei); } + + public String getBaseLanguage() { + return baseLanguage; + } + + public HashSet getOtherLanguage() { + return otherLanguage; + } + + public Vector getSearches() { + return searches; + } } diff --git a/src/main/java/org/leolo/map/osm/extract/model/SearchItem.java b/src/main/java/org/leolo/map/osm/extract/model/SearchItem.java index dc7afa7..9403fe0 100644 --- a/src/main/java/org/leolo/map/osm/extract/model/SearchItem.java +++ b/src/main/java/org/leolo/map/osm/extract/model/SearchItem.java @@ -1,33 +1,67 @@ package org.leolo.map.osm.extract.model; +import de.topobyte.osm4j.core.model.iface.OsmEntity; import de.topobyte.osm4j.core.model.iface.OsmNode; import de.topobyte.osm4j.core.model.iface.OsmRelation; import de.topobyte.osm4j.core.model.iface.OsmWay; +import java.util.HashSet; +import java.util.Set; + public abstract class SearchItem { private ActionFile af; + private HashSet tagName = new HashSet<>(); + public SearchItem(ActionFile af){ this.af = af; + setTags(); } public SearchItem(){ //Empty constructor } - protected abstract boolean matchString(String target); + protected final void setActionFile(ActionFile af){ + this.af = af; + setTags(); + } + + private void setTags(){ + tagName.add("name:"+af.getBaseLanguage()); + for(String lang:af.getOtherLanguage()){ + tagName.add("name:"+lang); + } + } + + public abstract boolean matchString(String target); public abstract void setSearchKey(String searchKey); - public boolean matchNode(OsmNode node){ + private boolean matchEntity(OsmEntity entity){ + for(int i=0;i