package btools.server; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.StringTokenizer; import java.util.TreeSet; public class SuspectManager extends Thread { private static SimpleDateFormat dfTimestampZ = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss" ); private static String formatZ( Date date ) { synchronized( dfTimestampZ ) { return dfTimestampZ.format( date ); } } private static String formatAge( File f ) { long age = System.currentTimeMillis() - f.lastModified(); long minutes = age / 60000; if ( minutes < 60 ) { return minutes + " minutes"; } long hours = minutes / 60; if ( hours < 24 ) { return hours + " hours"; } long days = hours / 24; return days + " days"; } private static String getLevelDecsription( int level ) { switch( level ) { case 30 : return "motorway"; case 28 : return "trunk"; case 26 : return "primary"; case 24 : return "secondary"; case 22 : return "tertiary"; default: return "none"; } } public static void process( String url, BufferedWriter bw ) throws IOException { bw.write( "\n" ); bw.write( "BRouter suspect manager. Help

\n" ); StringTokenizer tk = new StringTokenizer( url, "/" ); tk.nextToken(); tk.nextToken(); long id = 0L; String country = null; String filter = null; if ( tk.hasMoreTokens() ) { String ctry = tk.nextToken(); if ( new File( "suspects/suspects_" + ctry + ".txt" ).exists() ) { country = ctry; if ( tk.hasMoreTokens() ) { filter = tk.nextToken(); } } } if ( country == null ) // generate country list { bw.write( "\n" ); File[] files = new File( "suspects" ).listFiles(); TreeSet names = new TreeSet(); for ( File f : files ) { String name = f.getName(); if ( name.startsWith( "suspects_" ) && name.endsWith( ".txt" ) ) { names.add( name.substring( 9, name.length() - 4 ) ); } } for ( String ctry : names ) { String url2 = "/brouter/suspects/" + ctry; bw.write( "\n" ); } bw.write( "
" + ctry + " new  all 
\n" ); bw.write( "\n" ); bw.flush(); return; } File suspectFile = new File( "suspects/suspects_" + country + ".txt" ); if ( !suspectFile.exists() ) { bw.write( "suspect file for country '" + country + "' not found\n" ); bw.write( "\n" ); bw.flush(); return; } boolean showWatchList = false; if ( tk.hasMoreTokens() ) { String t = tk.nextToken(); if ( "watchlist".equals( t ) ) { showWatchList = true; } else { id = Long.parseLong( t ); } } if ( showWatchList ) { bw.write( "watchlist for " + country + "\n" ); bw.write( "
back to country list

\n" ); BufferedReader r = new BufferedReader( new FileReader( suspectFile ) ); for ( ;; ) { String line = r.readLine(); if ( line == null ) break; StringTokenizer tk2 = new StringTokenizer( line ); id = Long.parseLong( tk2.nextToken() ); String countryId = country + "/" + filter + "/" + id; if ( new File( "falsepositives/" + id ).exists() ) { continue; // known false positive } File confirmedEntry = new File( "confirmednegatives/" + id ); if ( !( isFixed( id, suspectFile ) && confirmedEntry.exists() ) ) { continue; } long age = System.currentTimeMillis() - confirmedEntry.lastModified(); if ( age / 1000 < 3600 * 24 * 8 ) { continue; } String hint = "   confirmed " + formatAge( confirmedEntry ) + " ago"; int ilon = (int) ( id >> 32 ); int ilat = (int) ( id & 0xffffffff ); double dlon = ( ilon - 180000000 ) / 1000000.; double dlat = ( ilat - 90000000 ) / 1000000.; String url2 = "/brouter/suspects/" + countryId; bw.write( "" + dlon + "," + dlat + "" + hint + "
\n" ); } r.close(); bw.write( "\n" ); bw.flush(); return; } String message = null; if ( tk.hasMoreTokens() ) { String command = tk.nextToken(); if ( "falsepositive".equals( command ) ) { int wps = NearRecentWps.count( id ); if ( wps < 8 ) { message = "marking false-positive requires at least 8 recent nearby waypoints from BRouter-Web, found: " + wps; } else { new File( "falsepositives/" + id ).createNewFile(); id = 0L; } } if ( "confirm".equals( command ) ) { int wps = NearRecentWps.count( id ); if ( wps < 2 ) { message = "marking confirmed requires at least 2 recent nearby waypoints from BRouter-Web, found: " + wps; } else { new File( "confirmednegatives/" + id ).createNewFile(); } } if ( "fixed".equals( command ) ) { File fixedMarker = new File( "fixedsuspects/" + id ); if ( !fixedMarker.exists() ) { fixedMarker.createNewFile(); } id = 0L; int hideDays = 0; if ( tk.hasMoreTokens() ) { String param = tk.nextToken(); hideDays = Integer.parseInt( param ); fixedMarker.setLastModified( System.currentTimeMillis() + hideDays*86400000L ); } fixedMarker.setLastModified( System.currentTimeMillis() + hideDays*86400000L ); } } if ( id != 0L ) { String countryId = country + "/" + filter + "/" + id; int ilon = (int) ( id >> 32 ); int ilat = (int) ( id & 0xffffffff ); double dlon = ( ilon - 180000000 ) / 1000000.; double dlat = ( ilat - 90000000 ) / 1000000.; String profile = "car-eco"; File configFile = new File( "configs/" + country + ".cfg" ); if ( configFile.exists() ) { BufferedReader br = new BufferedReader( new FileReader( configFile ) ); profile = br.readLine(); br.close(); } String url1 = "http://brouter.de/brouter-web/#zoom=18&lat=" + dlat + "&lon=" + dlon + "&lonlats=" + dlon + "," + dlat + "&profile=" + profile; // String url1 = "http://localhost:8080/brouter-web/#map=18/" + dlat + "/" // + dlon + "/Mapsforge Tile Server&lonlats=" + dlon + "," + dlat; String url2 = "https://www.openstreetmap.org/?mlat=" + dlat + "&mlon=" + dlon + "#map=19/" + dlat + "/" + dlon + "&layers=N"; double slon = 0.00156; double slat = 0.001; String url3 = "http://127.0.0.1:8111/load_and_zoom?left=" + ( dlon - slon ) + "&bottom=" + ( dlat - slat ) + "&right=" + ( dlon + slon ) + "&top=" + ( dlat + slat ); Date weekAgo = new Date( System.currentTimeMillis() - 604800000L ); String url4a = "https://overpass-turbo.eu/?Q=[date:"" + formatZ( weekAgo ) + "Z"];way[highway]({{bbox}});out meta geom;&C=" + dlat + ";" + dlon + ";18&R"; String url4b = "https://overpass-turbo.eu/?Q=(node(around%3A1%2C%7B%7Bcenter%7D%7D)-%3E.n%3Bway(bn.n)%3Brel(bn.n%3A%22via%22)%5Btype%3Drestriction%5D%3B)%3Bout%20meta%3B%3E%3Bout%20skel%20qt%3B&C=" + dlat + ";" + dlon + ";18&R"; String url5 = "https://tyrasd.github.io/latest-changes/#16/" + dlat + "/" + dlon; if ( message != null ) { bw.write( "" + message + "

\n" ); } bw.write( "Open in BRouter-Web

\n" ); bw.write( "Open in OpenStreetmap

\n" ); bw.write( "Open in JOSM (via remote control)

\n" ); bw.write( "Overpass: minus one week    node context

\n" ); bw.write( "Open in Latest-Changes / last week

\n" ); bw.write( "
\n" ); if ( isFixed( id, suspectFile ) ) { bw.write( "

back to watchlist

\n" ); } else { bw.write( "mark false positive (=not an issue)

\n" ); File confirmedEntry = new File( "confirmednegatives/" + id ); if ( confirmedEntry.exists() ) { String prefix = "mark as fixed

\n" ); bw.write( "hide for " ); bw.write( prefix2 + "/7\">1 week" ); bw.write( prefix2 + "/30\">1 month" ); bw.write( prefix2 + "/91\">3 months" ); bw.write( prefix2 + "/182\">6 months

\n" ); } else { bw.write( "mark as a confirmed issue

\n" ); } bw.write( "

back to issue list

\n" ); } } else { bw.write( filter + " suspect list for " + country + "\n" ); bw.write( "
see watchlist\n" ); bw.write( "
back to country list

\n" ); int maxprio = 0; for ( int pass = 1; pass <= 2; pass++ ) { if ( pass == 2 ) { bw.write( "current level: " + getLevelDecsription( maxprio ) + "

\n" ); } BufferedReader r = new BufferedReader( new FileReader( suspectFile ) ); for ( ;; ) { String line = r.readLine(); if ( line == null ) break; StringTokenizer tk2 = new StringTokenizer( line ); String idString = tk2.nextToken(); int prio = Integer.parseInt( tk2.nextToken() ); prio = ( ( prio + 1 ) / 2 ) * 2; // normalize (no link prios) if ( pass == 1 ) { if ( prio <= maxprio ) continue; } else { if ( prio < maxprio ) continue; } id = Long.parseLong( idString ); String countryId = country + "/" + filter + "/" + id; String hint = ""; if ( new File( "falsepositives/" + id ).exists() ) { continue; // known false positive } if ( isFixed( id, suspectFile ) ) { continue; // known fixed } if ( "new".equals( filter ) && new File( "suspectarchive/" + id ).exists() ) { continue; // known fixed } if ( pass == 1 ) { maxprio = prio; continue; } File confirmedEntry = new File( "confirmednegatives/" + id ); if ( confirmedEntry.exists() ) { hint = "   confirmed " + formatAge( confirmedEntry ) + " ago"; } int ilon = (int) ( id >> 32 ); int ilat = (int) ( id & 0xffffffff ); double dlon = ( ilon - 180000000 ) / 1000000.; double dlat = ( ilat - 90000000 ) / 1000000.; String url2 = "/brouter/suspects/" + countryId; bw.write( "" + dlon + "," + dlat + "" + hint + "
\n" ); } r.close(); } } bw.write( "\n" ); bw.flush(); return; } private static boolean isFixed( long id, File suspectFile ) { File fixedEntry = new File( "fixedsuspects/" + id ); return fixedEntry.exists() && fixedEntry.lastModified() > suspectFile.lastModified(); } }