package org.jrmerz.example.smartesri.client; import com.esri.gwt.client.Error; import com.esri.gwt.client.Graphic; import com.esri.gwt.client.MapWidget; import com.esri.gwt.client.Util; import com.esri.gwt.client.Graphic.Attributes; import com.esri.gwt.client.dojo.Color; import com.esri.gwt.client.event.ClickHandler; import com.esri.gwt.client.event.MapLoadHandler; import com.esri.gwt.client.event.MouseEvent; import com.esri.gwt.client.event.UpdateEndHandler; import com.esri.gwt.client.event.UpdateStartHandler; import com.esri.gwt.client.geometry.Point; import com.esri.gwt.client.layers.ArcGISTiledMapServiceLayer; import com.esri.gwt.client.layers.FeatureLayer; import com.esri.gwt.client.symbol.SimpleFillSymbol; import com.esri.gwt.client.symbol.SimpleLineSymbol; import com.esri.gwt.client.symbol.SimpleLineSymbol.StyleType; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.JsArray; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.smartgwt.client.widgets.grid.ListGrid; import com.smartgwt.client.widgets.grid.ListGridField; import com.smartgwt.client.widgets.grid.ListGridRecord; import com.smartgwt.client.widgets.grid.events.RecordClickEvent; import com.smartgwt.client.widgets.grid.events.RecordClickHandler; /** * This example shows how to use the gwt-esri library with another 3rd party * gwt library. In this case SmartGwt. * * To the point, this example shows CPAD (California Protected Areas Database) * as a ESRI ArcGIS feature service displayed using the gwt-esri library (FeatureLayer class). * The attributes of the returned graphics (Graphic: think geometry + style information + attributes) * are then displayed using the SmartGwt ListGrid. Geometries can be selected * by clicking a row in the ListGrid or clicking on a Graphic on the map. * * @author jrmerz */ public class SmartEsri implements EntryPoint { // main layout private VerticalPanel vp = new VerticalPanel(); // the esri map private MapWidget mapWidget = null; // SmartGwt table private ListGrid table = new ListGrid(); // CPAD feature layer private FeatureLayer fLayer = null; // loading panel private HTML loading = new HTML("Loading..."); // currently selected record private CpadRecord selectedRecord = null; // line style for currently selected record private SimpleLineSymbol selectedOutline = SimpleLineSymbol.create(StyleType.STYLE_SOLID, Color.create("#2278DA"), 2); public void onModuleLoad() { vp.setStyleName("root"); // add esri js packages to load Util.addRequiredPackage(Util.Package.ESRI_LAYERS_FEATURELAYER); // wait for esri js code to load before creating the map. Util.addEsriLoadHandler(new Runnable() { @Override public void run() { // create the map using esri's topo layer: http://www.arcgis.com/home/item.html?id=d5e02a0c1f2b4ec399823fdd3c2fdebd mapWidget = new MapWidget( ArcGISTiledMapServiceLayer.create("http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"), onMapLoad ); // set the layout vp.add(mapWidget); vp.add(table); loading.setVisible(true); loading.setStyleName("loading"); vp.add(loading); // add everything to DOM... this will trigger the loading of the MapWidget RootPanel.get().add(vp); RootPanel.getBodyElement().setClassName("tundra"); } }); initTable(); } // this is basically the example from the SmartGWT Showcase: // http://www.smartclient.com/smartgwt/showcase/#grid_cell_widgets private void initTable() { table.setShowRecordComponents(true); table.setShowRecordComponentsByCell(true); table.setWidth("700px"); table.setHeight("400px"); table.setShowAllRecords(true); ListGridField nameField = new ListGridField("unitName", "Unit Name"); ListGridField agencyIdField = new ListGridField("agencyId", "Agency Id"); ListGridField agencyLevelField = new ListGridField("agencyLevel", "Agency Level"); ListGridField accessField = new ListGridField("access", "Access"); ListGridField countyField = new ListGridField("county", "County"); ListGridField layerField = new ListGridField("layer", "Layer"); table.addRecordClickHandler(new RecordClickHandler(){ @Override public void onRecordClick(RecordClickEvent event) { selectRecord((CpadRecord) event.getRecord()); } }); table.setFields(nameField, agencyIdField, agencyLevelField, accessField, countyField, layerField); table.setCanResizeFields(true); } private MapLoadHandler onMapLoad = new MapLoadHandler(){ @Override public void onLoad() { // set the map's size mapWidget.setSize("700px", "500px"); // center map Point center = Point.create(-13523392.0,4660805.0, mapWidget.getSpatialReference()); mapWidget.centerAndZoom(center, 13); // create a new feature layer FeatureLayer.Options options = FeatureLayer.Options.create(); // fields to be return from arcgis server options.setOutFields(new String[] {"UNIT_NAME", "AGNCY_ID", "AGNCY_LEV", "ACCESS", "COUNTY", "LAYER"}); // create the feature layer fLayer = FeatureLayer.create("http://myplan.casil.ucdavis.edu/ArcGIS/rest/services/Planning_Cadastre/CPAD/MapServer/0", options); // add layer to map mapWidget.addLayer(fLayer); // show the loading message whenever an update starts fLayer.addUpdateStartHandler(new UpdateStartHandler(){ @Override public void onUpdateStart() { loading.setVisible(true); } }); // add update handler for updating SmartGwt grid fLayer.addUpdateEndHandler(featureLayerUpdateHandler); // add click handler to select grid row when graphic is clicked fLayer.addClickHandler(graphicClickHandler); } }; // graphic layer clickhandlers are only fired when a graphic is clicked private ClickHandler graphicClickHandler = new ClickHandler(){ @Override public void onClick(MouseEvent event) { // get the graphic that was clicked Graphic g = event.getGraphic(); // find the record of the graphic based on the graphic itself ListGridRecord[] records = table.getRecords(); for( int i = 0; i < records.length; i++ ) { CpadRecord cr = (CpadRecord) records[i]; if( cr.getGraphic() == g ) { // highlight and set row selectRecord(cr); // scroll to row table.scrollToRow(table.getRecordIndex(cr)); return; } } } }; // update the data grid whenever the feature layer finishes updating private UpdateEndHandler featureLayerUpdateHandler = new UpdateEndHandler() { @Override public void onUpdateEnd() { // grab all of the layers graphics JsArray<Graphic> graphics = fLayer.getGraphics(); // create the new record array CpadRecord[] records = new CpadRecord[graphics.length()]; // need to know if the currently selected graphic was returned in the new result set boolean foundSelectedRecord = false; for( int i = 0; i < graphics.length(); i++ ) { // create new table record for graphic records[i] = createCpadRecord(graphics.get(i)); if( selectedRecord != null ) { // if new record is the selected graphic update the selectedRecord if( records[i].getGraphic() == selectedRecord.getGraphic() ) { foundSelectedRecord = true; selectedRecord = records[i]; } } } // set data a draw table.setData(records); table.draw(); // if there is no selected graphic, make sure the selectedGraphic is null // and that the graphic no longer has an outline if( !foundSelectedRecord ) { if( selectedRecord != null ) { selectedRecord.getGraphic().setSymbol(null); updateGraphic(selectedRecord.getGraphic()); } selectedRecord = null; // if the selected record was found, update and scroll to the new row in the table } else { selectRecord(selectedRecord); table.scrollToRow(table.getRecordIndex(selectedRecord)); } // done loading loading.setVisible(false); } @Override public void onError(Error error) { // done loading but sadness happend :( loading.setVisible(false); } }; // set the attributes for the record if they exist in the graphics attributes private CpadRecord createCpadRecord(Graphic g) { CpadRecord cr = new CpadRecord(); Attributes attrs = g.getAttributes(); if( attrs.hasKey("UNIT_NAME") ) cr.setUnitName(attrs.getString("UNIT_NAME")); if( attrs.hasKey("AGNCY_ID") ) cr.setAgencyId(getCodedDomainValue(fLayer, "AGNCY_ID", attrs.getInt("AGNCY_ID"))); if( attrs.hasKey("AGNCY_LEV") ) cr.setAgencyLevel(getCodedDomainValue(fLayer, "AGNCY_LEV", attrs.getInt("AGNCY_LEV"))); if( attrs.hasKey("ACCESS") ) cr.setAccess(attrs.getString("ACCESS")); if( attrs.hasKey("COUNTY") ) cr.setCounty(getCodedDomainValue(fLayer, "COUNTY", attrs.getInt("COUNTY"))); if( attrs.hasKey("LAYER") ) cr.setLayer(attrs.getString("LAYER")); cr.setGraphic(g); return cr; } private void selectRecord(CpadRecord record) { // if a previous record is selected, deselect it on the map and in the table if( selectedRecord != null ) { // this feature layer has default renderers for each graphic, so setting the // graphics symbol to null will use the default symbol selectedRecord.getGraphic().setSymbol(null); updateGraphic(selectedRecord.getGraphic()); table.deselectRecord(selectedRecord); } // again, this layer has default renderers for the graphics // we are going to poach the style as well as color, then add the 'selected' outline. SimpleFillSymbol layerFill = (SimpleFillSymbol) fLayer.getRenderer().getSymbol(record.getGraphic()); // set the new style record.getGraphic().setSymbol( SimpleFillSymbol.create(layerFill.getStyle(), selectedOutline, layerFill.getColor()) ); // update the graphic and select a row selectedRecord = record; updateGraphic(selectedRecord.getGraphic()); table.selectRecord(record); } // there may be a better way to do this.... private void updateGraphic(Graphic g) { fLayer.remove(g); fLayer.add(g); } // you can ignore this for the most part. Basically some of the values in the feature // layer are returned as codedValue domain's. The js api does not have a very nice way // to interact w/ these that I can tell, therefore neither does the gwt library. At // which point it's just easier to right this in jsni. private native String getCodedDomainValue(FeatureLayer fLayer, String field, int code) /*-{ if( !fLayer.fields ) return code+""; for( var i in fLayer.fields ) { if( fLayer.fields[i].name == field ) { var domain = fLayer.fields[i].domain; if( !domain ) return code+""; if( !domain.codedValues ) return code+""; for( var j in domain.codedValues ) { if( domain.codedValues[j].code == code ) return domain.codedValues[j].name; } } } return code+""; }-*/; }