Update the orientation of a graphic using expressions based on its attributes.
Use case
Instead of reading the attribute and changing the rotation on the symbol for a single graphic (a manual CPU operation), you can bind the rotation to an expression that applies to the whole overlay (an automatic GPU operation). This usually results in a noticeable performance boost (smooth rotations).
How to use the sample
Adjust the heading and pitch sliders to rotate the cone.
How it works
Create a new GraphicsOverlay.
Create a new SimpleRenderer.
Set the heading expression to [HEADING] and the pitch expression to [PITCH] with simpleRenderer.getSceneProperties().setHeadingExpression(...).
Apply the renderer to the graphics overlay with graphicsOverlay.setRenderer(simpleRenderer).
Create a new Point and a new Graphic and add it to the overlay with e.g. graphicsOverlay.getGraphics().add(graphic).
To update the graphic's rotation, update the HEADING or PITCH property in the graphic's attributes with graphic.getAttributes().put(key, value).
/*
* Copyright 2019 Esri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/package com.esri.arcgisruntime.sample.scenepropertyexpressions;
import android.graphics.Color;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.widget.SeekBar;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.geometry.SpatialReferences;
import com.esri.arcgisruntime.mapping.ArcGISScene;
import com.esri.arcgisruntime.mapping.Basemap;
import com.esri.arcgisruntime.mapping.view.Camera;
import com.esri.arcgisruntime.mapping.view.Graphic;
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
import com.esri.arcgisruntime.mapping.view.LayerSceneProperties;
import com.esri.arcgisruntime.mapping.view.SceneView;
import com.esri.arcgisruntime.symbology.SceneSymbol;
import com.esri.arcgisruntime.symbology.SimpleMarkerSceneSymbol;
import com.esri.arcgisruntime.symbology.SimpleRenderer;
publicclassMainActivityextendsAppCompatActivity{
private SceneView mSceneView;
privatestaticfinalint PITCH_OFFSET = 90;
privatestaticfinal String HEADING_EXPRESSION = "HEADING";
privatestaticfinal String PITCH_EXPRESSION = "PITCH";
@OverrideprotectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// get a reference to the scene view mSceneView = findViewById(R.id.sceneView);
// create a scene with an imagery basemap ArcGISScene scene = new ArcGISScene(Basemap.Type.IMAGERY);
// add the SceneView to the stack pane mSceneView.setScene(scene);
// add a camera and initial camera position Point point = new Point(83.9, 28.4, 1000, SpatialReferences.getWgs84());
Camera camera = new Camera(point, 1000, 0, 50, 0);
mSceneView.setViewpointCamera(camera);
// create a graphics overlay GraphicsOverlay graphicsOverlay = new GraphicsOverlay();
graphicsOverlay.getSceneProperties().setSurfacePlacement(LayerSceneProperties.SurfacePlacement.RELATIVE);
mSceneView.getGraphicsOverlays().add(graphicsOverlay);
// add renderer using rotation expressions SimpleRenderer renderer = new SimpleRenderer();
renderer.getSceneProperties().setHeadingExpression('[' + HEADING_EXPRESSION + ']');
renderer.getSceneProperties().setPitchExpression('[' + PITCH_EXPRESSION + ']');
graphicsOverlay.setRenderer(renderer);
// create a red cone graphic// in this sample we've set the anchor position to center. By default, the anchor position is BOTTOM SimpleMarkerSceneSymbol coneSymbol = SimpleMarkerSceneSymbol.createCone(Color.RED, 100, 100, SceneSymbol.AnchorPosition.CENTER);
coneSymbol.setPitch(-PITCH_OFFSET); // correct symbol's default pitch Graphic cone = new Graphic(new Point(83.9, 28.41, 200, SpatialReferences.getWgs84()), coneSymbol);
graphicsOverlay.getGraphics().add(cone);
// bind attribute based on values in seek bars SeekBar headingSeekBar = findViewById(R.id.headingSeekBar);
cone.getAttributes().put(HEADING_EXPRESSION, headingSeekBar.getProgress());
headingSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@OverridepublicvoidonProgressChanged(SeekBar seekBar, int progress, boolean fromUser){
cone.getAttributes().put(HEADING_EXPRESSION, progress);
}
@OverridepublicvoidonStartTrackingTouch(SeekBar seekBar){
}
@OverridepublicvoidonStopTrackingTouch(SeekBar seekBar){
}
});
SeekBar pitchSeekBar = findViewById(R.id.pitchSeekBar);
cone.getAttributes().put(PITCH_EXPRESSION, pitchSeekBar.getProgress() - PITCH_OFFSET);
pitchSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@OverridepublicvoidonProgressChanged(SeekBar seekBar, int progress, boolean fromUser){
cone.getAttributes().put(PITCH_EXPRESSION, progress - PITCH_OFFSET);
}
@OverridepublicvoidonStartTrackingTouch(SeekBar seekBar){
}
@OverridepublicvoidonStopTrackingTouch(SeekBar seekBar){
}
});
}
@OverrideprotectedvoidonPause(){
mSceneView.pause();
super.onPause();
}
@OverrideprotectedvoidonResume(){
super.onResume();
mSceneView.resume();
}
@OverrideprotectedvoidonDestroy(){
mSceneView.dispose();
super.onDestroy();
}
}