OsmAnd/DataExtractionOSM/src/net/osmand/osm/util/AHSupermarketResolver.java

310 lines
12 KiB
Java
Raw Normal View History

package net.osmand.osm.util;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.UIManager;
import javax.xml.stream.XMLStreamException;
import net.osmand.Algoritms;
import net.osmand.data.DataTileManager;
import net.osmand.impl.ConsoleProgressImplementation;
import net.osmand.osm.Entity;
import net.osmand.osm.EntityInfo;
import net.osmand.osm.LatLon;
import net.osmand.osm.MapUtils;
import net.osmand.osm.Node;
import net.osmand.osm.OpeningHoursParser;
import net.osmand.osm.Entity.EntityId;
import net.osmand.osm.OpeningHoursParser.BasicDayOpeningHourRule;
import net.osmand.osm.OpeningHoursParser.OpeningHoursRule;
import net.osmand.osm.io.IOsmStorageFilter;
import net.osmand.osm.io.OsmBaseStorage;
import net.osmand.osm.io.OsmStorageWriter;
import net.osmand.swing.DataExtractionSettings;
import net.osmand.swing.MapPanel;
import net.osmand.swing.MapPointsLayer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.xml.sax.SAXException;
/**
* Downloads list of Albert Heijn supermarkets and converts them to a map.
*
* Source for json is:
* http://www.ah.nl/albertheijn/winkelinformatie/winkels
*
* @author Andrei Adamian
*
*/
public class AHSupermarketResolver {
private Log log = LogFactory.getLog(AHSupermarketResolver.class);
public Map<String, Map<String, Object>> getSupermarkets() throws IOException {
InputStream stream = openStream();
try {
JSONArray object = new JSONArray(new JSONTokener(new InputStreamReader(stream)));
return parseJSON(object);
} catch (JSONException t) {
throw new IllegalStateException("Can't read json stream",t);
} finally {
try {
stream.close();
} catch (IOException e) {
}
}
}
@SuppressWarnings("unchecked")
private Map<String, Map<String, Object>> parseJSON(JSONArray array) {
HashMap<String,Map<String, Object>> map = new LinkedHashMap<String, Map<String, Object>>();
for (int i=0;i<array.length();i++) {
try {
JSONObject object = (JSONObject) array.get(i);
String id = object.getString("no");
if (id != null) {
Iterator<String> keys = object.keys();
Map<String, Object> properties = new LinkedHashMap<String, Object>();
while (keys.hasNext()) {
String name = keys.next();
properties.put(name, object.get(name));
}
map.put(id, properties);
} else {
log.warn(String.format("Shop entry '%s' has been skipped, because 'no' property was not found", object));
}
} catch (JSONException e) {
log.warn("Can't get object at index "+i,e);
}
}
return map;
}
protected InputStream openStream() throws IOException {
URL url = new URL("http://www.ah.nl/albertheijn/winkelinformatie/winkels");
return url.openStream();
}
public static void selfTest() throws IOException {
final String json = "[{city:'Eindhoven',lat:51.443278,format:'TOGO',lng:5.480161,sunday:true,street:'Neckerspoel',hours:[{F:'0630',U:'2300',D:'09-08-2010'},{F:'0630',U:'2300',D:'10-08-2010'},{F:'0630',U:'2300',D:'11-08-2010'},{F:'0630',U:'2300',D:'12-08-2010'},{F:'0630',U:'2330',D:'13-08-2010'},{F:'0700',U:'2330',D:'14-08-2010'},{F:'0800',U:'2300',D:'15-08-2010'},{F:'0630',U:'2300',D:'16-08-2010'},{F:'0630',U:'2300',D:'17-08-2010'},{F:'0630',U:'2300',D:'18-08-2010'},{F:'0630',U:'2300',D:'19-08-2010'},{F:'0630',U:'2330',D:'20-08-2010'},{F:'0700',U:'2330',D:'21-08-2010'},{F:'0800',U:'2300',D:'22-08-2010'}],no:5816,phone:'040-2376060',zip:'5611 AD',housenr:'10'},{city:'Amsterdam',lat:52.346837,format:'TOGO',lng:4.918422,sunday:true,street:'Julianaplein',hours:[{F:'0630',U:'2359',D:'09-08-2010'},{F:'0630',U:'2359',D:'10-08-2010'},{F:'0630',U:'2359',D:'11-08-2010'},{F:'0630',U:'2359',D:'12-08-2010'},{F:'0630',U:'2359',D:'13-08-2010'},{F:'0700',U:'2359',D:'14-08-2010'},{F:'0900',U:'2359',D:'15-08-2010'},{F:'0630',U:'2359',D:'16-08-2010'},{F:'0630',U:'2359',D:'17-08-2010'},{F:'0630',U:'2359',D:'18-08-2010'},{F:'0630',U:'2359',D:'19-08-2010'},{F:'0630',U:'2359',D:'20-08-2010'},{F:'0700',U:'2359',D:'21-08-2010'},{F:'0900',U:'2359',D:'22-08-2010'}],no:5817,phone:'020-4689944',zip:'1097 DN',housenr:'1'}]";
AHSupermarketResolver resolver = new AHSupermarketResolver() {
protected InputStream openStream() throws IOException {
return new ByteArrayInputStream(json.getBytes());
}
};
Map<String, Map<String, Object>> map = resolver.getSupermarkets();
assert map.size() == 2;
assert map.containsKey("5816");
assert map.containsKey("5817");
// further checks
Map<String, Object> shop = map.get("5816");
assert "Eindhoven".equals(shop.get("city"));
shop = map.get("5817");
assert "Amsterdam".equals(shop.get("city"));
}
// this file could be retrieved using xapi
// http://xapi.openstreetmap.org/api/0.6/*[shop=supermarket][bbox=2.5,50,7.8,53.5]
public void updateOSMFile(String pathToOsmFile, String pathToModifiedFile, boolean show) throws IOException, SAXException, XMLStreamException, JSONException{
OsmBaseStorage storage = new OsmBaseStorage();
final Map<String, EntityId> winkelNumbers = new LinkedHashMap<String, EntityId>();
storage.getFilters().add(new IOsmStorageFilter(){
@Override
public boolean acceptEntityToLoad(OsmBaseStorage storage, EntityId entityId, Entity entity) {
if(entity.getTag("winkelnummer") !=null && entity.getTag("name").contains("eijn")){
winkelNumbers.put(entity.getTag("winkelnummer"), entityId);
return true;
}
// register all nodes in order to operate with ways
return true;
}
});
storage.parseOSM(new FileInputStream(pathToOsmFile), new ConsoleProgressImplementation(2), null, true);
Map<String, Map<String, Object>> supermarkets = getSupermarkets();
DataTileManager<Entity> deleted = new DataTileManager<Entity>();
for(String s : winkelNumbers.keySet()){
if(!supermarkets.containsKey(s)){
System.err.println("Shop " + s + " id=" +winkelNumbers.get(s) + " doesn't present on the site.");
EntityId e = winkelNumbers.get(s);
Entity en = storage.getRegisteredEntities().get(e);
deleted.registerObject(en.getLatLon().getLatitude(), en.getLatLon().getLongitude(),
en);
}
}
DataTileManager<Entity> notCorrelated = new DataTileManager<Entity>();
DataTileManager<Entity> notShown = new DataTileManager<Entity>();
for(String s : supermarkets.keySet()){
Map<String, Object> props = supermarkets.get(s);
if(winkelNumbers.get(s) != null){
EntityId id = winkelNumbers.get(s);
Entity e = storage.getRegisteredEntities().get(id);
EntityInfo info = storage.getRegisteredEntityInfo().get(id);
Map<String, String> newTags = new LinkedHashMap<String, String>();
String p = props.get("format")+"";
//IMPORTANT : comment what information should be updated or check
String name = "Albert Heijn";
if(!p.equals("AH")){
name += " " + p;
}
newTags.put("name", name);
newTags.put("phone", props.get("phone")+"");
newTags.put("addr:city", props.get("city")+"");
newTags.put("addr:street", props.get("street")+"");
newTags.put("addr:housenumber", props.get("housenr")+"");
newTags.put("addr:postcode", props.get("zip")+"");
JSONArray o = (JSONArray) props.get("hours");
List<OpeningHoursParser.OpeningHoursRule> rules = new ArrayList<OpeningHoursRule>();
BasicDayOpeningHourRule prev = null;
for(int i=0; i<7; i++){
JSONObject obj = o.getJSONObject(i);
if(!obj.isNull("C") && obj.getBoolean("C")){
} else {
String opened = obj.get("F")+"";
String closed = obj.get("U")+"";
int start = Integer.parseInt(opened.substring(0, 2)) * 60 + Integer.parseInt(opened.substring(2));
int end = Integer.parseInt(closed.substring(0, 2)) * 60 + Integer.parseInt(closed.substring(2));
if(prev != null && prev.getStartTime() == start && prev.getEndTime() == end){
prev.getDays()[i] = true;
} else {
BasicDayOpeningHourRule rule = new OpeningHoursParser.BasicDayOpeningHourRule();
rule.getDays()[i] = true;
rule.setStartTime(start);
rule.setEndTime(end);
prev = rule;
rules.add(rule);
}
}
}
newTags.put("opening_hours", OpeningHoursParser.toStringOpenedHours(rules));
// Check distance to info
LatLon real = new LatLon((Double)props.get("lat"), (Double) props.get("lng"));
double dist = MapUtils.getDistance(e.getLatLon(), real);
if(dist > 150){
// TODO move shop ?
System.err.println("Winkel number = " + s + " is too far from site info - " + dist + " m !!! " + real);
if(dist > 300){
notCorrelated.registerObject(real.getLatitude(), real.getLongitude(), e);
}
}
boolean changed = false;
for(String k : newTags.keySet()){
String val = newTags.get(k);
if(!Algoritms.objectEquals(val, e.getTag(k))){
e.putTag(k, val);
changed = true;
}
}
if(changed){
info.setAction("modify");
}
} else {
// TODO?
LatLon real = new LatLon((Double)props.get("lat"), (Double) props.get("lng"));
System.err.println("Winkel number = " + s + " is not found in database !!! " + real);
Node n = new Node(real.getLatitude(), real.getLongitude(), -1);
n.putTag("winkelnummer", "REG : " + s);
notShown.registerObject(real.getLatitude(), real.getLongitude(), n);
}
}
OsmStorageWriter writer = new OsmStorageWriter();
writer.saveStorage(new FileOutputStream(pathToModifiedFile), storage, null, true);
if(show){
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
final MapPanel panel = new MapPanel(DataExtractionSettings.getSettings().getTilesDirectory());
panel.setFocusable(true);
MapPointsLayer toAdd = panel.getLayer(MapPointsLayer.class);
toAdd.setPoints(notShown);
toAdd.setPointSize(5);
toAdd.setTagToShow("winkelnummer");
MapPointsLayer red = new MapPointsLayer();
red.setPoints(deleted);
red.setColor(Color.red);
red.setPointSize(5);
panel.addLayer(red);
MapPointsLayer blue = new MapPointsLayer();
blue.setPoints(notCorrelated);
blue.setColor(Color.blue);
blue.setPointSize(4);
panel.addLayer(blue);
JFrame frame = new JFrame("Map view");
frame.addWindowListener(new WindowAdapter(){
@Override
public void windowClosing(WindowEvent e) {
DataExtractionSettings settings = DataExtractionSettings.getSettings();
settings.saveDefaultLocation(panel.getLatitude(), panel.getLongitude());
settings.saveDefaultZoom(panel.getZoom());
System.exit(0);
}
});
Container content = frame.getContentPane();
content.add(panel, BorderLayout.CENTER);
JMenuBar bar = new JMenuBar();
bar.add(MapPanel.getMenuToChooseSource(panel));
frame.setJMenuBar(bar);
frame.setSize(512, 512);
frame.setVisible(true);
}
}
public static void main(String[] args) throws IOException, SAXException, XMLStreamException, JSONException {
AHSupermarketResolver resolver = new AHSupermarketResolver();
resolver.updateOSMFile("e:/Information/OSM maps/osm_map/holl_supermarket.osm", "e:/Information/OSM maps/osm_map/ams_poi_mod.osm",
true);
}
}