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