package org.pyneo.tabulae.traffic;
import android.location.Location;
import android.os.Parcelable;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import static org.pyneo.tabulae.traffic.Constants.*;
public class ReportRetriever {
private static final int TIMEOUT_CONNECT = 5000;
private static final int TIMEOUT_READ = 10000;
private static final int CACHE_TIME = 60 * 3; // time to keep the once retrieved version in seconds
// from Constants: private static final String USER_AGENT = ;
private static final boolean FOLLOW_REDIRECTS = false;
// "http://exporte.wdr.de/WDRVerkehrWebsite/map/all?zoom=6&bbox=6,50,8,51"
private static final SimpleDateFormat HUMAN_READABLE_TIMESTAMP = new SimpleDateFormat("dd.MM.yyyy', 'HH:mm' Uhr'", Locale.US);
/**
* request trafficreport, http level
*/
static Map<String, Object> request(URL url, File target, String referer) throws Exception {
if (DEBUG) Log.d(TAG, "request url=" + url);
URLConnection urlConnection = url.openConnection();
urlConnection.setConnectTimeout(TIMEOUT_CONNECT);
urlConnection.setReadTimeout(TIMEOUT_READ);
urlConnection.setRequestProperty("User-Agent", USER_AGENT);
urlConnection.setRequestProperty("Referer", referer);
if (urlConnection instanceof HttpURLConnection) {
((HttpURLConnection) urlConnection).setInstanceFollowRedirects(FOLLOW_REDIRECTS);
}
if (urlConnection instanceof HttpURLConnection
&& ((HttpURLConnection) urlConnection).getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException("http response code=" + ((HttpURLConnection) urlConnection).getResponseCode());
}
InputStream inputStream = urlConnection.getInputStream();
if ("gzip".equals(urlConnection.getContentEncoding())) {
inputStream = new GZIPInputStream(inputStream);
}
Object obj = JSONValue.parseWithException(new BufferedReader(new InputStreamReader(inputStream)));
if (!(obj instanceof JSONObject)) {
throw new Exception("Unexpected type");
}
try (final java.io.Writer out = new java.io.OutputStreamWriter(new FileOutputStream(target))) {
JSONValue.writeJSONString(obj, out);
out.flush();
}
if (DEBUG) Log.d(TAG, "request stored target=" + target);
return (JSONObject) obj;
}
/**
* request trafficreport, cache level
*/
static Map<String, Object> request(File cache_dir, String url, String referer) throws Exception {
File target = new java.io.File(cache_dir, "verkehrsdaten100-extapponly.json");
if (target.exists()) {
long filetime = target.lastModified() / 1000;
long currenttime = new Date().getTime() / 1000;
if (currenttime - filetime < CACHE_TIME) {
Object obj = JSONValue.parseWithException(new BufferedReader(new InputStreamReader(new FileInputStream(target))));
if (obj instanceof JSONObject) {
if (DEBUG) Log.d(TAG, "request loaded target=" + target);
return (JSONObject) obj;
}
target.delete();
}
}
return request(new URL(url), target, referer);
}
static class TrafficLocation extends Location {
TrafficLocation(double lat, double lon) {
super("");
setLatitude(lat);
setLongitude(lon);
}
}
public static class Incidents extends ArrayList<Incident> {
Incidents() {
}
}
public static class Incident {
String id;
String category;
String category_id;
String description;
String color;
Long debugUrgency;
List states;
String street_type;
String type_g;
List<Location> position;
Incident(
String id,
String category,
String category_id,
String description,
String color,
Long debugUrgency,
List states,
String street_type,
String type_g,
List<Location> position
) {
this.id = id;
this.category = category;
this.category_id = category_id;
this.description = description.replaceAll("[\n\r\t]", " ");
this.color = color;
this.debugUrgency = debugUrgency;
this.states = states;
this.street_type = street_type;
this.type_g = type_g;
this.position = position;
}
public String getName() {
return id;
}
public String getCategory() {
return category;
}
public String getCategoryId() {
return category_id;
}
public String getDescription() {
return description;
}
public String getColor() {
return color;
}
public Long getDebugUrgency() {
return debugUrgency;
}
public List getStates() {
return states;
}
public String getStreetType() {
return street_type;
}
public String getGeoType() {
return type_g;
}
public List<Location> getPosition() {
return position;
}
public String toString() {
return "Incident"
+ ", id=" + id
+ ", category=" + category
+ ", category_id=" + category_id
+ ", description=" + description
+ ", color=" + color
+ ", debugUrgency=" + debugUrgency
+ ", states=" + states
+ ", street_type=" + street_type
+ ", type_g=" + type_g
+ ", position=" + (position==null?null:position.get(0))
;
}
}
public static Incidents go_wdr(File cache_dir, Incidents incidents) throws Exception {
Map<String,Object> base = request(cache_dir,
"http://www.ndr.de/nachrichten/verkehr/verkehrsdaten100-extapponly.json",
"http://www.ndr.de/nachrichten/verkehr/");
String type = (String)base.get("type");
return incidents;
}
public static Incidents go_ndr(File cache_dir, Incidents incidents) throws Exception {
Map<String,Object> base = request(cache_dir,
"http://www.ndr.de/nachrichten/verkehr/verkehrsdaten100-extapponly.json",
"http://www.ndr.de/nachrichten/verkehr/");
Date human_readable_timestamp = HUMAN_READABLE_TIMESTAMP.parse((String)base.get("human_readable_timestamp"));
String type = (String)base.get("type");
if ("FeatureCollection".equals(type)) {
for (Object o: (List)base.get("features")) {
// a feature is a report of a single incident
Map m = (Map)o;
String type_f = (String)m.get("type");
String id = (String)m.get("id");
Map properties = (Map)m.get("properties");
// properties contain the textual informations
if (properties == null) {
continue;
}
String category = (String)properties.get("category");
String category_id = (String)properties.get("category_id"); // construction_area, ferry, missing_person_report, traffic_jam, transport
String description = (String)properties.get("description");
String color = (String)properties.get("color");
Long debugUrgency = (Long)properties.get("debugUrgency");
List states = (List)properties.get("states");
String street_type = (String)properties.get("street_type"); // town, street, aroad, m1road, ferry, NULL
List<Location> position = new ArrayList<>();
Map geometry = (Map)m.get("geometry");
// geometry contain lat/lon of the position of the incident
String type_g = null;
if (geometry != null) {
type_g = (String)geometry.get("type");
if ("LineString".equals(type_g)) {
List coordinates = (List)geometry.get("coordinates");
for (Object p: coordinates) {
List pair = (List)p;
position.add(new TrafficLocation((Double)pair.get(1), (Double)pair.get(0)));
}
}
else if ("Point".equals(type_g)) {
List pair = (List)geometry.get("coordinates");
position.add(new TrafficLocation((Double)pair.get(1), (Double)pair.get(0)));
}
else {
throw new Exception("unknwon geometry type_g=" + type_g);
}
}
//if ("traffic_jam".equals(category_id) && ("aroad".equals(street_type) || "m1road".equals(street_type))) {
if (position.size() > 1) {
incidents.add(new Incident(
id, category, category_id, description, color,
debugUrgency, states, street_type, type_g, position));
}
else {
Log.i(TAG, "request dropped category_id=" + category_id + ", street_type=" + street_type);
}
}
}
else {
throw new Exception("unknwon base type=" + type);
}
return incidents;
}
/** request trafficreports */
public static Incidents go(File cache_dir, Incidents incidents) throws Exception {
if (incidents == null) {
incidents = new Incidents();
}
else {
incidents.clear();
}
go_ndr(cache_dir, incidents);
//go_wdr(cache_dir, incidents);
if (DEBUG) Log.d(TAG, "go size=" + incidents.size());
return incidents;
}
}