package edu.sc.seis.sod.source.network; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.w3c.dom.Element; import com.csvreader.CsvReader; import edu.iris.Fissures.Location; import edu.iris.Fissures.LocationType; import edu.iris.Fissures.Orientation; import edu.iris.Fissures.Time; import edu.iris.Fissures.TimeRange; import edu.iris.Fissures.Unit; import edu.iris.Fissures.IfNetwork.ChannelId; import edu.iris.Fissures.IfNetwork.ChannelNotFound; import edu.iris.Fissures.IfNetwork.Instrumentation; import edu.iris.Fissures.IfNetwork.NetworkId; import edu.iris.Fissures.IfNetwork.NetworkNotFound; import edu.iris.Fissures.IfNetwork.SiteId; import edu.iris.Fissures.IfNetwork.StationId; import edu.iris.Fissures.model.QuantityImpl; import edu.iris.Fissures.model.SamplingImpl; import edu.iris.Fissures.model.TimeInterval; import edu.iris.Fissures.model.UnitImpl; import edu.iris.Fissures.network.ChannelImpl; import edu.iris.Fissures.network.NetworkAttrImpl; import edu.iris.Fissures.network.NetworkIdUtil; import edu.iris.Fissures.network.SiteImpl; import edu.iris.Fissures.network.StationIdUtil; import edu.iris.Fissures.network.StationImpl; import edu.sc.seis.fissuresUtil.cache.CacheNetworkAccess; import edu.sc.seis.fissuresUtil.display.configuration.DOMHelper; import edu.sc.seis.fissuresUtil.sac.InvalidResponse; import edu.sc.seis.seisFile.fdsnws.stationxml.Channel; import edu.sc.seis.sod.ConfigurationException; import edu.sc.seis.sod.UserConfigurationException; import edu.sc.seis.sod.source.AbstractCSVSource; import edu.sc.seis.sod.subsetter.AreaSubsetter; public class CSVNetworkSource extends AbstractCSVSource implements NetworkSource { public CSVNetworkSource(Element config) throws ConfigurationException { super(config, "CSVNetworkSource"); initStations(config); initChannels(config); } protected void initStations(Element config) throws ConfigurationException { if (DOMHelper.hasElement(config, "stationFile")) { String filename = DOMHelper.extractText(config, "stationFile"); this.csvFilename = filename; try { stations = getStationsFromReader(AreaSubsetter.makeRelativeOrRecipeDirReader(csvFilename)); } catch(FileNotFoundException e) { throw new UserConfigurationException(e.getMessage() + " as a station CSV file."); } catch(IOException e) { throw new ConfigurationException("Unable to read " + csvFilename, e); } } else if (DOMHelper.hasElement(config, "stations")) { try { stations = getStationsFromReader(new StringReader(DOMHelper.extractText(config, "stations").trim())); } catch(IOException e) { throw new ConfigurationException("Unable to read stations from:" + DOMHelper.extractText(config, "stations"), e); } } else { throw new ConfigurationException("Can't find stationFile or stations in configuration."); } networks = getNetworksFromStations(stations); } protected void initChannels(Element config) throws ConfigurationException { if (DOMHelper.hasElement(config, "channelFile")) { String filename = DOMHelper.extractText(config, "channelFile"); try { channels = getChannelsFromReader(AreaSubsetter.makeRelativeOrRecipeDirReader(filename), stations); } catch(FileNotFoundException e) { throw new UserConfigurationException(e.getMessage() + " as a channel CSV file.", e); } catch(IOException e) { throw new ConfigurationException("Unable to read " + filename, e); } } else if (DOMHelper.hasElement(config, "channels")) { try { channels = getChannelsFromReader(new StringReader(DOMHelper.extractText(config, "channels").trim()), stations); } catch(IOException e) { throw new ConfigurationException("Unable to read channels from:" + DOMHelper.extractText(config, "channels"), e); } } else { throw new ConfigurationException("Can't find channelFile or channels in configuration."); } } public CSVNetworkSource(String stationFile, String channelFile) throws ConfigurationException, FileNotFoundException, IOException { super("CSVNetworkSource"); stations = getStationsFromReader(AreaSubsetter.makeRelativeOrRecipeDirReader(stationFile)); channels = getChannelsFromReader(AreaSubsetter.makeRelativeOrRecipeDirReader(channelFile), stations); } public String getDescription() { return "CSVNetworkSource: " + csvFilename; } public List<NetworkAttrImpl> getNetworksFromStations(List<StationImpl> staList) { Map<String, NetworkAttrImpl> nets = new HashMap<String, NetworkAttrImpl>(); for (StationImpl sta : staList) { nets.put(StationIdUtil.toStringNoDates(sta.getId()), new NetworkAttrImpl(sta.getId().network_id, "", "", "")); } List<NetworkAttrImpl> out = new ArrayList<NetworkAttrImpl>(); for (NetworkAttrImpl net : nets.values()) { out.add(net); } return out; } public List<StationImpl> getStationsFromReader(Reader reader) throws IOException, FileNotFoundException, ConfigurationException { List<StationImpl> stations = new ArrayList<StationImpl>(); CsvReader csvReader = new CsvReader(reader); List<String> headers = validateHeaders(csvReader); while (csvReader.readRecord()) { // time to start populating field values // first up: the only required field... String netCode = csvReader.get(NET_CODE); String staCode = csvReader.get(CODE); float latitude = loadFloat(headers, csvReader, LATITUDE, 0); float longitude = loadFloat(headers, csvReader, LONGITUDE, 0); double elevation = loadDouble(headers, csvReader, ELEVATION, 0); double depth = loadDouble(headers, csvReader, DEPTH, 0); Unit elevationUnit = loadUnit(headers, csvReader, ELEVATION_UNITS, UnitImpl.METER); Unit depthUnit = loadUnit(headers, csvReader, DEPTH_UNITS, UnitImpl.METER); Location location = new Location(latitude, longitude, new QuantityImpl(elevation, elevationUnit), new QuantityImpl(depth, depthUnit), LocationType.GEOGRAPHIC); NetworkId netId = new NetworkId(netCode, loadTime(headers, csvReader, NET_START, DEFAULT_TIME)); Time staBegin = loadTime(headers, csvReader, START, DEFAULT_TIME); StationId staId = new StationId(netId, staCode, staBegin); StationImpl station = new StationImpl(staId, loadString(headers, csvReader, NAME, ""), location, loadString(headers, csvReader, OPERATOR, ""), loadString(headers, csvReader, DESCRIPTION, ""), loadString(headers, csvReader, COMMENT, ""), new NetworkAttrImpl(netId, "", "", "")); stations.add(station); } return stations; } protected StationImpl getStationForChannel(String netCode, String staCode) { for (StationImpl stationImpl : stations) { if (netCode.equals(stationImpl.getNetworkAttrImpl().get_code()) && staCode.equals(stationImpl.get_code())) { return stationImpl; } } return null; } public List<ChannelImpl> getChannelsFromReader(Reader reader, List<StationImpl> stations) throws IOException, FileNotFoundException, ConfigurationException { List<ChannelImpl> channels = new ArrayList<ChannelImpl>(); CsvReader csvReader = new CsvReader(reader); List<String> headers = validateHeaders(csvReader); while (csvReader.readRecord()) { String netCode = csvReader.get(NET_CODE); String staCode = csvReader.get(STATION_CODE); String siteCode = Channel.fixLocCode(csvReader.get(SITE_CODE)); String chanCode = csvReader.get(CODE); StationImpl curStation = getStationForChannel(netCode, staCode); if (curStation == null) { throw new UserConfigurationException("Station " + netCode + "." + staCode + " is not a known station. Add it to the stations section."); } Location location; if (headers.contains(LATITUDE) || headers.contains(LONGITUDE) || headers.contains(ELEVATION) || headers.contains(DEPTH)) { float latitude = loadFloat(headers, csvReader, LATITUDE, 0); float longitude = loadFloat(headers, csvReader, LONGITUDE, 0); double elevation = loadDouble(headers, csvReader, ELEVATION, 0); double depth = loadDouble(headers, csvReader, DEPTH, 0); Unit elevationUnit = loadUnit(headers, csvReader, ELEVATION_UNITS, UnitImpl.METER); Unit depthUnit = loadUnit(headers, csvReader, DEPTH_UNITS, UnitImpl.METER); location = new Location(latitude, longitude, new QuantityImpl(elevation, elevationUnit), new QuantityImpl(depth, depthUnit), LocationType.GEOGRAPHIC); } else { location = curStation.getLocation(); } Time chanBegin = loadTime(headers, csvReader, START, DEFAULT_TIME); float azimuth = loadFloat(headers, csvReader, AZIMUTH, ChannelImpl.getAzimuth(chanCode)); float dip = loadFloat(headers, csvReader, DIP, ChannelImpl.getDip(chanCode)); SamplingImpl sampling; if (headers.contains(SAMPLE_PERIOD)) { sampling = new SamplingImpl(1, new TimeInterval(loadFloat(headers, csvReader, SAMPLE_PERIOD, 1), UnitImpl.SECOND)); } else if (headers.contains(SAMPLE_PERIOD)) { sampling = new SamplingImpl(1, new TimeInterval(1 / loadFloat(headers, csvReader, SAMPLE_FREQUENCY, 1), UnitImpl.SECOND)); } else { sampling = new SamplingImpl(1, new TimeInterval(1, UnitImpl.SECOND)); } TimeRange chanTime = new TimeRange(chanBegin, loadTime(headers, csvReader, END, DEFAULT_END)); ChannelImpl channel = new ChannelImpl(new ChannelId(curStation.get_id().network_id, staCode, siteCode, chanCode, chanBegin), loadString(headers, csvReader, NAME, ""), new Orientation(azimuth, dip), sampling, chanTime, new SiteImpl(new SiteId(curStation.get_id().network_id, staCode, siteCode, chanBegin), location, curStation, "")); channels.add(channel); } return channels; } @Override public void setConstraints(NetworkQueryConstraints constraints) { // no op } public String toString() { return "CSVNetworkSource using " + csvFilename; } protected List<NetworkAttrImpl> networks; protected List<StationImpl> stations; protected List<ChannelImpl> channels; // required public static final String NET_CODE = "net.code"; public static final String STATION_CODE = "station.code"; public static final String SITE_CODE = "site.code"; public static final String CODE = "code"; // optional public static final String NET_START = "net.start"; public static final String NET_END = "net.end"; public static final String SAMPLE_PERIOD = "sampling.period"; public static final String SAMPLE_FREQUENCY = "sampling.frequency"; public static final String START = "start"; public static final String END = "end"; public static final String OPERATOR = "operator"; public static final String COMMENT = "comment"; public static final String DESCRIPTION = "description"; // defaultable public static final String AZIMUTH = "azimuth"; public static final String DIP = "dip"; @Override public List<? extends ChannelImpl> getChannels(StationImpl station) { List<ChannelImpl> out = new ArrayList<ChannelImpl>(); for (ChannelImpl chan : channels) { if (StationIdUtil.areEqual(station.getId(), chan.getStationImpl().getId())) { out.add(chan); } } return out; } @Override public Instrumentation getInstrumentation(ChannelImpl chanId) throws ChannelNotFound, InvalidResponse { throw new ChannelNotFound(); } @Override public CacheNetworkAccess getNetwork(NetworkAttrImpl attr) { return new CacheNetworkAccess(null, attr); } @Override public List<? extends CacheNetworkAccess> getNetworkByName(String name) throws NetworkNotFound { throw new NetworkNotFound(); } @Override public List<? extends NetworkAttrImpl> getNetworks() { return Collections.unmodifiableList(networks); } @Override public QuantityImpl getSensitivity(ChannelImpl chanId) throws ChannelNotFound, InvalidResponse { throw new ChannelNotFound(); } @Override public List<? extends StationImpl> getStations(NetworkAttrImpl net) { List<StationImpl> staList = new ArrayList<StationImpl>(); for (StationImpl sta : stations) { if (NetworkIdUtil.areEqual(net.getId(), sta.getId().network_id)) { staList.add(sta); } } return staList; } public String[] getFields() { return networkFields; } private static final String[] networkFields = new String[] {NET_CODE, STATION_CODE, SITE_CODE, CODE, LONGITUDE, LATITUDE, ELEVATION, ELEVATION_UNITS, DEPTH, DEPTH_UNITS, SAMPLE_PERIOD, SAMPLE_FREQUENCY, NET_START, NET_END, START, END, NAME, FE_SEIS_REGION, FE_GEO_REGION, FE_REGION, FE_REGION_TYPE, AZIMUTH, DIP, OPERATOR, COMMENT, DESCRIPTION}; @Override public TimeInterval getRefreshInterval() { return new TimeInterval(0, UnitImpl.MILLISECOND); } @Override public String getName() { if (csvFilename != null && csvFilename.length() != 0) { return csvFilename; } return "inline"; } }