source: other-projects/maori-lang-detection/src/org/greenstone/atea/CountryCodeCountsMapData.java@ 33919

Last change on this file since 33919 was 33919, checked in by ak19, 4 years ago

SummaryTool now uses the CountryCodeCountsMapData.java class to generate the geojson-features files from the tables it already created using MongoDB query results. Switched over from geojson.tools to geojson.io since the latter allows passing geojson mapdata in the URL. The firefox screenshotting is still not working. But I can't even get complex geojson features to work from the commandline yet, so then there's another possible layer of complexity when running firefox as a Java process. Added jna jar files used by Greenstone's SafeProcess for launching Firefox as a Java process.

File size: 26.9 KB
Line 
1package org.greenstone.atea;
2
3import java.io.BufferedReader;
4import java.io.BufferedWriter;
5import java.io.File;
6import java.io.FileReader;
7import java.io.FileWriter;
8import java.io.Writer;
9
10import java.net.URLEncoder;
11
12import java.util.HashMap;
13import java.util.LinkedList;
14import java.util.List;
15import java.util.Map;
16
17//import java.lang.Math; //automatically imported apparently
18
19import org.apache.commons.csv.*;
20import org.apache.log4j.Logger;
21
22// Google's gson imports for parsing any kind of json
23import com.google.gson.JsonArray;
24import com.google.gson.JsonElement;
25import com.google.gson.JsonObject;
26import com.google.gson.JsonParser;
27
28// For working with GeoJSON's Simple Features in Java
29import mil.nga.sf.geojson.Feature;
30import mil.nga.sf.geojson.FeatureCollection;
31import mil.nga.sf.geojson.FeatureConverter;
32import mil.nga.sf.geojson.Geometry;
33import mil.nga.sf.geojson.MultiPoint;
34import mil.nga.sf.geojson.Polygon;
35import mil.nga.sf.geojson.Position;
36
37
38import org.greenstone.util.SafeProcess;
39
40/**
41 * Run a mongodb query that produces counts per countrycode like in the following 2 examples:
42 *
43 * 1. count of country codes for all sites
44 * db.Websites.aggregate([
45 *
46 * { $unwind: "$geoLocationCountryCode" },
47 * {
48 * $group: {
49 * _id: "$geoLocationCountryCode",
50 * count: { $sum: 1 }
51 * }
52 * },
53 * { $sort : { count : -1} }
54 * ]);
55 *
56 * Then store the mongodb query result's JSON format output in a file called "counts.json".
57 * Then run this program with counts.json as parameter
58 * Copy the geojson output into http://geojson.tools/
59 *
60 * 2. count of country codes for sites that have at least one page detected as MRI
61 *
62 * db.Websites.aggregate([
63 * {
64 * $match: {
65 * numPagesInMRI: {$gt: 0}
66 * }
67 * },
68 * { $unwind: "$geoLocationCountryCode" },
69 * {
70 * $group: {
71 * _id: {$toLower: '$geoLocationCountryCode'},
72 * count: { $sum: 1 }
73 * }
74 * },
75 * { $sort : { count : -1} }
76 * ]);
77 *
78 * Store the mongodb query result's JSON format output in a file called "counts_sitesWithPagesInMRI.json".
79 * Then run this program with counts_sitesWithPagesInMRI.json as parameter.
80 * Copy the geojson output into http://geojson.tools/
81 *
82 * ##################
83 * TO COMPILE:
84 * maori-lang-detection/src$
85 * javac -cp ".:../conf:../lib/*" org/greenstone/atea/CountryCodeCountsMapData.java
86 *
87 * TO RUN:
88 * maori-lang-detection/src$
89 * java -cp ".:../conf:../lib/*" org/greenstone/atea/CountryCodeCountsMapData ../mongodb-data/counts.json
90 *###################
91 *
92 * This class needs the gson library, and now the sf-geojson(-2.02).jar and
93 * helper jars sf(-2.02).jar and 3 jackson jars too,
94 * to create and store Simple Features geo json objects with Java.
95 * I copied the gson jar file from GS3.
96 *
97 * Simple Features GeoJSON Java
98 * https://ngageoint.github.io/simple-features-geojson-java/ - liks to API and more
99 *
100 * https://mvnrepository.com/artifact/mil.nga.sf/sf-geojson (https://github.com/ngageoint/simple-features-geojson-java/)
101 *
102 * Also need the basic data types used by the Geometry objects above:
103 * https://mvnrepository.com/artifact/mil.nga/sf (https://github.com/ngageoint/simple-features-java)
104 *
105 * Further helper jars needed (because of encountering the exception documented at
106 * stackoverflow.com/questions/36278293/java-lang-classnotfoundexception-com-fasterxml-jackson-core-jsonprocessingexcep/36279872)
107 * https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core/2.10.0
108 * https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
109 * https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations/2.10.0
110 */
111public class CountryCodeCountsMapData {
112
113 static Logger logger = Logger.getLogger(org.greenstone.atea.CountryCodeCountsMapData.class.getName());
114
115 static public final String GEOJSON_MAP_TOOL_URL = "http://geojson.io/"; //"http://geojson.tools/";
116 static private final String DATA_STR = "#data=data:application/json,";
117
118 // "http://geojson.io" has a URL API to programmatically access
119 /*
120 See http://geojson.io/ -> Help
121
122 "I'm a coder
123
124 geojson.io has an array of cli tools that make it easy to go from a GeoJSON file on your computer to geojson.io."
125
126 http://geojson.io/#geojson-io-api
127 "Geojson.io API
128
129 You can interact with geojson.io programmatically in two ways:
130
131 => URL parameters
132 Browser console"
133
134 http://geojson.io/#url-api
135 "data=data:application/json,
136
137 Open the map and load a chunk of GeoJSON data from a URL segment directly onto the map.
138 The GeoJSON data should be encoded as per encodeURIComponent(JSON.stringify(geojson_data)).
139 Example:
140
141 http://geojson.io/#data=data:application/json,%7B%22type%22%3A%22LineString%22%2C%22coordinates%22%3A%5B%5B0%2C0%5D%2C%5B10%2C10%5D%5D%7D
142 */
143
144 public static final int SUPPRESS_MAPDATA_DISPLAY = 0;
145 public static final int PRINT_MAPDATA_TO_SCREEN = 1;
146
147 //Map<String, JsonObject> countryToJsonMap;
148 JsonArray countryCodesJsonArray;
149 JsonArray countryCountsJsonArray;
150
151 // North-central Antarctica coords
152 private final double ANTARCTICA_LNG = 57.0d;
153 private final double ANTARCTICA_LAT = -70.0d;
154 // For EU coords, spot in Atlantic Ocean close to western European coast.
155 private final double EU_LNG = -20.0d;
156 private final double EU_LAT = 50.0d;
157
158 private final String geoJsonFilenameWithSuffix;
159 private final File outputFolder;
160
161
162
163 public CountryCodeCountsMapData(String countryCountsJSONFilename) throws Exception {
164
165 // work out the unique filename we're going to save the geojson files under
166 // and the folder we're going to save them into
167 File countryCountsJSONFile = new File(countryCountsJSONFilename);
168 String tailname = countryCountsJSONFile.getName();
169 this.geoJsonFilenameWithSuffix = (tailname.startsWith("counts_")) ? tailname.substring("counts_".length()) : tailname;
170 this.outputFolder = countryCountsJSONFile.getParentFile().getCanonicalFile(); // canonical resolves any .. and . in path
171
172 // locate the countrycodes.json file
173 File countryCoordsJSONFile = new File(this.getClass().getClassLoader().getResource("countrycodes.json").getFile());
174
175 // Create a map of ALL country code names to ALL the country code json objects
176 // that contain the location (lat, lng) info for each country code
177 Map<String, JsonObject> countryToJsonMap = new HashMap<String, JsonObject>();
178
179 // Parse json file of country codes and put into a JsonArray.
180 // then put into map of each country code to its JsonObject.
181 countryCodesJsonArray = parseJSONFile(countryCoordsJSONFile);
182 for(JsonElement obj : countryCodesJsonArray) {
183 JsonObject countryCodeJson = obj.getAsJsonObject();
184 countryToJsonMap.put(countryCodeJson.get("country").getAsString(), countryCodeJson);
185 }
186
187 // Parse json file of country code counts
188 // Then for each JsonObject in this file,
189 // find a match on its country code in the map created above to get a country code JsonObject
190 // Get the longitude and latitude of the JsonObject that matched that country code.
191 // Add this lng,lat location information to the current JsonObject from the counts file.
192 countryCountsJsonArray = parseJSONFile(countryCountsJSONFile);
193
194 for(JsonElement obj : countryCountsJsonArray) {
195 JsonObject json = obj.getAsJsonObject();
196 String countryCode = json.get("_id").getAsString().toUpperCase();
197 // set the property back as uppercase and with property name "countrycode" instead of "_id"
198 json.remove("_id");
199 json.addProperty("countrycode", countryCode);
200
201 int count = (int)json.get("count").getAsDouble();
202
203 //logger.info("Got country code: " + countryCode);
204 //logger.info(" count: " + count);
205
206 // locate in countryCode map
207 JsonObject countryCodeJson = countryToJsonMap.get(countryCode);
208
209 if(countryCodeJson != null) {
210 //logger.info("Found in map: " + countryCodeJson.toString());
211
212 // for geojson, want longitude then latitude
213 Double lng = countryCodeJson.get("longitude").getAsDouble();
214 Double lat = countryCodeJson.get("latitude").getAsDouble();
215 //logger.info("long: " + Double.toString(lng) + ", lat: " + Double.toString(lat));
216 String countryName = countryCodeJson.get("name").getAsString();
217
218 // let's add lat and lng fields to countryCounts object
219 json.addProperty("lng", lng); // adds Number: https://javadoc.io/static/com.google.code.gson/gson/2.8.5/com/google/gson/JsonObject.html
220 json.addProperty("lat", lat);
221 json.addProperty("region", countryName);
222
223 } else {
224 logger.info("No geolocation info found for country code " + countryCode);
225 if(countryCode.equals("EU")) {
226 logger.info(" Adding lat,lng for somewhere around Europe");
227 //logger.info("Unlisted country code: EU");
228 // add lat and lng for Europe
229 json.addProperty("lng", EU_LNG);
230 json.addProperty("lat", EU_LAT);
231 json.addProperty("region", "Europe");
232 }
233 else if(countryCode.equals("UNKNOWN")) {
234 logger.info(" Adding lat,lng for somewhere in Antarctica");
235 //logger.info("Unlisted country code: UNKNOWN");
236 // add lat and lng for Antarctica
237 json.addProperty("lng", ANTARCTICA_LNG);
238 json.addProperty("lat", ANTARCTICA_LAT);
239 json.addProperty("region", "UNKNOWN");
240 } else {
241 logger.error("ERROR: entirely unknown country code: " + countryCode);
242 }
243 }
244 }
245
246 }
247
248 /** Convert mongodb tabular output of json records stored in the given file
249 * into a JsonArray.
250 */
251 public JsonArray parseJSONFile(File file) throws Exception {
252 JsonArray jsonArray = null;
253 // read into string
254 try (
255 BufferedReader reader = new BufferedReader(new FileReader(file));
256 ) {
257 StringBuilder str = //new StringBuilder();
258 new StringBuilder("[");
259 String line;
260
261 boolean multi_line_comment = false;
262
263 while((line = reader.readLine()) != null) {
264 line = line.trim();
265
266 // ignore any single line comments nested in multi-line symbols
267 if(line.startsWith("/*") && line.endsWith("*/")) {
268 continue; // skip line
269 }
270
271 // skip multi-line comments spread over multiple lines
272 // assumes this ends on a line containing */ without further content on the line.
273 if(line.startsWith("/*") && !line.endsWith("*/")) {
274 multi_line_comment = true;
275 continue; // skip line
276 }
277 if(multi_line_comment) {
278 if(line.contains("*/")) {
279 multi_line_comment = false;
280 }
281
282 continue; // we're in a comment or at end of comment, skip line
283 }
284
285 str.append(line);
286 if(line.endsWith("}")) {
287 str.append(",\n");
288 }
289 }
290 // replace last comma with closing bracket
291 String fileContents = str.substring(0, str.length()-2) + "]";
292
293 //System.err.println("Got file:\n" + fileContents);
294
295 // https://stackoverflow.com/questions/2591098/how-to-parse-json-in-java
296 jsonArray = new JsonParser().parse(fileContents).getAsJsonArray();
297
298 } catch(Exception e) {
299 throw e;
300 }
301
302
303 return jsonArray;
304 }
305
306 /**
307 * Reading
308 * https://www.here.xyz/api/concepts/geojsonbasics/
309 * https://ngageoint.github.io/simple-features-geojson-java/docs/api/
310 *
311 * https://stackoverflow.com/questions/55621480/cant-access-coordinates-member-of-geojson-feature-collection
312 *
313 * Downloaded geojson simple features' jar file from maven, but it didn't work:
314 * a more private version of MultiPoint.java is not included in the jar file (there's only
315 * mil.nga.sf.geojson.MultiPoint , whereas
316 * mil.nga.sf.MultiPoint is missing
317 *
318 * This seems to have gone wrong at
319 * https://github.com/ngageoint/simple-features-geojson-java/tree/master/src/main/java/mil/nga/sf
320 * but the one at
321 * https://github.com/ngageoint/simple-features-java/tree/master/src/main/java/mil/nga/sf
322 * has it. So I've been trying to build that, but don't have the correct version of maven.
323 */
324 public Geometry toMultiPointGeoJson() {
325 //System.err.println("toGeoJSON() is not yet implemented.");
326
327 List<Position> points = new LinkedList<Position>();
328
329 for(JsonElement obj : this.countryCountsJsonArray) {
330 JsonObject json = obj.getAsJsonObject();
331 Double lng = json.get("lng").getAsDouble();
332 Double lat = json.get("lat").getAsDouble();
333
334 Position point = new Position(lng, lat);
335 points.add(point);
336 }
337
338 Geometry multiPoint = new MultiPoint(points);
339
340 return multiPoint;
341 }
342
343 // https://javadoc.io/static/com.google.code.gson/gson/2.8.5/index.html
344 public FeatureCollection toFeatureCollection() {
345 final int HISTOGRAM_WIDTH = 4;
346
347 FeatureCollection featureCollection = new FeatureCollection();
348
349 for(JsonElement obj : this.countryCountsJsonArray) {
350 JsonObject json = obj.getAsJsonObject();
351
352 String countryCode = json.get("countrycode").getAsString();
353 String region = json.get("region").getAsString();
354 int count = json.get("count").getAsInt();
355
356 // make a histogram for each country
357 Geometry rectangle = this.toPolygon(json, count, HISTOGRAM_WIDTH);
358
359 Feature countryFeature = new Feature(rectangle);
360 Map<String, Object> featureProperties = new HashMap<String, Object>();
361 featureProperties.put("count", new Integer(count));
362 featureProperties.put("code", countryCode);
363 featureProperties.put("region", region);
364 countryFeature.setProperties(featureProperties);
365
366 featureCollection.addFeature(countryFeature);
367 }
368
369 return featureCollection;
370 }
371
372 // create rectangular "histogram" for each country code
373 private Geometry toPolygon(JsonObject json, final int count, final int HISTOGRAM_WIDTH) {
374 int half_width = HISTOGRAM_WIDTH/2;
375 double vertical_factor = 1.0;
376
377 final Double lng = json.get("lng").getAsDouble();
378 final Double lat = json.get("lat").getAsDouble();
379
380 String countryCode = json.get("countrycode").getAsString();
381
382
383 //create the 4 corners of the rectangle
384 // West is negative, east is positive, south is negative, north is positive
385 // See http://www.learnz.org.nz/sites/learnz.org.nz/files/lat-long-geo-data-01_0.jpg
386 // But since the histograms grow vertically/northwards and we can't go past a latitude of 90,
387 // to compensate, we increase the width of the histograms by the same factor as our inability
388 // to grow northwards.
389 Double north = lat + (vertical_factor * count);
390
391 while (north > 90) {
392 // recalculate north after decreasing histogram's vertical growth
393 // by the same factor as we increase its width
394 vertical_factor = vertical_factor/2.0;
395 half_width = 2 * half_width;
396 north = lat + (vertical_factor * count);
397 }
398 Double east = lng + half_width;
399 Double west = lng - half_width;
400 Double south = lat;
401
402 List<Position> pts = recalculateAreaIfLarge(count, HISTOGRAM_WIDTH, countryCode, lat, lng, north, south, east, west);
403
404 /*
405 System.err.println("For country " + countryCode + ":");
406 System.err.println("north = " + north);
407 System.err.println("south = " + south);
408 System.err.println("east = " + east);
409 System.err.println("west = " + west + "\n");
410 System.err.println("-------------");
411 */
412
413 List<List<Position>> outerList = new LinkedList<List<Position>>();
414 if(pts != null) {
415 outerList.add(pts);
416 } else {
417
418
419 List<Position> points = new LinkedList<Position>();
420 outerList.add(points);
421
422 points.add(new Position(west, south)); // Position(lng, lat) not Position(lat, lng)
423 points.add(new Position(west, north));
424 points.add(new Position(east, north));
425 points.add(new Position(east, south));
426 }
427
428 Geometry rectangle = new Polygon(outerList);
429
430 // Coords: a List of List of Positions, see https://ngageoint.github.io/simple-features-geojson-java/docs/api/
431 // https://www.here.xyz/api/concepts/geojsonbasics/#polygon
432
433 return rectangle;
434 }
435
436 private List<Position> recalculateAreaIfLarge(final int count, final int HISTOGRAM_WIDTH, String countryCode,
437 final Double lat, final Double lng,
438 Double north, Double south, Double east, Double west) {
439 boolean recalculated = false;
440
441 // Check if we're dealing with very large numbers, in which case, we can have follow off the longitude edges
442 // Max longitude values are -180 to 180. So a max of 360 units between them. (Max latitude is -90 to 90)
443 // "Longitude is in the range -180 and +180 specifying coordinates west and east of the Prime Meridian, respectively.
444 // For reference, the Equator has a latitude of 0°, the North pole has a latitude of 90° north (written 90° N or +90°),
445 // and the South pole has a latitude of -90°."
446 if((east + Math.abs(west)) > 360 || east > 180 || west < -180) {
447 System.err.println("For country " + countryCode + ":");
448 System.err.println("north = " + north);
449 System.err.println("south = " + south);
450 System.err.println("east = " + east);
451 System.err.println("west = " + west + "\n");
452
453 int half_width = HISTOGRAM_WIDTH/2; // reset half_width
454
455 double v_tmp_count = Math.sqrt(count);
456 //double h_tmp_count = Math.floor(v_tmp_count);
457 //v_tmp_count = Math.ceil(v_tmp_count);
458 double h_tmp_count = v_tmp_count;
459System.err.println("count = " + count);
460 System.err.println("v = " + v_tmp_count);
461 System.err.println("h = " + h_tmp_count);
462 System.err.println("lat = " + lat);
463 System.err.println("lng = " + lng + "\n");
464
465 if(h_tmp_count > 90) { // 360 max width, of which each longitude
466 // is 4 units (horizontal factor = 4, and half-width is half
467 // of that). So max width/h_tmp_count allowed 90 => 360
468 // longitude on map (-180 to 180).
469 // Put the excess h_tmp_count into v_tmp_count and ensure
470 // that does not go over 90+90 = 180 max. Vertical_factor is 1.
471
472 System.err.println("Out of longitude range. Attempting to compensate...");
473
474 double diff = h_tmp_count - 80.0; // actually 90 wraps on geojson tools, 80 doesn't
475 h_tmp_count -= diff;
476 v_tmp_count = (count/h_tmp_count);
477
478 if(v_tmp_count > 180 || h_tmp_count > 90) {
479 System.err.println("Warning: still exceeded max latitude and/or longitude range");
480 }
481
482 }
483
484 System.err.println("Recalculating polygon for country with high count: " + countryCode + ".");
485 System.err.println("count = " + count);
486 System.err.println("v = " + v_tmp_count);
487 System.err.println("h = " + h_tmp_count);
488 System.err.println("lat = " + lat);
489 System.err.println("lng = " + lng + "\n");
490
491
492 north = lat + v_tmp_count;
493 south = lat;
494 east = lng + (h_tmp_count * half_width); // a certain width, half_width, represents one unit in the x axis
495 west = lng - (h_tmp_count * half_width);
496
497 /*
498 System.err.println("north = " + north);
499 System.err.println("south = " + south);
500 System.err.println("east = " + east);
501 System.err.println("west = " + west + "\n");
502 */
503
504 if(north > 90) {
505 // centre vertically on lat
506 north = lat + (v_tmp_count/2);
507 south = lat - (v_tmp_count/2);
508 }
509
510 if(west < -180.0) {
511 double h_diff = -180.0 - west; // west is a larger negative value than -180, so subtracting west from -180 produces a positive h_diff value
512 west = -180.0; // set to extreme western edge
513 east = east + h_diff;
514 }
515 else if(east > 180.0) {
516 double h_diff = east - 180.0; // the country's longitude (lng) is h_diff from the eastern edge
517 east = 180.0; // maximise eastern edge
518 west = west - h_diff; // then grow the remainder of h_tmp_count in the opposite (western/negative) direction
519 }
520
521 // NOTE: Can't centre on country, (lat,lng), as we don't know whether either of lat or lng has gone past the edge
522
523 // Hopefully we don't exceed +90/-90 lat and +/-180 longitude
524
525 recalculated = true;
526
527
528 } else if(west < -140.0) {
529 // past -140 west, the edges don't wrap well in geotools, so shift any points more west/negative than -140:
530
531 double diff = -140.0 - west;
532 west = -140.0;
533 east += diff;
534
535 recalculated = true;
536 }
537
538 if(recalculated) {
539 System.err.println("\nnorth = " + north);
540 System.err.println("south = " + south);
541 System.err.println("east = " + east);
542 System.err.println("west = " + west);
543
544
545 List<Position> points = new LinkedList<Position>();
546
547 points.add(new Position(west, south)); // Position(lng, lat) not Position(lat, lng)
548 points.add(new Position(west, north));
549 points.add(new Position(east, north));
550 points.add(new Position(east, south));
551
552 return points;
553 }
554
555 return null;
556 }
557
558 // by default, display mapdata output on screen too
559 public String writeMultiPointGeoJsonToFile() {
560 return writeMultiPointGeoJsonToFile(PRINT_MAPDATA_TO_SCREEN);
561 }
562 public String writeMultiPointGeoJsonToFile(int displayMapData) {
563 final String filename = "multipoint_" + this.geoJsonFilenameWithSuffix;
564 File outFile = new File(this.outputFolder, filename);
565
566 Geometry geometry = this.toMultiPointGeoJson();
567 String multiPointGeojsonString = FeatureConverter.toStringValue(geometry);
568 if(displayMapData == PRINT_MAPDATA_TO_SCREEN) {
569 System.err.println("\nMap data as MultiPoint geometry:\n" + multiPointGeojsonString + "\n");
570 }
571 try (
572 Writer writer = new BufferedWriter(new FileWriter(outFile));
573 ) {
574
575 // Some basic re-formatting for some immediate legibility
576 // But pasting the contents of the file (or the System.err output above)
577 // directly into http://geojson.tools/ or http://geojson.io/
578 // will instantly reformat the json perfectly anyway.
579 multiPointGeojsonString = multiPointGeojsonString.replace("[[", "\n[\n\t[");
580 multiPointGeojsonString = multiPointGeojsonString.replace("],[", "],\n\t[");
581 multiPointGeojsonString = multiPointGeojsonString.replace("]]", "]\n]");
582
583 writer.write(multiPointGeojsonString + "\n");
584 } catch(Exception e) {
585 logger.error("Unable to write multipoint geojson:\n**********************");
586 logger.error(multiPointGeojsonString);
587 logger.error("**********************\ninto file " + outFile.getAbsolutePath());
588 logger.error(e.getMessage(), e);
589 }
590
591 return outFile.getAbsolutePath();
592
593 }
594
595 // by default, display mapdata output on screen too
596 public String writeFeaturesGeoJsonToFile() {
597 return writeFeaturesGeoJsonToFile(PRINT_MAPDATA_TO_SCREEN);
598 }
599 // write out geojson features to appropriately named file
600 // If displayMapData == PRINT_MAPDATA_TO_SCREEN, then it will also be printed to screen
601 public String writeFeaturesGeoJsonToFile(int displayMapData) {
602 final String filename = "geojson-features_" + this.geoJsonFilenameWithSuffix;
603 File outFile = new File(this.outputFolder, filename);
604
605 FeatureCollection featureColl = this.toFeatureCollection();
606 String featuresGeojsonString = FeatureConverter.toStringValue(featureColl);
607 if(displayMapData == PRINT_MAPDATA_TO_SCREEN) {
608 System.err.println("\nMap data as featurecollection:\n" + featuresGeojsonString + "\n");
609 }
610 try (
611 Writer writer = new BufferedWriter(new FileWriter(outFile));
612 ) {
613
614 writer.write(featuresGeojsonString + "\n");
615 } catch(Exception e) {
616 logger.error("Unable to write multipoint geojson:\n**********************");
617 logger.error(featuresGeojsonString);
618 logger.error("**********************\ninto file " + outFile.getAbsolutePath());
619 logger.error(e.getMessage(), e);
620 }
621
622 return outFile.getAbsolutePath();
623 }
624
625
626 public String getFeaturesGeoJsonString(boolean uriEncoded) {
627 String featuresGeojsonString = FeatureConverter.toStringValue(this.toFeatureCollection());
628 if(uriEncoded) {
629 // Want to return encodeURIComponent(JSON.stringify(featuresGeojsonString));
630 // https://stackoverflow.com/questions/607176/java-equivalent-to-javascripts-encodeuricomponent-that-produces-identical-outpu
631 URLEncoder.encode(featuresGeojsonString);
632 }
633 return featuresGeojsonString;
634 }
635
636 public String getAsMapURL() {
637 boolean uriEncoded = true;
638 String url = GEOJSON_MAP_TOOL_URL + DATA_STR + getFeaturesGeoJsonString(uriEncoded);
639
640 return url;
641 }
642
643 public String geoJsonMapScreenshot(File outputFolder, String fileNamePrefix) {
644 // https://stackoverflow.com/questions/49606051/how-to-take-a-screenshot-in-firefox-headless-selenium-in-java
645
646 // https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode
647 // /path/to/firefox -P my-profile --screenshot test.jpg https://developer.mozilla.org --window-size=800,1000
648 // https://stackoverflow.com/questions/15783701/which-characters-need-to-be-escaped-when-using-bash
649
650 String mapURL = this.getAsMapURL();
651
652 String mapURLescapedForBash = mapURL.replace("\"", "\\\"");//.replace("[", "\\[").replace("]", "\\]");
653
654 File outputFile = new File(outputFolder + File.separator + fileNamePrefix+".png");
655 String outputFilePath = Utility.getFilePath(outputFile);
656
657
658 String[] cmdArgs = {
659 "firefox",
660 "--screenshot",
661 outputFilePath,
662 mapURLescapedForBash //"'" + mapURL + "'"
663 };
664
665 System.err.print("Running:");
666 for(String arg : cmdArgs) {
667 System.err.print(" " + arg);
668 }
669 System.err.println();
670
671
672 //String cmdArgs = "firefox --screenshot " + outputFilePath + " " + GEOJSON_MAP_TOOL_URL + DATA_STR;
673 //String cmdArgs = "firefox --screenshot " + outputFilePath + " " + "'" + mapURL + "'";
674 //System.err.println("Running: " + cmdArgs);
675
676 SafeProcess proc = new SafeProcess(cmdArgs);
677
678 int retVal = proc.runProcess();
679
680 logger.info("Process out: " + proc.getStdOutput());
681 logger.info("Process err: " + proc.getStdError());
682 logger.info("Screenshot process returned with: " + retVal);
683
684 return outputFilePath;
685
686 }
687
688 public int getTotalCount() {
689 int total = 0;
690 for(JsonElement obj : this.countryCountsJsonArray) {
691 JsonObject json = obj.getAsJsonObject();
692 int count = json.get("count").getAsInt();
693 total += count;
694 }
695 return total;
696 }
697
698
699 // Unfinished and unused
700 public void parseCSVFile(String filename) throws Exception {
701 File csvData = new File(filename);
702 CSVParser parser = CSVParser.parse(csvData, java.nio.charset.Charset.forName("US-ASCII"), CSVFormat.RFC4180);
703 for (CSVRecord csvRecord : parser) {
704 logger.info("Got record: " + csvRecord.toString());
705 }
706 }
707
708 public static void printUsage() {
709 System.err.println("CountryCodeCountsMapData <counts-by-countrycode-file>.json");
710 }
711
712 public static void main(String args[]) {
713 if(args.length != 1) {
714 printUsage();
715 System.exit(-1);
716 }
717
718 try {
719 File countsFile = new File(args[0]);
720 if(!countsFile.exists()) {
721 System.err.println("File " + countsFile + " does not exist");
722 System.exit(-1);
723 }
724
725 CountryCodeCountsMapData mapData = new CountryCodeCountsMapData(args[0]);
726
727 String multipointOutFileName = mapData.writeMultiPointGeoJsonToFile();
728 String featuresOutFileName = mapData.writeFeaturesGeoJsonToFile();
729
730 System.err.println("***********\nWrote mapdata to files " + multipointOutFileName
731 + " and " + featuresOutFileName);
732 System.err.println("You can paste the geojson contents of either of these files into "
733 + " the editor at " + GEOJSON_MAP_TOOL_URL
734 + " to see the data arranged on a world map");
735
736 System.err.println("Total count for query: " + mapData.getTotalCount());
737
738 } catch(Exception e) {
739 logger.error(e.getMessage(), e);
740 }
741 }
742}
Note: See TracBrowser for help on using the repository browser.