package mil.nga.giat.geowave.format.stanag4676; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.TreeMap; import javax.imageio.ImageIO; import javax.imageio.stream.ImageInputStream; import mil.nga.giat.geowave.core.geotime.GeometryUtils; import mil.nga.giat.geowave.core.ingest.hdfs.mapreduce.KeyValueData; import mil.nga.giat.geowave.format.stanag4676.image.ImageChipInfo; import mil.nga.giat.geowave.format.stanag4676.image.ImageChipUtils; import mil.nga.giat.geowave.format.stanag4676.parser.TrackReader.ProcessMessage; import mil.nga.giat.geowave.format.stanag4676.parser.model.MotionEventPoint; import mil.nga.giat.geowave.format.stanag4676.parser.model.MotionImagery; import mil.nga.giat.geowave.format.stanag4676.parser.model.TrackEvent; import mil.nga.giat.geowave.format.stanag4676.parser.model.TrackMessage; import mil.nga.giat.geowave.format.stanag4676.parser.model.TrackPoint; import mil.nga.giat.geowave.format.stanag4676.parser.model.TrackRun; import org.apache.hadoop.io.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.io.BaseEncoding; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.io.WKBWriter; import mil.nga.giat.geowave.format.stanag4676.parser.model.MissionFrame; import mil.nga.giat.geowave.format.stanag4676.parser.model.MissionSummary; import mil.nga.giat.geowave.format.stanag4676.parser.model.MissionSummaryMessage; import mil.nga.giat.geowave.format.stanag4676.parser.model.ModalityType; import mil.nga.giat.geowave.format.stanag4676.parser.model.NATO4676Message; import mil.nga.giat.geowave.format.stanag4676.parser.model.ObjectClassification; import mil.nga.giat.geowave.format.stanag4676.parser.model.TrackClassification; public class IngestMessageHandler implements ProcessMessage { private final static Logger LOGGER = LoggerFactory.getLogger(IngestMessageHandler.class); private final WKBWriter wkbWriter = new WKBWriter( 3); private final static String DEFAULT_IMAGE_FORMAT = "jpg"; private final List<KeyValueData<Text, Stanag4676EventWritable>> intermediateData = new ArrayList<KeyValueData<Text, Stanag4676EventWritable>>(); public IngestMessageHandler() {} public List<KeyValueData<Text, Stanag4676EventWritable>> getIntermediateData() { return intermediateData; } // Parses events sent out by 4676 parser code - each msg is a "Track" entry // - here we extract what we want and emit it as a value to group up in the // reducer @Override public void notify( final NATO4676Message msg ) throws IOException, InterruptedException { if (msg == null) { LOGGER.error("Received null msg"); return; } if (msg instanceof TrackMessage) { TrackMessage trackMessage = (TrackMessage) msg; for (final TrackEvent evt : trackMessage.getTracks()) { if (evt.getPoints().size() > 0) { final String trackUuid = evt.getUuid().toString(); String missionUUID = evt.getMissionId(); final String comment = evt.getComment(); if ((missionUUID == null) && (comment != null)) { missionUUID = comment; } if (missionUUID == null) { /* TODO: parse mission from filename? - can provide here */ missionUUID = ""; } else { missionUUID = missionUUID.replaceAll( "Mission:", "").trim(); } final String trackNumber = evt.getTrackNumber(); String trackStatus = ""; if (evt.getStatus() != null) { trackStatus = evt.getStatus().name(); } String trackClassification = ""; if ((evt.getSecurity() != null) && (evt.getSecurity().getClassification() != null)) { trackClassification = evt.getSecurity().getClassification().name(); } final TreeMap<Long, ImageChipInfo> timesWithImageChips = new TreeMap<Long, ImageChipInfo>(); final List<MotionImagery> images = evt.getMotionImages(); // keep track of the minimum image size and use that to size // the video int width = -1; int height = -1; for (final MotionImagery imagery : images) { try { String imageChip = imagery.getImageChip(); BufferedImage img = null; if (imageChip != null && imageChip.length() > 0) { final byte[] binary = BaseEncoding.base64().decode( imageChip); final ImageInputStream stream = ImageIO .createImageInputStream(new ByteArrayInputStream( binary)); img = ImageIO.read(stream); if ((width < 0) || (img.getWidth() > width)) { width = img.getWidth(); } if ((height < 0) || (img.getHeight() > height)) { height = img.getHeight(); } } timesWithImageChips.put( imagery.getTime(), new ImageChipInfo( img, imagery.getFrameNumber(), imagery.getPixelRow(), imagery.getPixelColumn())); } catch (final Exception e) { LOGGER.warn( "Unable to write image chip to file", e); } } for (final Entry<Long, ImageChipInfo> chipInfo : timesWithImageChips.entrySet()) { final BufferedImage img = chipInfo.getValue().getImage(); if (img != null) { final BufferedImage scaledImage = toBufferedImage( img.getScaledInstance( width, height, Image.SCALE_SMOOTH), BufferedImage.TYPE_3BYTE_BGR); chipInfo.getValue().setImage( scaledImage); try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { ImageIO.write( scaledImage, DEFAULT_IMAGE_FORMAT, baos); baos.flush(); chipInfo.getValue().setImageBytes( baos.toByteArray()); } catch (final Exception e) { LOGGER.warn( "Unable to write image chip to file", e); } } } for (final TrackPoint pt : evt.getPoints().values()) { final String trackItemUUID = pt.getUuid(); final long timeStamp = pt.getEventTime(); final long endTimeStamp = -1L; final double speed = pt.getSpeed(); final double course = pt.getCourse(); String trackItemClassification = "UNKNOWN"; if ((pt.getSecurity() != null) && (pt.getSecurity().getClassification() != null)) { trackItemClassification = pt.getSecurity().getClassification().name(); } ModalityType mt = pt.getTrackPointSource(); final String trackPointSource = (mt != null) ? mt.toString() : ""; final double latitude = pt.getLocation().latitude; final double longitude = pt.getLocation().longitude; final double elevation = pt.getLocation().elevation; final byte[] geometry = wkbWriter.write(GeometryUtils.GEOMETRY_FACTORY .createPoint(new Coordinate( longitude, latitude))); double detailLatitude = Stanag4676EventWritable.NO_DETAIL; double detailLongitude = Stanag4676EventWritable.NO_DETAIL; double detailElevation = Stanag4676EventWritable.NO_DETAIL; byte[] detailGeometry = null; if (pt.getDetail() != null && pt.getDetail().getLocation() != null) { detailLatitude = pt.getDetail().getLocation().latitude; detailLongitude = pt.getDetail().getLocation().longitude; detailElevation = pt.getDetail().getLocation().elevation; detailGeometry = wkbWriter.write(GeometryUtils.GEOMETRY_FACTORY.createPoint(new Coordinate( detailLongitude, detailLatitude))); } final ImageChipInfo chipInfo = timesWithImageChips.get(timeStamp); int pixelRow = -1; int pixelColumn = -1; int frameNumber = -1; byte[] imageBytes = new byte[] {}; if (chipInfo != null) { pixelRow = chipInfo.getPixelRow(); pixelColumn = chipInfo.getPixelColumn(); frameNumber = chipInfo.getFrameNumber(); imageBytes = chipInfo.getImageBytes(); } Stanag4676EventWritable sw = new Stanag4676EventWritable(); sw.setTrackPointData( geometry, detailGeometry, imageBytes, missionUUID, trackNumber, trackUuid, trackStatus, trackClassification, trackItemUUID, trackPointSource, timeStamp, endTimeStamp, speed, course, trackItemClassification, latitude, longitude, elevation, detailLatitude, detailLongitude, detailElevation, pixelRow, pixelColumn, frameNumber); intermediateData.add(new KeyValueData<Text, Stanag4676EventWritable>( new Text( trackUuid), sw)); } for (final MotionEventPoint pt : evt.getMotionPoints().values()) { final byte[] geometry = wkbWriter.write(GeometryUtils.GEOMETRY_FACTORY .createPoint(new Coordinate( pt.getLocation().longitude, pt.getLocation().latitude))); final String trackItemUUID = pt.getUuid(); final long timeStamp = pt.getEventTime(); final long endTimeStamp = pt.getEndTime(); final double speed = pt.getSpeed(); final double course = pt.getCourse(); String trackItemClassification = "UNKNOWN"; if ((pt.getSecurity() != null) && (pt.getSecurity().getClassification() != null)) { trackItemClassification = pt.getSecurity().getClassification().name(); } final double latitude = pt.getLocation().latitude; final double longitude = pt.getLocation().longitude; final double elevation = pt.getLocation().elevation; ModalityType mt = pt.getTrackPointSource(); final String trackPointSource = (mt != null) ? mt.toString() : ""; final ImageChipInfo chipInfo = timesWithImageChips.get(timeStamp); int pixelRow = -1; int pixelColumn = -1; int frameNumber = -1; byte[] imageBytes = new byte[] {}; if (chipInfo != null) { pixelRow = chipInfo.getPixelRow(); pixelColumn = chipInfo.getPixelColumn(); frameNumber = chipInfo.getFrameNumber(); imageBytes = chipInfo.getImageBytes(); } final String motionEvent = pt.motionEvent; Stanag4676EventWritable sw = new Stanag4676EventWritable(); sw.setMotionPointData( geometry, imageBytes, missionUUID, trackNumber, trackUuid, trackStatus, trackClassification, trackItemUUID, trackPointSource, timeStamp, endTimeStamp, speed, course, trackItemClassification, latitude, longitude, elevation, pixelRow, pixelColumn, frameNumber, motionEvent); // motion events emitted, grouped by track intermediateData.add(new KeyValueData<Text, Stanag4676EventWritable>( new Text( trackUuid), sw)); } for (TrackClassification tc : evt.getClassifications()) { long objectClassTime = tc.getTime(); String objectClass = tc.classification.toString(); int objectClassConf = tc.credibility.getValueConfidence(); int objectClassRel = tc.credibility.getSourceReliability(); Stanag4676EventWritable sw = new Stanag4676EventWritable(); sw.setTrackObjectClassData( objectClassTime, objectClass, objectClassConf, objectClassRel); intermediateData.add(new KeyValueData<Text, Stanag4676EventWritable>( new Text( trackUuid), sw)); } } } } if (msg instanceof MissionSummaryMessage) { MissionSummaryMessage missionSummaryMessage = (MissionSummaryMessage) msg; MissionSummary missionSummary = missionSummaryMessage.getMissionSummary(); if (missionSummary != null && missionSummary.getCoverageArea() != null) { Polygon missionPolygon = missionSummary.getCoverageArea().getPolygon(); final byte[] missionGeometry = wkbWriter.write(missionPolygon); String missionUUID = missionSummary.getMissionId(); String missionName = missionSummary.getName(); int missionNumFrames = missionSummary.getFrames().size(); long missionStartTime = missionSummary.getStartTime(); long missionEndTime = missionSummary.getEndTime(); String missionClassification = missionSummary.getSecurity(); StringBuilder sb = new StringBuilder(); for (final ObjectClassification oc : missionSummary.getClassifications()) { if (sb.length() > 0) sb.append(","); sb.append(oc.toString()); } String activeObjectClass = sb.toString(); Stanag4676EventWritable msw = new Stanag4676EventWritable(); msw.setMissionSummaryData( missionGeometry, missionUUID, missionName, missionNumFrames, missionStartTime, missionEndTime, missionClassification, activeObjectClass); intermediateData.add(new KeyValueData<Text, Stanag4676EventWritable>( new Text( missionUUID), msw)); for (MissionFrame frame : missionSummary.getFrames()) { if (frame != null && frame.getCoverageArea() != null) { Polygon framePolygon = frame.getCoverageArea().getPolygon(); final byte[] frameGeometry = wkbWriter.write(framePolygon); long frameTimeStamp = frame.getFrameTime(); int frameNumber = frame.getFrameNumber(); Stanag4676EventWritable fsw = new Stanag4676EventWritable(); fsw.setMissionFrameData( frameGeometry, missionUUID, frameNumber, frameTimeStamp); intermediateData.add(new KeyValueData<Text, Stanag4676EventWritable>( new Text( missionUUID), fsw)); } } } } } private static BufferedImage toBufferedImage( final Image image, final int type ) { if (image instanceof BufferedImage) { return (BufferedImage) image; } return ImageChipUtils.toBufferedImage( image, type); } @Override public void notify( final TrackRun run ) { // TODO Auto-generated method stub } @Override public void initialize( final TrackRun run ) { // TODO Auto-generated method stub } }