public class ShapefileLayer extends FeatureLayer {
private String url;
public ShapefileLayer(String shpPath, Envelope fullExtent) {
super(new ShapefileFeatureTable(shpPath));
url = new File(shpPath).getAbsolutePath();
this.setFullExtent(fullExtent);
this.setInitialExtent(fullExtent);
this.setVisible(true);
this.initLayer();
this.changeStatus(STATUS.INITIALIZED);
}
@Override
public double getMaxScale() {
return 100000;
}
@Override
public double getMinScale() {
return 1;
}
@Override
public String getUrl() {
return url;
}
}
mapView.addLayer(new ShapefileLayer("/mnt/sdcard2/shp/gaz.shp", fullLayerExtent));
public class ShapefileFeatureTable extends FeatureTable {
private String tableName;
private boolean hasGeometry;
private SpatialReference spatialReference;
private Map<String, Field> fields = new HashMap<String, Field>(2);
private Map<Long, Feature> features = new HashMap<Long, Feature>();
private SimpleLineSymbol sls = new SimpleLineSymbol(Color.BLUE, 8);
private long featureId = 1;
public ShapefileFeatureTable(String shapefilePath) {
tableName = shapefilePath.substring(shapefilePath.lastIndexOf('/') + 1, shapefilePath.lastIndexOf('.'));
//TODO: load from shp
fields.put("OID", new ShapefileField("OID", Field.esriFieldTypeOID, 8));
fields.put("SHAPE", new ShapefileField("SHAPE", Field.esriFieldTypeGeometry, -1));
hasGeometry = true;
spatialReference = SpatialReference.create(SpatialReference.WKID_WGS84);
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(shapefilePath);
bis = new BufferedInputStream(fis);
ShapeFileReader reader = new ShapeFileReader(bis);
AbstractShape shape = reader.next();
while(shape != null) {
this.addFeature(new ShapefileFeature(shape,
null,
spatialReference, sls));
shape = reader.next();
}
} catch (Exception e) {
}
}
@Override
public long addFeature(Feature feature) throws TableException {
if(feature instanceof ShapefileFeature) {
this.validateSchema(feature);
long id = featureId++;
((ShapefileFeature) feature).setId(id);
features.put(id, feature);
return id;
}
throw new TableException("You can only add type: ShapefileFeature!');
}
@Override
public long[] addFeatures(List<Feature> features) throws TableException {
if(features != null && features.size() > 0) {
long[] result = new long[features.size()];
int index = 0;
for(Feature feature : features) {
result[index++] = this.addFeature(feature);
}
return result;
}
return new long[0];
}
@Override
public void deleteFeature(long featureId) throws TableException {
features.remove(featureId);
}
@Override
public void deleteFeatures(long[] featureIds) throws TableException {
if(featureIds != null && featureIds.length > 0) {
for(long featureId : featureIds) {
this.deleteFeature(featureId);
}
}
}
@Override
public String getCopyright() {
return "C";
}
@Override
public Feature getFeature(long id) throws TableException {
return features.get(id);
}
@Override
public FeatureResult getFeatures(long[] ids) {
ShapefileFeatures result = new ShapefileFeatures();
if(ids != null && ids.length > 0) {
List<Object> features = new ArrayList<Object>(ids.length);
for(long id : ids) {
features.add(this.features.get(id));
}
result.setFeatures(this.features.values());
}
return result;
}
@Override
public Field getField(String fieldName) {
return fields.get(fieldName);
}
@Override
public List<Field> getFields() {
return new ArrayList<Field>(fields.values());
}
@Override
public String getTableName() {
return tableName;
}
@Override
public boolean hasGeometry() {
return hasGeometry;
}
@Override
public boolean isEditable() {
return true;
}
@Override
public Future<FeatureResult> queryFeatures(final QueryParameters queryParameters, final CallbackListener<FeatureResult> callbackListener) {
return new FutureTask<FeatureResult>(new Callable<FeatureResult>() {
public FeatureResult call() throws Exception {
try {
FeatureResult result = ShapefileFeatureTable.this.queryFeatures(queryParameters);
callbackListener.onCallback(result);
return result;
} catch(Exception e) {
callbackListener.onError(e);
throw e;
}
}
});
}
@Override
public Future<long[]> queryIds(final QueryParameters queryParameters, final CallbackListener<long[]> callbackListener) {
return new FutureTask<long[]>(new Callable<long[]>() {
public long[] call() throws Exception {
try {
FeatureResult result = ShapefileFeatureTable.this.queryFeatures(queryParameters);
long[] ids = new long[(int) result.featureCount()];
Iterator<Object> iterator = result.iterator();
int index = 0;
while(iterator.hasNext()) {
Object obj = iterator.next();
if(obj instanceof Feature) {
ids[index++] = ((Feature) obj).getId();
}
}
callbackListener.onCallback(ids);
return ids;
} catch(Exception e) {
callbackListener.onError(e);
throw e;
}
}
});
}
private FeatureResult queryFeatures(QueryParameters queryParameters) {
int maxFeatures = queryParameters.getMaxFeatures();
if(maxFeatures <= 0) {
maxFeatures = Integer.MAX_VALUE;
}
List<Feature> features = null;
if(queryParameters.getObjectIds() != null && queryParameters.getObjectIds().length > 0) {
features = new ArrayList<Feature>(Math.min(maxFeatures, queryParameters.getObjectIds().length));
for(long id : queryParameters.getObjectIds()) {
features.add(this.features.get(id));
}
}
Geometry g = queryParameters.getGeometry();
if(g != null) {
SpatialRelationship sr = queryParameters.getSpatialRelationship();
if(sr == null) {
throw new IllegalArgumentException("Nie podano typu relacji przestrzennej!");
}
if(features != null) {
features = GeometryUtils.filterFeatures(features, g, sr, maxFeatures);
} else {
features = GeometryUtils.filterFeatures(this.features, g, sr, maxFeatures);
}
}
ShapefileFeatures result = new ShapefileFeatures();
result.setFeatures(features);
return result;
}
@Override
public void updateFeature(long featureId, Feature feature) throws TableException {
this.validateSchema(feature);
if(features.containsKey(featureId)) {
features.put(featureId, feature);
}
}
@Override
public void updateFeatures(long[] ids, List<Feature> features) throws TableException {
if(ids != null && features != null && ids.length > 0 && ids.length == features.size()) {
int index = 0;
for(Feature feature : features) {
this.updateFeature(ids[index++], feature);
}
}
}
private void validateSchema(Feature feature) throws TableException {
//TODO
}
public static class ShapefileFeatures extends FeatureResult implements Iterator<Object> {
private List<Feature> features;
private int index;
private ShapefileFeatures() { }
private void setFeatures(Collection<Feature> features) {
if(features instanceof List) {
this.features = (List<Feature>)features;
} else {
this.features = new ArrayList<Feature>(features);
}
}
@Override
public Iterator<Object> iterator() {
return this;
}
@Override
public long featureCount() {
return features.size();
}
public boolean hasNext() {
return index < features.size() - 1;
}
public Object next() {
return this.hasNext() ? features.get(index++) : null;
}
public void remove() {
features.remove(index);
}
}
private static class ShapefileField extends Field {
private String name;
private int type;
private int length;
public ShapefileField(String name, int type, int length) {
this.name = name;
this.type = type;
this.length = length;
}
@Override
public String getAlias() {
return name;
}
@Override
public Domain getDomain() {
return null;
}
@Override
public int getFieldType() {
return type;
}
@Override
public String getName() {
return name;
}
@Override
public int getLength() {
return length;
}
}
}
public class ShapefileFeature implements Feature {
private long id;
private AbstractShape abstractShape;
private Geometry geometry;
private Map<String, Object> attributes;
private SpatialReference spatialReference;
private Symbol symbol;
public ShapefileFeature(AbstractShape abstractShape,
Map<String, Object> attributes, SpatialReference spatialReference,
Symbol symbol) {
this.abstractShape = abstractShape;
this.attributes = attributes;
this.spatialReference = spatialReference;
this.symbol = symbol;
}
/*package*/ void setId(long id) {
this.id = id;
}
public Object getAttributeValue(String fieldName) {
return attributes == null ? null : attributes.get(fieldName);
}
public Map<String, Object> getAttributes() {
return attributes;
}
public Geometry getGeometry() {
if(geometry == null) {
geometry = this.toGeometry(abstractShape);
}
return geometry;
}
public long getId() {
return id;
}
public SpatialReference getSpatialReference() {
return spatialReference;
}
public Symbol getSymbol() {
return symbol;
}
private Geometry toGeometry(AbstractShape ps) {
if(ps instanceof PolylineShape) {
Polyline result = new Polyline();
PointData[] points = ((PolylineShape)ps).getPoints();
for(int i=0;i<points.length - 1;++i) {
Line linePart = new Line();
linePart.setStart(new Point(points.getX(), points.getY()));
linePart.setEnd(new Point(points[i + 1].getX(), points[i + 1].getY()));
result.addSegment(linePart, result.getPathCount() == 0);
}
return result;
}
//TODO: other geometries will be implemented later
throw new IllegalStateException("Not yet implemented!");
}
}
public class GeometryUtils {
private static Map<SpatialRelationship, GeometryRelationFilter> FILTERS = new HashMap<SpatialRelationship, GeometryRelationFilter>();
static {
FILTERS.put(SpatialRelationship.CONTAINS, new GeometryRelationFilter() {
public boolean isRelationvalid(Geometry left, Geometry right, SpatialReference sr) {
return GeometryEngine.contains(left, right, sr);
}
});
FILTERS.put(SpatialRelationship.CROSSES, new GeometryRelationFilter() {
public boolean isRelationvalid(Geometry left, Geometry right, SpatialReference sr) {
return GeometryEngine.crosses(left, right, sr);
}
});
FILTERS.put(SpatialRelationship.ENVELOPE_INTERSECTS, new GeometryRelationFilter() {
private Envelope leftGeomExtent = new Envelope();
private Envelope rightGeomExtent = new Envelope();
public synchronized boolean isRelationvalid(Geometry left, Geometry right, SpatialReference sr) {
left.queryEnvelope(leftGeomExtent);
right.queryEnvelope(rightGeomExtent);
Envelope leftEnvelope = leftGeomExtent.getXMin() <= rightGeomExtent.getXMin() ? leftGeomExtent : rightGeomExtent;
Envelope rightenvelope = leftEnvelope == leftGeomExtent ? rightGeomExtent : leftGeomExtent;
if(leftEnvelope.getXMax() < rightenvelope.getXMin()) {
return false;
}
Envelope topEnvelope = leftGeomExtent.getYMax() >= rightGeomExtent.getYMax() ? leftGeomExtent : rightGeomExtent;
Envelope bottomEnvelope = topEnvelope == leftGeomExtent ? rightGeomExtent : leftGeomExtent;
return topEnvelope.getYMin() < bottomEnvelope.getYMax();
}
});
FILTERS.put(SpatialRelationship.INTERSECTS, new GeometryRelationFilter() {
public boolean isRelationvalid(Geometry left, Geometry right, SpatialReference sr) {
Geometry intersection = GeometryEngine.intersect(left, right, sr);
return intersection != null && !intersection.isEmpty();
}
});
FILTERS.put(SpatialRelationship.TOUCHES, new GeometryRelationFilter() {
public boolean isRelationvalid(Geometry left, Geometry right, SpatialReference sr) {
return GeometryEngine.touches(left, right, sr);
}
});
FILTERS.put(SpatialRelationship.WITHIN, new GeometryRelationFilter() {
public boolean isRelationvalid(Geometry left, Geometry right, SpatialReference sr) {
return GeometryEngine.within(left, right, sr);
}
});
FILTERS.put(SpatialRelationship.OVERLAPS, new GeometryRelationFilter() {
public boolean isRelationvalid(Geometry left, Geometry right, SpatialReference sr) {
if(left.getDimension() != right.getDimension()) {
Geometry intersection = GeometryEngine.intersect(left, right, sr);
return intersection.getDimension() == left.getDimension();
}
throw new IllegalArgumentException("Relacja pokrywania (overlap) jest nieokre�?lona dla różnych typów geometrii!");
}
});
FILTERS.put(SpatialRelationship.INDEX_INTERSECTS, new GeometryRelationFilter() {
public boolean isRelationvalid(Geometry left, Geometry right, SpatialReference sr) {
//TODO
throw new IllegalStateException("Relacja INDEX_INTERSECTS jest niezaimplementowana!");
}
});
}
public static List<Feature> filterFeatures(Map<Long, Feature> features,
Geometry filterGeometry, SpatialRelationship sr, int maxFeatures) {
GeometryRelationFilter filter = FILTERS.get(sr);
List<Feature> result = new ArrayList<Feature>(500 > features.size() ? 500 : features.size());
for(Entry<Long, Feature> feature : features.entrySet()) {
Feature f = feature.getValue();
if(filter.isRelationvalid(filterGeometry, f.getGeometry(), f.getSpatialReference())) {
result.add(f);
}
if(result.size() >= maxFeatures) {
break;
}
}
return result;
}
public static List<Feature> filterFeatures(Collection<Feature> features,
Geometry filterGeometry, SpatialRelationship sr, int maxFeatures) {
GeometryRelationFilter filter = FILTERS.get(sr);
List<Feature> result = new ArrayList<Feature>(500 > features.size() ? 500 : features.size());
for(Feature f : features) {
if(filter.isRelationvalid(filterGeometry, f.getGeometry(), f.getSpatialReference())) {
result.add(f);
}
if(result.size() >= maxFeatures) {
break;
}
}
return result;
}
private static interface GeometryRelationFilter {
boolean isRelationvalid(Geometry left, Geometry right, SpatialReference sr);
}
}
Hello Marcin,
Have you managed to complete your task yet? I recently started looking into it myself and need some help with it.
Steb by step process of how to make a shapetable, add a shape file and display it would be great if somebody could help out
ArcGIS Runtime SDK for Android 10.2.5 supports direct read of shape files. Feature editing is not supported at this time.
ShapefileFeatureTable | ArcGIS Android 10.2.5 API
mShapefilePath = "/shapefiles_data/Data/Points.shp"; mSdcard = Environment.getExternalStorageDirectory().getPath(); mMapView = (MapView) findViewById(R.id.mapView); mTiledlayer = new ArcGISTiledMapServiceLayer("http://services.arcgisonline.com/arcgis/rest/services/ESRI_Imagery_World_2D/MapServer"); mMapView.addLayer(mTiledlayer); try { mTable = new ShapefileFeatureTable(mSdcard+mShapefilePath); mFlayer = new FeatureLayer(mTable); mFlayer.setRenderer(new SimpleRenderer(new SimpleMarkerSymbol(Color.MAGENTA, 10, STYLE.TRIANGLE))); mMapView.addLayer(mFlayer); Log.d("**ShapefileTest**", "SpatialReference : "+ mTable.getSpatialReference()); } catch (FileNotFoundException e) { Log.d("**ShapefileTest**", "File not found in SDCard, nothing to load"); Toast.makeText(getApplicationContext(), "File not found in SDCard, nothing to load", Toast.LENGTH_LONG).show(); }
Thank you