varsceneLayerUrl=@"https://myportal.com/server/rest/services/Hosted/SceneLayerServiceName/SceneServer";//portal items also ok as long as the portal is the current active portal...//var sceneLayerUrl = @"https://myportal.com/home/item.html?id=123456789abcdef1234567890abcdef0";await QueuedTask.Run(()=>{//Create with initial visibility set to false. Add to current scenevarcreateparams=new LayerCreationParams(new Uri(sceneLayerUrl, UriKind.Absolute)){IsVisible=false};//cast to specific type of scene layer being created - in this case FeatureSceneLayervarsceneLayer= LayerFactory.Instance.CreateLayer<Layer>(createparams, MapView.Active.Map)as FeatureSceneLayer;//or...specify the cast directlyvarsceneLayer2= LayerFactory.Instance.CreateLayer<FeatureSceneLayer>(createparams, MapView.Active.Map);//ditto for BuildingSceneLayer, PointCloudSceneLayer, IntegratedMeshSceneLayer//...});
Enumerate the Discipline Layers from a Building SceneLayer
publicvoidQueryBuildingSceneLayer(){varbldgLayer= MapView.Active.Map.GetLayersAsFlattenedList().OfType<BuildingSceneLayer>().First();vardisciplines=newDictionary<string,BuildingDisciplineSceneLayer>();//A Building layer has two children - Overview and FullModel//Overview is a FeatureSceneLayer//Full Model is a BuildingDisciplineSceneLayer that contains the disciplinesvarfullModel= bldgLayer.FindLayers("Full Model").First()as BuildingDisciplineSceneLayer;
CollectDisciplineLayers(fullModel, disciplines);}internalvoidCollectDisciplineLayers(BuildingDisciplineSceneLayerdisciplineLayer,Dictionary<string,BuildingDisciplineSceneLayer>disciplines){//collect information on the disciplinesvarname= disciplineLayer.Name;varlayerType=((ISceneLayerInfo)disciplineLayer).SceneServiceLayerType.ToString();vardiscipline= disciplineLayer.GetDiscipline();//etc//TODO - use collected information
disciplines.Add(discipline, disciplineLayer);//Discipline layers are composite layers tooforeach(var childDiscipline in disciplineLayer.Layers.OfType<BuildingDisciplineSceneLayer>()){//Discipline layers can also contain FeatureSceneLayers that render the//individual full model contentsvarcontent_names=string.Join(", ", childDiscipline.Layers
.OfType<FeatureSceneLayer>().Select(fl => fl.Name));
CollectDisciplineLayers(childDiscipline, disciplines);}}
Query Building Scene Layer for available Types and Values
//Must be called on the MCT//Retrieve the complete set of types and values for the building scene//var bsl = ...;vardict= bsl.QueryAvailableFieldsAndValues();//get a list of existing disciplinesvardisciplines= dict.SingleOrDefault(kvp => kvp.Key =="Discipline").Value ??newList<string>();//get a list of existing categoriesvarcategories= dict.SingleOrDefault(kvp => kvp.Key =="Category").Value ??newList<string>();//get a list of existing floors or "levels"varfloors= dict.SingleOrDefault(kvp => kvp.Key =="BldgLevel").Value ??newList<string>();
Create a Default Filter and Get Filter Count
//Must be called on the MCT//Creates a default filter on the building scene//var bsl = ...;varfilter1= bsl.CreateDefaultFilter();varvalues= filter1.FilterBlockDefinitions[0].SelectedValues;//values will be a single value for the type//"CreatedPhase", value "New Construction"//There will be at least one filter after "CreateDefaultFilter()" callvarfiltersCount= bsl.GetFilters().Count;
Get all the Filters that Contain WireFrame Blocks
//var bsl = ...;//Note: wire_frame_filters can be null in this examplevarwire_frame_filters= bsl.GetFilters().Where(f => f.FilterBlockDefinitions.Any(fb => fb.FilterBlockMode == Object3DRenderingMode.Wireframe));//substitute Object3DRenderingMode.None to get blocks with a solid mode (default)//and...//fb.FilterBlockMode == Object3DRenderingMode.Wireframe &&//fb.FilterBlockMode == Object3DRenderingMode.None//for blocks with both
Set and Clear Active Filter for BuildingSceneLayer
//Must be called on the MCT//Note: Use HasFilter to check if a given filter id exists in the layer//var bsl = ...;if(bsl.HasFilter(filter1.ID))
bsl.SetActiveFilter(filter1.ID);varactiveFilter= bsl.GetActiveFilter();//Clear the active filter
bsl.ClearActiveFilter();
Modify BuildingSceneLayer Filter Name and Description
//Must be called on the MCT//var bsl = ...;//var filter1 = bsl.GetFilter(filterID1);
filter1.Name ="Updated Filter Name";
filter1.Description ="Updated Filter description";
bsl.SetFilter(filter1);
Create a Filter using Building Level and Category
//refer to "Query Building Scene Layer for available Types and Values"//...//var bsl = ...;//var dict = bsl.QueryAvailableFieldsAndValues();//var categories = dict.SingleOrDefault(kvp => kvp.Key == "Category").Value;//var floors = dict.SingleOrDefault(kvp => kvp.Key == "BldgLevel").Value;//Make a new filter definitionvarfd=new FilterDefinition(){Name="Floor and Category Filter",Description="Example filter",};//Set up the values for the filtervarfiltervals=newDictionary<string,List<string>>();
filtervals.Add("BldgLevel",newList<string>(){ floors[0]});varcategory_vals= categories.Where(v =>v=="Walls"||v=="Stairs").ToList()??newList<string>();if(category_vals.Count()>0){
filtervals.Add("Category", category_vals);}//Create a solid block (other option is "Wireframe")varfdef=new FilterBlockDefinition(){FilterBlockMode= Object3DRenderingMode.None,Title="Solid Filter",SelectedValues=filtervals//Floor and Category};//Apply the block
fd.FilterBlockDefinitions =newList<FilterBlockDefinition>(){ fdef };//Add the filter definition to the layer
bsl.SetFilter(fd);//Set it active. The ID is auto-generated
bsl.SetActiveFilter(fd.ID);
Modify BuildingSceneLayer Filter Block
//Must be called on the MCT//Assuming retrieve filter ok//var bsl = ...;//var filter1 = bsl.GetFilter(...);varfilterBlock=new FilterBlockDefinition();
filterBlock.FilterBlockMode = Object3DRenderingMode.Wireframe;varselectedValues=newDictionary<string,List<string>>();//We assume QueryAvailableFieldsAndValues() contains "Walls" and "Doors"//For 'Category'
selectedValues["Category"]=newList<string>(){"Walls","Doors"};
filterBlock.SelectedValues =selectedValues;//Overwrite
filter1.FilterBlockDefinitions =newList<FilterBlockDefinition>(){ filterBlock };
bsl.SetFilter(filter1);
Remove BuildingSceneLayer Filter
//var bsl = ...;//Note: Use HasFilter to check if a given filter id exists in the layer//Must be called on the MCTif(bsl.HasFilter(filter1.ID))
bsl.RemoveFilter(filter1.ID);//Or remove all filters
bsl.RemoveAllFilters();
//must support editing!//var featSceneLayer = ... ;if(!featSceneLayer.HasAssociatedFeatureService ||!featSceneLayer.IsEditable)return;//Check geometry type...must be point in this exampleif(featSceneLayer.ShapeType != esriGeometryType.esriGeometryPoint)return;vareditOp=new EditOperation(){Name="Create new 3d point feature",SelectNewFeatures=true};varattributes=newDictionary<string,object>();//mapPoint contains the new 3d point location
attributes.Add("SHAPE", mapPoint);
attributes.Add("TreeID","1");
editOp.Create(featSceneLayer, attributes);
editOp.ExecuteAsync();//fyi, no await
Delete all the selected features in FeatureSceneLayer
//must support editing!//var featSceneLayer = .... ;if(!featSceneLayer.HasAssociatedFeatureService ||!featSceneLayer.IsEditable)return;vardelOp=new EditOperation(){Name="Delete selected features"};//Assuming we have a selection on the layer...
delOp.Delete(featSceneLayer, featSceneLayer.GetSelection().GetObjectIDs());await delOp.ExecuteAsync();//await if needed but not required
Edit the attributes of a FeatureSceneLayer
//must support editing!varfeatSceneLayer= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureSceneLayer>().FirstOrDefault();if(!featSceneLayer.HasAssociatedFeatureService ||!featSceneLayer.IsEditable)return;varok=await QueuedTask.Run(()=>{vareditOp=new EditOperation(){Name="Edit FeatureSceneLayer Attributes",SelectModifiedFeatures=true};//make an inspectorvarinspector=new Inspector();//get the attributes for the specified oid inspector.Load(featSceneLayer, oid); inspector["PermitNotes"]="test";//modify editOp.Modify(inspector);return editOp.Execute();//synchronous flavor});
//Must be called on the MCTvardataSourceType= featSceneLayer?.GetDataSourceType()??
SceneLayerDataSourceType.Unknown;if(dataSourceType== SceneLayerDataSourceType.SLPK){//Uses SLPK - only cached attributes}elseif(dataSourceType== SceneLayerDataSourceType.Service){//Hosted service - can have live attributes - check HasAssociatedFeatureService}
Get the Associated Feature class
//var featSceneLayer = ....;if(featSceneLayer.HasAssociatedFeatureService){//Must be called on the MCTusing(varfc= featSceneLayer.GetFeatureClass()){//TODO query underlying feature class}}
Get Field Definitions
//var featSceneLayer = ....;await QueuedTask.Run(()=>{//Get only the readonly fieldsvarreadOnlyFields= featSceneLayer.GetFieldDescriptions().Where(fdesc => fdesc.IsReadOnly);//etc});
Set a Definition Query
//Must be called on the MCT//var featSceneLayer = ...;
featSceneLayer.SetDefinitionQuery("Name = 'Ponderosa Pine'");
Select features via the MapView
//assume the geometry used in SelectFeaturesEx() is coming from a //map tool...////SketchType = SketchGeometryType.Rectangle;//SketchOutputMode = SketchOutputMode.Screen;await QueuedTask.Run(()=>{varresult= MapView.Active.SelectFeaturesEx(geometry);//Get scene layers with selectionsvarscene_layers= result.Where(kvp => kvp.Key is FeatureSceneLayer);foreach(var kvp in scene_layers){varscene_layer= kvp.Key as FeatureSceneLayer;varsel_oids= kvp.Value;//If there are attributes then get themif(scene_layer.HasAssociatedFeatureService){varinsp=new Inspector();foreach(var oid in sel_oids){ insp.Load(scene_layer, oid);//TODO something with retrieved attributes}}}});
Has Associated FeatureService
varfeatSceneLayer= MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureSceneLayer>().First();if(featSceneLayer.HasAssociatedFeatureService){//Can Select and Search...possibly edit}
Search Rows on the FeatureSceneLayer
//var featSceneLayer = ...;if(!featSceneLayer.HasAssociatedFeatureService)return;//Search and Select not supported//Multipatch (Object3D) or point?varis3dObject=((ISceneLayerInfo)featSceneLayer).SceneServiceLayerType
== esriSceneServiceLayerType.Object3D;await QueuedTask.Run(()=>{varqueryFilter=new QueryFilter{WhereClause="Name = 'Ponderosa Pine'",SubFields="*"};introwCount=0;//or select... var select = featSceneLayer.Select(queryFilter)using(RowCursorrowCursor= featSceneLayer.Search(queryFilter)){while(rowCursor.MoveNext()){using(varfeature= rowCursor.Current as Feature){varoid= feature.GetObjectID();varshape= feature.GetShape();varattrib= feature["Name"];if(is3dObject){//shape is a multipatch}else{//shape is a point}rowCount+=1;}}}});
Hide Selected features and Show Hidden features
//var featSceneLayer = ...;if(featSceneLayer.HasAssociatedFeatureService)return;//Search and Select not supportedawait QueuedTask.Run(()=>{QueryFilterqf=new QueryFilter(){ObjectIDs=newList<long>(){6069,6070,6071},SubFields="*"}; featSceneLayer.Select(qf, SelectionCombinationMethod.New); featSceneLayer.HideSelectedFeatures();varselectionCount= featSceneLayer.SelectionCount; featSceneLayer.ShowHiddenFeatures();selectionCount= featSceneLayer.SelectionCount;});
Use MapView Selection SelectFeaturesEx or GetFeaturesEx
//var featSceneLayer = ...;varsname= featSceneLayer.Name;await QueuedTask.Run(()=>{//Select all features within the current map viewvarsz= MapView.Active.GetViewSize();varc_ll=new Coordinate2D(0,0);varc_ur=new Coordinate2D(sz.Width, sz.Height);//Use screen coordinates for 3D selection on MapViewvarenv= EnvelopeBuilder.CreateEnvelope(c_ll, c_ur);//HasAssociatedFeatureService does not matter for SelectFeaturesEx//or GetFeaturesExvarresult= MapView.Active.SelectFeaturesEx(env);//var result = MapView.Active.GetFeaturesEx(env);//The list of object ids from SelectFeaturesExvaroids1= result.Where(kvp => kvp.Key.Name ==sname).First().Value;//TODO - use the object ids MapView.Active.Map.ClearSelection();});
Use Select or Search with a Spatial Query
//var featSceneLayer = ...;//var sname = featSceneLayer.Name;await QueuedTask.Run(()=>{if(!featSceneLayer.HasAssociatedFeatureService)return;//no search or select//Select all features within the current map viewvarsz= MapView.Active.GetViewSize();varmap_pt1= MapView.Active.ClientToMap(new System.Windows.Point(0, sz.Height));varmap_pt2= MapView.Active.ClientToMap(new System.Windows.Point(sz.Width,0));//Convert to an envelopevartemp_env= EnvelopeBuilder.CreateEnvelope(map_pt1, map_pt2, MapView.Active.Map.SpatialReference);//Project if needed to the layer spatial refSpatialReferencesr=null;using(varfc= featSceneLayer.GetFeatureClass())using(varfdef= fc.GetDefinition())sr= fdef.GetSpatialReference();varenv= GeometryEngine.Instance.Project(temp_env, sr)as Envelope;//Set up a query filtervarsf=new SpatialQueryFilter(){FilterGeometry=env,SpatialRelationship= SpatialRelationship.Intersects,SubFields="*"};//Select against the feature servicevarselect= featSceneLayer.Select(sf);if(select.GetCount()>0){//enumerate over the selected featuresusing(varrc=select.Search()){while(rc.MoveNext()){using(varfeature= rc.Current as Feature){varoid= feature.GetObjectID();//etc.}}}} MapView.Active.Map.ClearSelection();});
//var pcsl = ...;ISceneLayerInfoslInfo= pcsl as ISceneLayerInfo;SceneLayerDataSourceTypedataSourceType= slInfo.GetDataSourceType();if(dataSourceType== SceneLayerDataSourceType.Service){//TODO...}elseif(dataSourceType== SceneLayerDataSourceType.SLPK){}
Query all class codes and lables in the PointCloudSceneLayer
//Must be called on the MCT//var pcsl = ...;Dictionary<int,string>classCodesAndLabels= pcsl.QueryAvailableClassCodesAndLabels();
Set a Filter for PointCloudSceneLayer
//Must be called on the MCT//var pcsl = ...;//Retrieve the available classification codesvardict= pcsl.QueryAvailableClassCodesAndLabels();//Filter out low noise and unclassified (7 and 1 respectively)//consult https://pro.arcgis.com/en/pro-app/help/data/las-dataset/storing-lidar-data.htmvarfilterDef=new PointCloudFilterDefinition(){ClassCodes= dict.Keys.Where(c =>c!=7&&c!=1).ToList(),ReturnValues=newList<PointCloudReturnType>{ PointCloudReturnType.FirstOfMany }};//apply the filter
pcsl.SetFilters(filterDef.ToCIM());
Update the ClassFlags for PointCloudSceneLayer
//Must be called on the MCT//var pcsl = ...;varfilters= pcsl.GetFilters();PointCloudFilterDefinitionfdef=null;if(filters.Count()==0){fdef=new PointCloudFilterDefinition(){//7 is "edge of flight line" - excludeClassFlags=newList<ClassFlag>{new ClassFlag(7, ClassFlagOption.Exclude)}};}else{fdef= PointCloudFilterDefinition.FromCIM(filters);//keep any include or ignore class flagsvarkeep= fdef.ClassFlags.Where(cf => cf.ClassFlagOption != ClassFlagOption.Exclude).ToList();//7 is "edge of flight line" - exclude
keep.Add(new ClassFlag(7, ClassFlagOption.Exclude));
fdef.ClassFlags =keep;}//apply
pcsl.SetFilters(fdef.ToCIM());
Get the filters for PointCloudSceneLayer
//Must be called on the MCT//var pcsl = ...;IReadOnlyList<CIMPointCloudFilter>updatedFilter= pcsl.GetFilters();foreach(var filter in updatedFilter){//There is either 0 or 1 of eachif(filter is CIMPointCloudReturnFilter returnFilter){PointCloudFilterDefinitionpcfl= PointCloudFilterDefinition.FromCIM(updatedFilter);List<PointCloudReturnType>updatedReturnValues= pcfl.ReturnValues;}if(filter is CIMPointCloudValueFilter classCodesFilter){// do something}if(filter is CIMPointCloudBitFieldFilter classFlagsFilter){// do something}}
Clear filters in PointCloudSceneLayer
//Must be called on the MCT//var pcsl = ...;
pcsl.ClearFilters();
Get PointCloudSceneLayer Renderer and RendererType
//Must be called on the MCT//var pcsl = ...;CIMPointCloudRenderercimGetPCLRenderer= pcsl.GetRenderer();//Can be one of Unknown, Stretch, ClassBreaks, UniqueValue, RGBPointCloudRendererTypepclRendererType= pcsl.RendererType;
Query PointCloudSceneLayer Renderer fields
//Must be called on the MCT//var pcsl = ...;IReadOnlyList<string>flds= pcsl.QueryAvailablePointCloudRendererFields(
PointCloudRendererType.UniqueValueRenderer);varfldCount= flds.Count;
Create and Set a Stretch Renderer
//Must be called on the MCT//var pcsl = ...;varfields= pcsl.QueryAvailablePointCloudRendererFields(PointCloudRendererType.StretchRenderer);varstretchDef=new PointCloudRendererDefinition(PointCloudRendererType.StretchRenderer){//Will be either ELEVATION or INTENSITYField= fields[0]};//Create the CIM RenderervarstretchRenderer= pcsl.CreateRenderer(stretchDef)as CIMPointCloudStretchRenderer;//Apply a color rampvarstyle= Project.Current.GetItems<StyleProjectItem>().First(s => s.Name =="ArcGIS Colors");varcolorRamp= style.SearchColorRamps("").First();
stretchRenderer.ColorRamp = colorRamp.ColorRamp;//Apply modulation
stretchRenderer.ColorModulation =new CIMColorModulationInfo(){MinValue=0,MaxValue=100};//apply the renderer
pcsl.SetRenderer(stretchRenderer);
Create and Set a ClassBreaks Renderer
//Must be called on the MCT//var pcsl = ...;varfields= pcsl.QueryAvailablePointCloudRendererFields(PointCloudRendererType.ClassBreaksRenderer);varclassBreakDef=new PointCloudRendererDefinition(PointCloudRendererType.ClassBreaksRenderer){//ELEVATION or INTENSITYField= fields[0]};//create the renderervarcbr= pcsl.CreateRenderer(classBreakDef)as CIMPointCloudClassBreaksRenderer;//Set up a color scheme to usevarstyle= Project.Current.GetItems<StyleProjectItem>().First(s => s.Name =="ArcGIS Colors");varrampStyle= style.LookupItem(
StyleItemType.ColorRamp,"Spectrum By Wavelength-Full Bright_Multi-hue_2")as ColorRampStyleItem;varcolorScheme= rampStyle.ColorRamp;//Set up 6 manual class breaksvarbreaks=6;varcolors= ColorFactory.Instance.GenerateColorsFromColorRamp(
colorScheme, breaks);varclassBreaks=newList<CIMColorClassBreak>();varmin= cbr.Breaks[0].UpperBound;varmax= cbr.Breaks[cbr.Breaks.Count()-1].UpperBound;varstep=(max-min)/(double)breaks;//add in the class breaksdoubleupper= min;for(intb=1;b <= breaks;b++){doublelower= upper;upper=b==breaks?max:min+(b*step);varcb=new CIMColorClassBreak(){UpperBound=upper,Label=string.Format("{0:#0.0#} - {1:#0.0#}", lower, upper),Color= colors[b-1]};
classBreaks.Add(cb);}
cbr.Breaks = classBreaks.ToArray();
pcsl.SetRenderer(cbr);
PointCloudSceneLayer Extended Properties
Edit Color Modulation
//Must be called on the MCT//var pcsl = ...;vardef= pcsl.GetDefinition()as CIMPointCloudLayer;//Get the ColorModulation off the renderervarmodulation= def.Renderer.ColorModulation;if(modulation==null)modulation=new CIMColorModulationInfo();//Set the minimum and maximum intensity as needed
modulation.MinValue =0;
modulation.MaxValue =100.0;//apply back
def.Renderer.ColorModulation =modulation;//Commit changes back to the CIM
pcsl.SetDefinition(def);
Edit The Renderer to use Fixed Size
//Must be called on the MCT//var pcsl = ...;vardef= pcsl.GetDefinition()as CIMPointCloudLayer;//Set the point shape and sizing on the renderer
def.Renderer.PointShape = PointCloudShapeType.DiskShaded;varpointSize=new CIMPointCloudFixedSizeAlgorithm(){UseRealWorldSymbolSizes=false,Size=8};
def.Renderer.PointSizeAlgorithm =pointSize;//Commit changes back to the CIM
pcsl.SetDefinition(def);
Edit the Renderer to Scale Size
//Must be called on the MCT//var pcsl = ...;//var def = pcsl.GetDefinition() as CIMPointCloudLayer;//Set the point shape and sizing on the renderer
def.Renderer.PointShape = PointCloudShapeType.DiskFlat;//defaultvarscaleSize=new CIMPointCloudSplatAlgorithm(){MinSize=8,ScaleFactor=1.0//100%};
def.Renderer.PointSizeAlgorithm =scaleSize;//Commit changes back to the CIM
pcsl.SetDefinition(def);
Edit Density settings
//Must be called on the MCT//var pcsl = ...;vardef= pcsl.GetDefinition()as CIMPointCloudLayer;//PointsBudget - corresponds to Display Limit on the UI// - the absolute maximum # of points to displaydef= pcsl.GetDefinition()as CIMPointCloudLayer;
def.PointsBudget =1000000;//PointsPerInch - corresponds to Density Min --- Max on the UI// - the max number of points per display inch to renderer
def.PointsPerInch =15;//Commit changes back to the CIM
pcsl.SetDefinition(def);