package com.revolsys.record.io.format.moep; import java.io.BufferedInputStream; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.revolsys.geometry.cs.CoordinateSystem; import com.revolsys.geometry.cs.epsg.EpsgCoordinateSystems; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.LineString; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.impl.PointDoubleXY; import com.revolsys.gis.grid.Bcgs20000RectangularMapGrid; import com.revolsys.gis.grid.UtmRectangularMapGrid; import com.revolsys.io.FileUtil; import com.revolsys.io.IoConstants; import com.revolsys.properties.BaseObjectWithProperties; import com.revolsys.record.Record; import com.revolsys.record.RecordFactory; public class MoepBinaryIterator extends BaseObjectWithProperties implements Iterator<Record> { private static final int COMPLEX_LINE = 3; private static final int CONSTRUCTION_COMPLEX_LINE = 5; private static final int CONSTRUCTION_LINE = 4; private static final int POINT = 1; private static final int SIMPLE_LINE = 2; private static final int TEXT = 6; private char actionName; private final byte[] buffer = new byte[512]; private Point center; private byte coordinateBytes; private Record currentRecord; private final MoepDirectoryReader directoryReader; private GeometryFactory factory; private String featureCode; private byte fileType; private boolean hasNext = true; private final InputStream in; private boolean loadNextObject = true; private final String mapsheet; private String originalFileType; private final RecordFactory recordFactory; public MoepBinaryIterator(final MoepDirectoryReader directoryReader, final String fileName, final InputStream in, final RecordFactory recordFactory) { this.directoryReader = directoryReader; this.recordFactory = recordFactory; switch (fileName.charAt(fileName.length() - 5)) { case 'd': this.originalFileType = "dem"; break; case 'm': this.originalFileType = "contours"; break; case 'n': this.originalFileType = "nonPositional"; break; case 'p': this.originalFileType = "planimetric"; break; case 'g': this.originalFileType = "toponymy"; break; case 'w': this.originalFileType = "woodedArea"; break; case 's': this.originalFileType = "supplimentary"; break; default: this.originalFileType = "unknown"; break; } this.in = new BufferedInputStream(in, 10000); this.mapsheet = getMapsheetFromFileName(fileName); try { loadHeader(); } catch (final IOException e) { throw new IllegalArgumentException("file cannot be opened", e); } } @Override public void close() { FileUtil.closeSilent(this.in); } private String getMapsheetFromFileName(final String fileName) { final File file = new File(fileName); final String baseName = FileUtil.getFileNamePrefix(file); final Pattern pattern = Pattern.compile("\\d{2,3}[a-z]\\d{3}"); final Matcher matcher = pattern.matcher(baseName); if (matcher.find()) { return matcher.group(); } else { return baseName; } } @Override public boolean hasNext() { if (!this.hasNext) { return false; } else if (this.loadNextObject) { return loadNextRecord() != null; } else { return true; } } private void loadHeader() throws IOException { this.fileType = (byte)read(); if (this.fileType / 100 == 0) { this.coordinateBytes = 2; } else { this.fileType %= 100; this.coordinateBytes = 4; } String mapsheet = readString(11); mapsheet = mapsheet.replaceAll("\\.", "").toLowerCase(); final Bcgs20000RectangularMapGrid bcgsGrid = new Bcgs20000RectangularMapGrid(); final UtmRectangularMapGrid utmGrid = new UtmRectangularMapGrid(); final double latitude = bcgsGrid.getLatitude(mapsheet) + 0.05; final double longitude = bcgsGrid.getLongitude(mapsheet) - 0.1; final int crsId = utmGrid.getNad83Srid(longitude, latitude); final CoordinateSystem coordinateSystem = EpsgCoordinateSystems.getCoordinateSystem(crsId); final String submissionDateString = readString(6); final double centreX = readLEInt(this.in); final double centreY = readLEInt(this.in); this.center = new PointDoubleXY(centreX, centreY); this.factory = GeometryFactory.fixed(coordinateSystem.getCoordinateSystemId(), 1.0, 1.0, 1.0); setProperty(IoConstants.GEOMETRY_FACTORY, this.factory); } protected Record loadNextRecord() { try { return loadRecord(); } catch (final IOException e) { throw new RuntimeException(e.getMessage(), e); } } protected Record loadRecord() throws IOException { final int featureKey = read(); if (featureKey != 255) { final boolean hasFeatureCode = featureKey / 100 != 0; if (hasFeatureCode) { final String featureCode = readString(10); if (!featureCode.startsWith("HA9000")) { this.actionName = featureCode.charAt(6); this.featureCode = featureCode.substring(0, 6) + "0" + featureCode.substring(7); } else { this.actionName = 'W'; this.featureCode = featureCode; } } final int extraParams = featureKey % 100 / 10; final int featureType = featureKey % 10; final byte numBytes = (byte)read(); final Record record = this.recordFactory.newRecord(MoepConstants.RECORD_DEFINITION); record.setValue(MoepConstants.MAPSHEET_NAME, this.mapsheet); record.setValue(MoepConstants.FEATURE_CODE, this.featureCode); record.setValue(MoepConstants.ORIGINAL_FILE_TYPE, this.originalFileType); String attribute = null; if (numBytes > 0) { attribute = readString(numBytes); } switch (featureType) { case POINT: record.setValue(MoepConstants.DISPLAY_TYPE, "primary"); final Point point = readPoint(this.in); record.setGeometryValue(point); if (extraParams == 1 || extraParams == 3) { final int angleInt = readLEInt(this.in); final int angle = angleInt / 10000; record.setValue(MoepConstants.ANGLE, angle); } break; case CONSTRUCTION_LINE: case CONSTRUCTION_COMPLEX_LINE: record.setValue(MoepConstants.DISPLAY_TYPE, "constructionLine"); readLineString(extraParams, record); break; case SIMPLE_LINE: case COMPLEX_LINE: record.setValue(MoepConstants.DISPLAY_TYPE, "primaryLine"); readLineString(extraParams, record); break; case TEXT: record.setValue(MoepConstants.DISPLAY_TYPE, "primary"); final Point textPoint = readPoint(this.in); record.setGeometryValue(textPoint); if (extraParams == 1) { final int angleInt = readLEInt(this.in); final int angle = angleInt / 10000; record.setValue(MoepConstants.ANGLE, angle); } final int fontSize = readLEShort(this.in); final int numChars = read(); final String text = readString(numChars); if (attribute == null) { record.setValue(MoepConstants.FONT_NAME, "31"); record.setValue(MoepConstants.FONT_WEIGHT, "0"); } else { final String fontName = new String(attribute.substring(0, 3).trim()); record.setValue(MoepConstants.FONT_NAME, fontName); if (attribute.length() > 3) { final String other = new String( attribute.substring(3, Math.min(attribute.length(), 5)).trim()); record.setValue(MoepConstants.FONT_WEIGHT, other); } else { record.setValue(MoepConstants.FONT_WEIGHT, "0"); } if (attribute.length() > 5) { final String textGroup = new String(attribute.substring(4, 9).trim()); record.setValue(MoepConstants.TEXT_GROUP, textGroup); } } record.setValue(MoepConstants.FONT_SIZE, fontSize); record.setValue(MoepConstants.TEXT, text); break; } switch (this.actionName) { case 'W': setAdmissionHistory(record, this.actionName); break; case 'Z': setAdmissionHistory(record, this.actionName); break; case 'X': setRetirementHistory(record, this.actionName); break; case 'Y': setRetirementHistory(record, this.actionName); break; default: setAdmissionHistory(record, 'W'); break; } this.currentRecord = record; this.loadNextObject = false; return this.currentRecord; } else { close(); this.hasNext = false; return null; } } /** * Get the next data object read by this reader. * * @return The next Record. * @exception NoSuchElementException If the reader has no more data objects. */ @Override public Record next() { if (hasNext()) { this.loadNextObject = true; return this.currentRecord; } else { throw new NoSuchElementException(); } } private int read() throws IOException { return this.in.read(); } private LineString readContourLine(final int numCoords) throws IOException { final double[] coords = new double[numCoords * 2]; for (int i = 0; i < numCoords; i++) { readCoordinate(this.in, coords, i, 2); } return this.factory.lineString(2, coords); } private void readCoordinate(final InputStream in, final double[] coords, final int index, final int axisCount) throws IOException { for (int i = 0; i < 2; i++) { int coordinate; if (this.coordinateBytes == 2) { coordinate = readLEShort(in); } else { coordinate = readLEInt(in); } coords[index * 3 + i] = this.center.getCoordinate(i) + coordinate; } if (axisCount > 2) { final int z = readLEShort(in); coords[index * 3 + 2] = z; } } private int readLEInt(final InputStream in) throws IOException { final int ch1 = in.read(); final int ch2 = in.read(); final int ch3 = in.read(); final int ch4 = in.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) { throw new EOFException(); } return (ch1 << 0) + (ch2 << 8) + (ch3 << 16) + (ch4 << 24); } private short readLEShort(final InputStream in) throws IOException { final int ch1 = in.read(); final int ch2 = in.read(); if ((ch1 | ch2) < 0) { throw new EOFException(); } return (short)((ch1 << 0) + (ch2 << 8)); } private void readLineString(final int extraParams, final Record object) throws IOException { int numCoords = 0; if (extraParams == 2 || extraParams == 4) { numCoords = readLEShort(this.in); } else { numCoords = read(); } if (extraParams == 3 || extraParams == 4) { final int z = readLEShort(this.in); final LineString line = readContourLine(numCoords); object.setGeometryValue(line); object.setValue(MoepConstants.ELEVATION, new Integer(z)); } else { final LineString line = readSimpleLine(numCoords); object.setGeometryValue(line); } } private Point readPoint(final InputStream in) throws IOException { final double[] coords = new double[3]; readCoordinate(in, coords, 0, 3); return this.factory.point(coords); } private LineString readSimpleLine(final int numCoords) throws IOException { final double[] coords = new double[numCoords * 3]; for (int i = 0; i < numCoords; i++) { readCoordinate(this.in, coords, i, 3); } return this.factory.lineString(3, coords); } private String readString(final int length) throws IOException { final int read = this.in.read(this.buffer, 0, length); if (read > -1) { return new String(this.buffer, 0, read).trim(); } else { return null; } } @Override public void remove() { } private void setAdmissionHistory(final Record object, final char reasonForChange) { if (this.directoryReader != null) { object.setValue(MoepConstants.ADMIT_SOURCE_DATE, this.directoryReader.getSubmissionDate()); object.setValue(MoepConstants.ADMIT_INTEGRATION_DATE, this.directoryReader.getIntegrationDate()); object.setValue(MoepConstants.ADMIT_REVISION_KEY, this.directoryReader.getRevisionKey()); object.setValue(MoepConstants.ADMIT_SPECIFICATIONS_RELEASE, this.directoryReader.getSpecificationsRelease()); } object.setValue(MoepConstants.ADMIT_REASON_FOR_CHANGE, String.valueOf(reasonForChange)); } private void setRetirementHistory(final Record object, final char reasonForChange) { if (this.directoryReader != null) { object.setValue(MoepConstants.RETIRE_SOURCE_DATE, this.directoryReader.getSubmissionDate()); object.setValue(MoepConstants.RETIRE_INTEGRATION_DATE, this.directoryReader.getIntegrationDate()); object.setValue(MoepConstants.RETIRE_REVISION_KEY, this.directoryReader.getRevisionKey()); object.setValue(MoepConstants.RETIRE_SPECIFICATIONS_RELEASE, this.directoryReader.getSpecificationsRelease()); } object.setValue(MoepConstants.RETIRE_REASON_FOR_CHANGE, String.valueOf(reasonForChange)); } }