package org.opensha2.internal; import static com.google.common.base.Strings.padEnd; import static com.google.common.base.Strings.padStart; import static org.opensha2.internal.NshmpPolygon.AK_CLIP; import static org.opensha2.internal.NshmpPolygon.CEUS_CLIP; import static org.opensha2.internal.NshmpPolygon.CONTERMINOUS_US; import static org.opensha2.internal.NshmpPolygon.CYBERSHAKE; import static org.opensha2.internal.NshmpPolygon.LA_BASIN; import static org.opensha2.internal.NshmpPolygon.NEW_MADRID; import static org.opensha2.internal.NshmpPolygon.PUGET; import static org.opensha2.internal.NshmpPolygon.SF_BAY; import static org.opensha2.internal.NshmpPolygon.UCERF3_NSHM14; import static org.opensha2.internal.NshmpPolygon.UCERF3_RELM; import static org.opensha2.internal.NshmpPolygon.WASATCH; import static org.opensha2.internal.NshmpPolygon.WUS_CLIP; import org.opensha2.geo.Location; import org.opensha2.geo.LocationList; import org.opensha2.internal.GeoJson.Feature; import org.opensha2.internal.GeoJson.FeatureCollection; import org.opensha2.internal.GeoJson.PointProperties; import org.opensha2.util.Maths; import org.opensha2.util.NamedLocation; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Optional; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Set; /** * Methods for exporting nshmp site data as CSV and GeoJSON. * * @author Peter Powers */ final class NshmpSiteFiles { private static final Gson GSON = new GsonBuilder() .setPrettyPrinting() .disableHtmlEscaping() .create(); private static final Path EXPORT_DIR = Paths.get("etc", "nshm"); /** * Updates all site list and map files in etc/nshm. */ public static void main(String[] args) throws IOException { writeNshmpSites(); writeSites("nureg", EnumSet.allOf(NuregSite.class), DEC3_FMT); writeCybershakeSites("cybershake", EnumSet.allOf(CybershakeSite.class)); writeNshmpPolys(); // writeNshmpSummaryPoly(); // writeNshmpSites_0p1(); } /* * Currently, we're exporting map regions as polygons. Although the GeoJSON * spec supports polygons with holes (and hence 3-dimensional arrays, we only * support singular polygons. Polygons render better than PointStrings in any * event. */ static void writeNshmpPolys() throws IOException { LocationList usCoords = CONTERMINOUS_US.coordinates(); Path ceusOut = EXPORT_DIR.resolve("map-ceus.geojson"); LocationList ceusBounds = CEUS_CLIP.coordinates(); writePolyJson(ceusOut, "NSHMP Central & Eastern US", usCoords, 0.1, ceusBounds); Path wusOut = EXPORT_DIR.resolve("map-wus.geojson"); LocationList wusBounds = WUS_CLIP.coordinates(); writePolyJson(wusOut, "NSHMP Western US", usCoords, 0.1, wusBounds); // TODO AK needs to be updated with proper clipping region as above // currently just mercator rectangle writePolyJson( EXPORT_DIR.resolve("map-alaska.geojson"), "Alaska", AK_CLIP.coordinates(), 0.1, null); writePolyJson( EXPORT_DIR.resolve("map-la-basin.geojson"), LA_BASIN.toString(), LA_BASIN.coordinates(), 0.05, null); writePolyJson( EXPORT_DIR.resolve("map-sf-bay.geojson"), SF_BAY.toString(), SF_BAY.coordinates(), 0.05, null); writePolyJson( EXPORT_DIR.resolve("map-puget.geojson"), PUGET.toString(), PUGET.coordinates(), 0.05, null); writePolyJson( EXPORT_DIR.resolve("map-wasatch.geojson"), WASATCH.toString(), WASATCH.coordinates(), 0.05, null); writePolyJson( EXPORT_DIR.resolve("map-new-madrid.geojson"), NEW_MADRID.toString(), NEW_MADRID.coordinates(), 0.05, null); writePolyJson( EXPORT_DIR.resolve("map-ucerf3-nshm.geojson"), UCERF3_NSHM14.toString(), UCERF3_NSHM14.coordinates(), 0.1, null); writePolyJson( EXPORT_DIR.resolve("map-ucerf3-relm.geojson"), UCERF3_RELM.toString(), UCERF3_RELM.coordinates(), 0.1, null); writePolyJson( EXPORT_DIR.resolve("map-cybershake.geojson"), CYBERSHAKE.toString(), CYBERSHAKE.coordinates(), 0.1, null); } static void writeNshmpSummaryPoly() throws IOException { Set<NshmpPolygon> polys = EnumSet.range(LA_BASIN, UCERF3_NSHM14); writePolysJson( EXPORT_DIR.resolve("map-nshmp-all.geojson"), FluentIterable.from(polys) .transform(Functions.toStringFunction()) .toList(), FluentIterable.from(polys) .transform(new Function<NshmpPolygon, LocationList>() { @Override public LocationList apply(NshmpPolygon poly) { return poly.coordinates(); } }) .toList()); } static void writePolysJson(Path out, List<String> nameList, List<LocationList> coordList) throws IOException { List<Feature> features = new ArrayList<>(); int i = 0; for (LocationList coords : coordList) { features.add(GeoJson.createPolygon( nameList.get(i++), coords, Optional.<String> absent(), Optional.of(0.1))); } FeatureCollection fc = new FeatureCollection(); fc.features = features; String json = GeoJson.cleanPoly(GSON.toJson(fc)); Files.write(out, json.getBytes(StandardCharsets.UTF_8)); } static void writePolyJson( Path out, String name, LocationList coords, double spacing, LocationList bounds) throws IOException { List<Feature> features = new ArrayList<>(); if (bounds != null) { features.add(GeoJson.createPolygon( name + " Map Extents", bounds, Optional.of(GeoJson.Value.EXTENTS), Optional.<Double> absent())); } features.add(GeoJson.createPolygon( name, coords, Optional.<String> absent(), Optional.of(spacing))); FeatureCollection fc = new FeatureCollection(); fc.features = features; String json = GeoJson.cleanPoly(GSON.toJson(fc)); Files.write(out, json.getBytes(StandardCharsets.UTF_8)); } static void writeNshmpSites_0p1() throws IOException { writeSites( "ceus-0p1", FluentIterable.from(NshmpSite.ceus()) .transform(adjustLocation_0p1()) .toList(), DEC2_FMT); writeSites( "wus-0p1", FluentIterable.from(NshmpSite.wus()) .transform(adjustLocation_0p1()) .toList(), DEC2_FMT); } static void writeNshmpSites() throws IOException { writeNshmpSites("nshmp", EnumSet.allOf(NshmpSite.class)); writeNshmpSites("ceus", NshmpSite.ceus()); writeNshmpSites("wus", NshmpSite.wus()); writeNshmpSites("nrc", NshmpSite.nrc()); writeNshmpSites("alaska", NshmpSite.alaska()); writeSites("nehrp", NshmpSite.nehrp(), DEC2_FMT); } static void writeNshmpSites(String name, Collection<NshmpSite> sites) throws IOException { List<NshmpSite> sortedSites = Lists.newArrayList(sites); Collections.sort(sortedSites, new NshmpSite.StateComparator()); writeSites(name, sortedSites, DEC2_FMT); } static void writeCybershakeSites( String name, Collection<CybershakeSite> sites) throws IOException { Path jsonOut = EXPORT_DIR.resolve("sites-" + name + ".geojson"); writeCybershakeJsonSites(jsonOut, sites, CybershakeVs30.NONE); jsonOut = EXPORT_DIR.resolve("sites-" + name + "-vs30-wills.geojson"); writeCybershakeJsonSites(jsonOut, sites, CybershakeVs30.WILLS); jsonOut = EXPORT_DIR.resolve("sites-" + name + "-vs30-cvm.geojson"); writeCybershakeJsonSites(jsonOut, sites, CybershakeVs30.CVM); Path csvOut = EXPORT_DIR.resolve("sites-" + name + ".csv"); writeCybershakeCsvSites(csvOut, sites, CybershakeVs30.NONE); csvOut = EXPORT_DIR.resolve("sites-" + name + "-vs30-wills.csv"); writeCybershakeCsvSites(csvOut, sites, CybershakeVs30.WILLS); csvOut = EXPORT_DIR.resolve("sites-" + name + "-vs30-cvm.csv"); writeCybershakeCsvSites(csvOut, sites, CybershakeVs30.CVM); } static void writeSites( String name, Collection<? extends NamedLocation> sites, DecimalFormat latLonFormat) throws IOException { Path jsonOut = EXPORT_DIR.resolve("sites-" + name + ".geojson"); writeJsonSites(jsonOut, sites); Path csvOut = EXPORT_DIR.resolve("sites-" + name + ".csv"); writeCsvSites(csvOut, sites, latLonFormat); } private static final DecimalFormat DEC2_FMT = new DecimalFormat("0.00"); private static final DecimalFormat DEC3_FMT = new DecimalFormat("0.000"); private static final DecimalFormat DEC5_FMT = new DecimalFormat("0.00000"); private static final int NAME_BUFF = 28; private static final int LON_BUFF = 8; private static final int LAT_BUFF = 7; private static final int LON_BUFF_CYBER = 10; private static final int LAT_BUFF_CYBER = 9; private enum CybershakeVs30 { NONE, WILLS, CVM; } private static void writeCsvSites( Path out, Collection<? extends NamedLocation> locs, final DecimalFormat latLonFormat) throws IOException { Iterable<String> lines = Iterables.transform(locs, new Function<NamedLocation, String>() { @Override public String apply(NamedLocation loc) { StringBuilder sb = new StringBuilder(); sb.append(padEnd(loc.toString() + ",", NAME_BUFF, ' ')); sb.append(padStart(latLonFormat.format(loc.location().lon()), LON_BUFF, ' ')); sb.append(','); sb.append(padStart(latLonFormat.format(loc.location().lat()), LAT_BUFF, ' ')); return sb.toString(); } }); String header = new StringBuilder(padEnd("name,", NAME_BUFF, ' ')) .append(padStart("lon", LON_BUFF, ' ')) .append(',') .append(padStart("lat", LAT_BUFF, ' ')) .toString(); Files.write( out, Iterables.concat(ImmutableList.of(header), lines), StandardCharsets.UTF_8); } private static void writeCybershakeCsvSites( Path out, Collection<CybershakeSite> locs, final CybershakeVs30 vs30) throws IOException { Iterable<String> lines = Iterables.transform(locs, new Function<CybershakeSite, String>() { @Override public String apply(CybershakeSite loc) { StringBuilder sb = new StringBuilder(); sb.append(padEnd(loc.toString() + ",", NAME_BUFF, ' ')); sb.append(padStart(DEC5_FMT.format(loc.location().lon()), LON_BUFF_CYBER, ' ')); sb.append(','); sb.append(padStart(DEC5_FMT.format(loc.location().lat()), LAT_BUFF_CYBER, ' ')); if (vs30 != CybershakeVs30.NONE) { double vs30value = (vs30 == CybershakeVs30.WILLS) ? loc.willsVs30() : loc.cvmVs30(); sb.append(", ").append(padStart(Double.toString(vs30value), 7, ' ')); sb.append(", ").append(DEC2_FMT.format(loc.z1p0())); sb.append(", ").append(DEC2_FMT.format(loc.z2p5())); } return sb.toString(); } }); StringBuilder header = new StringBuilder(padEnd("name,", NAME_BUFF, ' ')) .append(padStart("lon", LON_BUFF_CYBER, ' ')) .append(',') .append(padStart("lat", LAT_BUFF_CYBER, ' ')); if (vs30 != CybershakeVs30.NONE) { header.append(", vs30") .append(", z1p0") .append(", z2p5"); } Files.write( out, Iterables.concat(ImmutableList.of(header.toString()), lines), StandardCharsets.UTF_8); } private static void writeJsonSites(Path out, Collection<? extends NamedLocation> sites) throws IOException { List<Feature> features = new ArrayList<>(sites.size()); for (NamedLocation loc : sites) { features.add(GeoJson.createPoint(loc)); } FeatureCollection fc = new FeatureCollection(); fc.features = features; String json = GeoJson.cleanPoints(GSON.toJson(fc)); Files.write(out, json.getBytes(StandardCharsets.UTF_8)); } private static void writeCybershakeJsonSites( Path out, Collection<CybershakeSite> sites, CybershakeVs30 vs30) throws IOException { List<Feature> features = new ArrayList<>(sites.size()); for (CybershakeSite loc : sites) { Feature feature = GeoJson.createPoint(loc); CybershakeSiteProperties props = new CybershakeSiteProperties(); props.title = loc.name(); // props.runId = loc.runId(); if (vs30 != CybershakeVs30.NONE) { props.vs30 = (vs30 == CybershakeVs30.WILLS) ? loc.willsVs30() : loc.cvmVs30(); props.z1p0 = loc.z1p0(); props.z2p5 = loc.z2p5(); } feature.properties = props; features.add(feature); } FeatureCollection fc = new FeatureCollection(); fc.features = features; String json = GeoJson.cleanPoints(GSON.toJson(fc)); Files.write(out, json.getBytes(StandardCharsets.UTF_8)); } static class CybershakeSiteProperties extends PointProperties { // Integer runId; Double vs30; Double z1p0; Double z2p5; } private static final Function<NamedLocation, NamedLocation> adjustLocation_0p1() { return new Function<NamedLocation, NamedLocation>() { @Override public NamedLocation apply(final NamedLocation input) { return new NamedLocation() { @Override public Location location() { return Location.create( Maths.round(input.location().lat(), 1), Maths.round(input.location().lon(), 1)); } @Override public String id() { return input.id(); } @Override public String toString() { return input.toString(); } }; } }; } }