package mil.nga.giat.geowave.cli.osm.mapreduce.Convert; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.accumulo.core.data.ByteSequence; import org.apache.accumulo.core.data.Key; import org.apache.accumulo.core.data.Value; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import mil.nga.giat.geowave.cli.osm.accumulo.osmschema.ColumnFamily; import mil.nga.giat.geowave.cli.osm.accumulo.osmschema.ColumnQualifier; import mil.nga.giat.geowave.cli.osm.accumulo.osmschema.Constants; import mil.nga.giat.geowave.cli.osm.accumulo.osmschema.Schema; import mil.nga.giat.geowave.cli.osm.mapreduce.Convert.OsmProvider.OsmProvider; import mil.nga.giat.geowave.cli.osm.osmfeature.types.attributes.AttributeDefinition; import mil.nga.giat.geowave.cli.osm.osmfeature.types.features.FeatureDefinition; import mil.nga.giat.geowave.cli.osm.osmfeature.types.features.FeatureDefinitionSet; import mil.nga.giat.geowave.cli.osm.types.TypeUtils; import mil.nga.giat.geowave.cli.osm.types.generated.MemberType; import mil.nga.giat.geowave.core.geotime.GeometryUtils; import mil.nga.giat.geowave.core.store.data.field.FieldReader; import mil.nga.giat.geowave.core.store.data.field.FieldUtils; public class SimpleFeatureGenerator { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleFeatureGenerator.class); public List<SimpleFeature> mapOSMtoSimpleFeature( final Map<Key, Value> items, final OsmProvider osmProvider ) { final List<SimpleFeature> features = new ArrayList<>(); final OSMUnion osmunion = new OSMUnion( items); for (final FeatureDefinition fd : FeatureDefinitionSet.Features) { String mappingKey = null; String mappingVal = null; boolean matched = false; for (final String mapper : fd.mappingKeys) { if (osmunion.tags != null) { // later handle relations where // tags on on ways for (final Map.Entry<String, String> tag : osmunion.tags.entrySet()) { if (tag.getKey().equals( mapper)) { if ((fd.mappings != null) && (fd.mappings.size() > 0)) { if (fd.isMappedValue(tag.getValue())) { matched = true; mappingVal = tag.getValue(); } } if ((fd.subMappings != null) && (fd.subMappings.size() > 0)) { final String subval = fd.getSubMappingClass( tag.getKey(), tag.getValue()); if (subval != null) { mappingKey = subval; mappingVal = tag.getValue(); matched = true; } } } } } } if (!matched) { continue; } // feature matches this osm entry, let's being final SimpleFeatureType sft = FeatureDefinitionSet.featureTypes.get(fd.name); final SimpleFeatureBuilder sfb = new SimpleFeatureBuilder( sft); for (final AttributeDefinition ad : fd.attributes) { if (ad.type.equals("id")) { sfb.set( FeatureDefinitionSet.normalizeOsmNames(ad.name), ad.convert(osmunion.Id)); } else if (ad.type.equals("geometry") || ad.type.equals("validated_geometry")) { final Geometry geom = getGeometry( osmunion, osmProvider, fd); if (geom == null) { LOGGER.error( "Unable to generate geometry for {} of type {}", osmunion.Id, osmunion.OsmType.toString()); return null; } sfb.set( FeatureDefinitionSet.normalizeOsmNames(ad.name), geom); } else if (ad.type.equals("mapping_value")) { sfb.set( FeatureDefinitionSet.normalizeOsmNames(ad.name), ad.convert(mappingVal)); } else if (ad.type.equals("mapping_key")) { sfb.set( FeatureDefinitionSet.normalizeOsmNames(ad.name), ad.convert(mappingKey)); } else if ((ad.key != null) && !ad.key.equals("null")) { if (osmunion.tags.containsKey(ad.key)) { sfb.set( FeatureDefinitionSet.normalizeOsmNames(ad.name), ad.convert(osmunion.tags.get(ad.key))); } } } features.add(sfb.buildFeature(String.valueOf(osmunion.Id) + osmunion.OsmType.toString())); } return features; } private static Geometry getGeometry( final OSMUnion osm, final OsmProvider provider, final FeatureDefinition fd ) { switch (osm.OsmType) { case NODE: { return GeometryUtils.GEOMETRY_FACTORY.createPoint(new Coordinate( osm.Longitude, osm.Lattitude)); } case RELATION: { return provider.processRelation( osm, fd); } case WAY: { return provider.processWay( osm, fd); } } return null; } public static enum OSMType { NODE, WAY, RELATION, UNSET } public static class OSMUnion { private final static Logger LOGGER = LoggerFactory.getLogger(OSMUnion.class); protected final FieldReader<Long> longReader = FieldUtils.getDefaultReaderForClass(Long.class); protected final FieldReader<Integer> intReader = FieldUtils.getDefaultReaderForClass(Integer.class); protected final FieldReader<String> stringReader = FieldUtils.getDefaultReaderForClass(String.class); protected final FieldReader<Double> doubleReader = FieldUtils.getDefaultReaderForClass(Double.class); protected final FieldReader<Boolean> booleanReader = FieldUtils.getDefaultReaderForClass(Boolean.class); protected final FieldReader<Calendar> calendarReader = FieldUtils.getDefaultReaderForClass(Calendar.class); // Common public Long Id = null; public Long Version = null; public Long Timestamp = null; public Long Changeset = null; public Long UserId = null; public String UserName = null; public Boolean Visible = true; // per spec - default to true // nodes public Double Lattitude = null; public Double Longitude = null; // ways public List<Long> Nodes = null; // relations public Map<Integer, RelationSet> relationSets = null; public Map<String, String> tags = null; public OSMType OsmType = OSMType.UNSET; public OSMUnion() {} public OSMUnion( final Map<Key, Value> osm ) { for (final Map.Entry<Key, Value> item : osm.entrySet()) { if (OsmType.equals(OSMType.UNSET)) { final ByteSequence CF = item.getKey().getColumnFamilyData(); if (Schema.arraysEqual( CF, ColumnFamily.NODE)) { OsmType = OSMType.NODE; } else if (Schema.arraysEqual( CF, ColumnFamily.WAY)) { OsmType = OSMType.WAY; } else if (Schema.arraysEqual( CF, ColumnFamily.RELATION)) { OsmType = OSMType.RELATION; } } final ByteSequence CQ = item.getKey().getColumnQualifierData(); if (Schema.arraysEqual( CQ, ColumnQualifier.ID)) { Id = longReader.readField(item.getValue().get()); } else if (Schema.arraysEqual( CQ, ColumnQualifier.VERSION)) { Version = longReader.readField(item.getValue().get()); } else if (Schema.arraysEqual( CQ, ColumnQualifier.TIMESTAMP)) { Timestamp = longReader.readField(item.getValue().get()); } else if (Schema.arraysEqual( CQ, ColumnQualifier.CHANGESET)) { Changeset = longReader.readField(item.getValue().get()); } else if (Schema.arraysEqual( CQ, ColumnQualifier.USER_ID)) { UserId = longReader.readField(item.getValue().get()); } else if (Schema.arraysEqual( CQ, ColumnQualifier.USER_TEXT)) { UserName = stringReader.readField(item.getValue().get()); } else if (Schema.arraysEqual( CQ, ColumnQualifier.OSM_VISIBILITY)) { Visible = booleanReader.readField(item.getValue().get()); } else if (Schema.arraysEqual( CQ, ColumnQualifier.LATITUDE)) { Lattitude = doubleReader.readField(item.getValue().get()); } else if (Schema.arraysEqual( CQ, ColumnQualifier.LONGITUDE)) { Longitude = doubleReader.readField(item.getValue().get()); } else if (Schema.arraysEqual( CQ, ColumnQualifier.REFERENCES)) { try { Nodes = TypeUtils.deserializeLongArray( item.getValue().get(), null).getIds(); } catch (final IOException e) { LOGGER.error( "Error deserializing Avro encoded Relation member set", e); } } else if (Schema.startsWith( CQ, ColumnQualifier.REFERENCE_MEMID_PREFIX.getBytes(Constants.CHARSET))) { final String s = new String( CQ.toArray(), Constants.CHARSET); final Integer id = Integer.valueOf(s.split("_")[1]); if (relationSets == null) { relationSets = new HashMap<>(); } if (!relationSets.containsKey(id)) { relationSets.put( id, new RelationSet()); } relationSets.get(id).memId = longReader.readField(item.getValue().get()); } else if (Schema.startsWith( CQ, ColumnQualifier.REFERENCE_ROLEID_PREFIX.getBytes(Constants.CHARSET))) { final String s = new String( CQ.toArray(), Constants.CHARSET); final Integer id = Integer.valueOf(s.split("_")[1]); if (relationSets == null) { relationSets = new HashMap<>(); } if (!relationSets.containsKey(id)) { relationSets.put( id, new RelationSet()); } relationSets.get(id).roleId = stringReader.readField(item.getValue().get()); } else if (Schema.startsWith( CQ, ColumnQualifier.REFERENCE_TYPE_PREFIX.getBytes(Constants.CHARSET))) { final String s = new String( CQ.toArray(), Constants.CHARSET); final Integer id = Integer.valueOf(s.split("_")[1]); if (relationSets == null) { relationSets = new HashMap<>(); } if (!relationSets.containsKey(id)) { relationSets.put( id, new RelationSet()); } switch (stringReader.readField(item.getValue().get())) { case "NODE": { relationSets.get(id).memType = MemberType.NODE; break; } case "WAY": { relationSets.get(id).memType = MemberType.WAY; break; } case "RELATION": { relationSets.get(id).memType = MemberType.RELATION; break; } default: break; } } else { // these should all be tags if (tags == null) { tags = new HashMap<>(); } tags.put( item.getKey().getColumnQualifier().toString(), new String( item.getValue().get(), Constants.CHARSET)); } } } } public static class RelationSet { public String roleId = null; public Long memId = null; public MemberType memType = null; } }