// -*- mode: java; c-basic-offset: 2; -*- // Copyright 2016-2017 MIT, All rights reserved // Released under the Apache License, Version 2.0 // http://www.apache.org/licenses/LICENSE-2.0 package com.google.appinventor.client.editor.simple.components; import com.google.appinventor.client.ErrorReporter; import com.google.appinventor.client.Ode; import com.google.appinventor.client.editor.simple.SimpleEditor; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.json.client.JSONObject; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.SimplePanel; import java.util.ArrayList; import java.util.List; public class MockFeatureCollection extends MockContainer implements MockMapFeature { public static final String TYPE = "FeatureCollection"; private static final String PROPERTY_NAME_SOURCE = "Source"; private static final String PROPERTY_NAME_FEATURESFROMGEOJSON = "FeaturesFromGeoJSON"; private MockMap map; private List<MockMapFeature> features = new ArrayList<MockMapFeature>(); private JavaScriptObject collection; private boolean initialized = false; public MockFeatureCollection(SimpleEditor editor) { super(editor, TYPE, images.featurecollection(), new MockFeatureCollectionLayout()); SimplePanel panel = new SimplePanel(); panel.setWidth("16px"); panel.setHeight("16px"); panel.setStylePrimaryName("ode-SimpleMockComponent"); Image icon = new Image(images.featurecollection()); panel.add(icon); initComponent(panel); initCollection(); initialized = true; } MockMap getMap() { return map; } @Override public void addToMap(MockMap map) { setVisible(false); // MockFeatureCollection is managed by Leaflet layout.layoutWidth = 0; layout.layoutHeight = 0; this.map = map; setContainer(map); addToMap(map.getMapInstance()); for (MockComponent component : getChildren()) { ((MockMapFeature) component).addToMap(map); } } @Override public boolean onDrop(MockMap map, int x, int y, int offsetX, int offsetY) { return true; } @Override protected boolean isPropertyVisible(String propertyName) { if (propertyName.equals(PROPERTY_NAME_WIDTH) || propertyName.equals(PROPERTY_NAME_HEIGHT)) { return false; } return super.isPropertyVisible(propertyName); } @Override public void onPropertyChange(String propertyName, String newValue) { super.onPropertyChange(propertyName, newValue); if (propertyName.equals(PROPERTY_NAME_SOURCE)) { setSourceProperty(newValue); } else if (propertyName.equals(PROPERTY_NAME_FEATURESFROMGEOJSON)) { setGeoJSONProperty(newValue); } else if (propertyName.equals(PROPERTY_NAME_VISIBLE)) { setVisibleProperty(newValue); } } @Override public void onRemoved() { for (MockMapFeature feature : features) { ((MockComponent) feature).onRemoved(); } features.clear(); } private void setSourceProperty(String text) { if (!initialized || !editor.isLoadComplete()) { return; } if (text == null || text.equals("")) { // Setting the property to null removes all children List<MockComponent> children = new ArrayList<MockComponent>(getChildren()); for (MockComponent component : children) { removeComponent(component, true); component.onRemoved(); } children.clear(); features.clear(); clearLayers(); return; } long projectId = Ode.getInstance().getCurrentYoungAndroidProjectId(); Ode.getInstance().getProjectService().load(projectId, "assets/" + text, new AsyncCallback<String>() { @Override public void onFailure(Throwable caught) { ErrorReporter.reportError(caught.getMessage()); } @Override public void onSuccess(String result) { setGeoJSONProperty(result); MockFeatureCollection.this.onSelectedChange(true); // otherwise the last imported component // will be shown in the properties panel } }); } private void setVisibleProperty(String text) { if (map == null) { // cannot change visibility if there is no map return; } boolean isVisible = Boolean.parseBoolean(text); setNativeVisible(map.getMapInstance(), isVisible); } private void setGeoJSONProperty(String text) { if (map == null) { // cannot change features if there is no map return; } setNativeGeoJSON(map.getMapInstance(), text); } @SuppressWarnings("unused") // Called from JSNI private void onEachFeature(String type, JSONObject properties, JavaScriptObject layer) { if (type.equals("Point") || type.equals("MultiPoint")) { features.add(MockMarker.fromGeoJSON(this, properties, layer)); } else if (type.equals("LineString") || type.equals("MultiLineString")) { features.add(MockLineString.fromGeoJSON(this, properties, layer)); } else if (type.equals("Polygon") || type.equals("MultiPolygon")) { features.add(MockPolygon.fromGeoJSON(this, properties, layer)); } else if (type.equals("Circle")) { features.add(MockCircle.fromGeoJSON(this, properties, layer)); } } // JSNI Methods private native void initCollection()/*-{ [email protected]inventor.client.editor.simple.components.MockFeatureCollection::collection = top.L.featureGroup(); }-*/; private native void addToMap(JavaScriptObject map)/*-{ var collection = [email protected]kFeatureCollection::collection; map.addLayer(collection); }-*/; private native void clearLayers()/*-{ var collection = [email protected]kFeatureCollection::collection; if (collection) { collection.clearLayers(); } }-*/; private native void setNativeGeoJSON(JavaScriptObject map, String geojson)/*-{ var collection = [email protected]kFeatureCollection::collection; if (collection) { var self = this; map.removeLayer(collection); if (geojson.charCodeAt(0) == 0xFEFF) geojson = geojson.substr(1); // strip byte order marker, if present collection = top.L.geoJson(JSON.parse(geojson), { pointToLayer: function(feature, latlng) { for (var key in feature.properties) { var lowerKey = key.toLowerCase(); if (lowerKey == 'r' || lowerKey == 'radius') { return top.L.circle(latlng, feature.properties[key]); } } return top.L.marker(latlng, {draggable: true, keyboard: false, alt: 'Marker'}); }, style: function(feature) { return { color: '#000', weight: 2, fillColor: '#480', fillOpacity: 1, pointerEvents: 'auto' }; }, onEachFeature: function(feature, layer) { var properties = @com.google.gwt.json.client.JSONObject::new(Lcom/google/gwt/core/client/JavaScriptObject;)( feature.properties); [email protected]kFeatureCollection::onEachFeature(*)( layer instanceof top.L.Circle ? 'Circle' : feature.geometry.type, properties, layer); } }); // collection.addTo(map); [email protected]kFeatureCollection::collection = collection; } }-*/; private native void setNativeVisible(JavaScriptObject map, boolean visible)/*-{ }-*/; /* var collection = [email protected]kFeatureCollection::collection; if (collection) { if (visible) { map.addLayer(collection); } else { map.removeLayer(collection); } } }-*/; }