// Note: call within QueuedTask.Run(){//Create with initial visibility set to false. Add to current scenevarcreateparams=newLayerCreationParams(newUri(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)asFeatureSceneLayer;//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
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()asBuildingDisciplineSceneLayer;//collect information on the disciplinesvarname=fullModel.Name;vardiscipline=fullModel.GetDiscipline();//etc//TODO - use collected informationdisciplines.Add(discipline,fullModel);//Discipline layers are composite layers tooforeach(varchildDisciplineinfullModel.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));// Recursively call this "function" to go deeper}
Query Building Scene Layer for available Types and Values
// Note: call within QueuedTask.Run(){vardict=bsl.GetAvailableFieldsAndValues();//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
// Note: call within QueuedTask.Run(){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
// Note: call within QueuedTask.Run(){//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
// Note: call within QueuedTask.Run(){//Note: Use HasFilter to check if a given filter id exists in the layerif(bsl.HasFilter(filter.ID))bsl.SetActiveFilter(filter.ID);varactiveFilter=bsl.GetActiveFilter();//Clear the active filterbsl.ClearActiveFilter();}
Get BuildingSceneLayer Filter ID and Filter
varfilterDefinition=bsl.GetFilter(filterID);//or via Linq//var filter = bsl.GetFilters().FirstOrDefault(f => f.ID == filterID1);
Modify BuildingSceneLayer Filter Name and Description
// Note: call within QueuedTask.Run(){filter.Name="Updated Filter Name";filter.Description="Updated Filter description";bsl.UpdateFilter(filter);}
Create a Filter using Building Level and Category
// Note: call within QueuedTask.Run(){//refer to "Query Building Scene Layer for available Types and Valuesvardict=bsl.GetAvailableFieldsAndValues();varcategories=dict.SingleOrDefault(kvp =>kvp.Key=="Category").Value;//get a list of existing floors or "levels"varfloors=dict.SingleOrDefault(kvp =>kvp.Key=="BldgLevel").Value;//Make a new filter definitionvarfd=newFilterDefinition(){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=newFilterBlockDefinition(){FilterBlockMode=Object3DRenderingMode.None,Title="Solid Filter",SelectedValues=filtervals//Floor and Category};//Apply the blockfd.FilterBlockDefinitions=newList<FilterBlockDefinition>(){fdef};//Add the filter definition to the layerbsl.UpdateFilter(fd);//Set it active. The ID is auto-generatedbsl.SetActiveFilter(fd.ID);}
Modify BuildingSceneLayer Filter Block
// Note: call within QueuedTask.Run(){varfilterBlock=newFilterBlockDefinition();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;//Overwritefilter.FilterBlockDefinitions=newList<FilterBlockDefinition>(){filterBlock};bsl.UpdateFilter(filter);}
Remove BuildingSceneLayer Filter
// Note: call within QueuedTask.Run(){//Note: Use HasFilter to check if a given filter id exists in the layerif(bsl.HasFilter(filter.ID))bsl.RemoveFilter(filter.ID);//Or remove all filtersbsl.RemoveAllFilters();}
FeatureSceneLayer Editing
Determine if a FeatureSceneLayer supports editing
featSceneLayer=MapView.Active.Map.GetLayersAsFlattenedList().OfType<FeatureSceneLayer>().FirstOrDefault();if(!featSceneLayer.HasAssociatedFeatureService||!featSceneLayer.IsEditable){//not supported - exit the function}//TODO continue editing here...
Create a new Point feature in FeatureSceneLayer
//must support editing!if(!featSceneLayer.HasAssociatedFeatureService||!featSceneLayer.IsEditable){//not supported - exit the function}//Check geometry type...must be point in this examplevareditOp=newEditOperation(){Name="Create new 3d point feature",SelectNewFeatures=true};varattributes=newDictionary<string,object>();//mapPoint contains the new 3d point locationattributes.Add("SHAPE",mapPoint);attributes.Add("TreeID","1");editOp.Create(featSceneLayer,attributes);editOp.ExecuteAsync();
Delete all the selected features in FeatureSceneLayer
if(!featSceneLayer.HasAssociatedFeatureService||!featSceneLayer.IsEditable)return;// Note: call within QueuedTask.Run(){vardelOp=newEditOperation(){Name="Delete selected features"};//Assuming we have a selection on the layer...delOp.Delete(featSceneLayer,featSceneLayer.GetSelection().GetObjectIDs());delOp.ExecuteAsync();}
Edit the attributes of a FeatureSceneLayer
//must support editing!if(!featSceneLayer.HasAssociatedFeatureService||!featSceneLayer.IsEditable)return;// Note: call within QueuedTask.Run(){vareditOp=newEditOperation(){Name="Edit FeatureSceneLayer Attributes",SelectModifiedFeatures=true};//make an inspectorvarinspector=newInspector();//get the attributes for the specified oidinspector.Load(featSceneLayer,oid);inspector["PermitNotes"]="test";//modifyeditOp.Modify(inspector);editOp.Execute();//synchronous flavor}
// Note: call within QueuedTask.Run(){vardataSourceType=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
// Note: call within QueuedTask.Run(){if(featSceneLayer.HasAssociatedFeatureService){using(varfc=featSceneLayer.GetFeatureClass()){//TODO query underlying feature class}}}
Get Field Definitions
// Note: call within QueuedTask.Run(){//Get only the readonly fieldsvarreadOnlyFields=featSceneLayer.GetFieldDescriptions().Where(fdesc =>fdesc.IsReadOnly);}
//assume the geometry used in SelectFeaturesEx() is coming from a //map tool...//SketchType = SketchGeometryType.Rectangle;//SketchOutputMode = SketchOutputMode.Screen;// Note: call within QueuedTask.Run(){varresult=MapView.Active.SelectFeaturesEx(geometry);//Get scene layers with selectionsvarscene_layers=result.ToDictionary<FeatureSceneLayer>();foreach(varkvpinscene_layers){varscene_layer=kvp.KeyasFeatureSceneLayer;varsel_oids=kvp.Value;//If there are attributes then get themif(scene_layer.HasAssociatedFeatureService){varinsp=newInspector();foreach(varobjectidinsel_oids){insp.Load(scene_layer,objectid);//TODO something with retrieved attributes}}}}
Has Associated FeatureService
if(featSceneLayer.HasAssociatedFeatureService){//Can Select and Search...possibly edit}
Search Rows on the FeatureSceneLayer
if(!featSceneLayer.HasAssociatedFeatureService)return;//Search and Select not supported//Multipatch (Object3D) or point?//var is3dObject = ((ISceneLayerInfo)featSceneLayer).SceneServiceLayerType // == esriSceneServiceLayerType.Object3D;varis3dObject=featSceneLayer.FeatureSceneLayerType==FeatureSceneLayerType.Object3D;// Note: call within QueuedTask.Run(){varqueryFilter=newQueryFilter{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.CurrentasFeature){oid=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
if(featSceneLayer.HasAssociatedFeatureService)return;//Search and Select not supported// Note: call within QueuedTask.Run(){QueryFilterqf=newQueryFilter(){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
varsname=featSceneLayer.Name;// Note: call within QueuedTask.Run(){//Select all features within the current map viewvarsz=MapView.Active.GetViewSize();varc_ll=newCoordinate2D(0,0);varc_ur=newCoordinate2D(sz.Width,sz.Height);//Use screen coordinates for 3D selection on MapViewvarenv=EnvelopeBuilderEx.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.ToDictionary().Where(kvp =>kvp.Key.Name==sname).First().Value;//TODO - use the object idsMapView.Active.Map.ClearSelection();}
Use Select or Search with a Spatial Query
// Note: call within 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(newSystem.Windows.Point(0,sz.Height));varmap_pt2=MapView.Active.ClientToMap(newSystem.Windows.Point(sz.Width,0));//Convert to an envelopevartemp_env=EnvelopeBuilderEx.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)asEnvelope;//Set up a query filtervarsf=newSpatialQueryFilter(){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.CurrentasFeature){oid=feature.GetObjectID();//etc.}}}}MapView.Active.Map.ClearSelection();}
Query all class codes and labels in the PointCloudSceneLayer
// Note: call within QueuedTask.Run(){Dictionary<int,string>classCodesAndLabels=pointCloudSceneLayer.GetAvailableClassCodesAndLabels();}
Set a Filter for PointCloudSceneLayer
// Note: call within QueuedTask.Run(){//Retrieve the available classification codesvardict=pointCloudSceneLayer.GetAvailableClassCodesAndLabels();//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=newPointCloudFilterDefinition(){ClassCodes=dict.Keys.Where(c =>c!=7&&c!=1).ToList(),ReturnValues=newList<PointCloudReturnType>{PointCloudReturnType.FirstOfMany}};//apply the filterpointCloudSceneLayer.SetFilters(filterDef.ToCIM());}
Update the ClassFlags for PointCloudSceneLayer
// Note: call within QueuedTask.Run(){varfilters=pointCloudSceneLayer.GetFilters();PointCloudFilterDefinitionfdef=null;if(filters.Count()==0){fdef=newPointCloudFilterDefinition(){//7 is "edge of flight line" - excludeClassFlags=newList<ClassFlag>{newClassFlag(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" - excludekeep.Add(newClassFlag(7,ClassFlagOption.Exclude));fdef.ClassFlags=keep;}//applypointCloudSceneLayer.SetFilters(fdef.ToCIM());}
Get the filters for PointCloudSceneLayer
// Note: call within QueuedTask.Run(){IReadOnlyList<CIMPointCloudFilter>updatedFilter=pointCloudSceneLayer.GetFilters();foreach(varfinupdatedFilter){//There is either 0 or 1 of eachif(fisCIMPointCloudReturnFilterreturnFilter){PointCloudFilterDefinitionpcfl=PointCloudFilterDefinition.FromCIM(updatedFilter);List<PointCloudReturnType>updatedReturnValues=pcfl.ReturnValues;}if(fisCIMPointCloudValueFilterclassCodesFilter){// do something}if(fisCIMPointCloudBitFieldFilterclassFlagsFilter){// do something}}}
Clear filters in PointCloudSceneLayer
// Note: call within QueuedTask.Run()pointCloudSceneLayer.ClearFilters();
Get PointCloudSceneLayer Renderer and RendererType
// Note: call within QueuedTask.Run(){CIMPointCloudRenderercimGetPCLRenderer=pointCloudSceneLayer.GetRenderer();//Can be one of Unknown, Stretch, ClassBreaks, UniqueValue, RGBPointCloudRendererTypepclRendererType=pointCloudSceneLayer.RendererType;}
Query PointCloudSceneLayer Renderer fields
// Note: call within QueuedTask.Run(){IReadOnlyList<string>flds=pointCloudSceneLayer.GetAvailablePointCloudRendererFields(PointCloudRendererType.UniqueValueRenderer);varfldCount=flds.Count;}
Create and Set a Stretch Renderer
// Note: call within QueuedTask.Run(){varfields=pointCloudSceneLayer.GetAvailablePointCloudRendererFields(PointCloudRendererType.StretchRenderer);varstretchDef=newPointCloudRendererDefinition(PointCloudRendererType.StretchRenderer){//Will be either ELEVATION or INTENSITYField=fields[0]};//Create the CIM RenderervarstretchRenderer=pointCloudSceneLayer.CreateRenderer(stretchDef)asCIMPointCloudStretchRenderer;//Apply a color rampvarstyle=Project.Current.GetItems<StyleProjectItem>().First(s =>s.Name=="ArcGIS Colors");varcolorRamp=style.SearchColorRamps("").First();stretchRenderer.ColorRamp=colorRamp.ColorRamp;//Apply modulationstretchRenderer.ColorModulation=newCIMColorModulationInfo(){MinValue=0,MaxValue=100};//apply the rendererpointCloudSceneLayer.SetRenderer(stretchRenderer);}
Create and Set a ClassBreaks Renderer
// Note: call within QueuedTask.Run(){varfields=pointCloudSceneLayer.GetAvailablePointCloudRendererFields(PointCloudRendererType.ClassBreaksRenderer);varclassBreakDef=newPointCloudRendererDefinition(PointCloudRendererType.ClassBreaksRenderer){//ELEVATION or INTENSITYField=fields[0]};//create the renderervarcbr=pointCloudSceneLayer.CreateRenderer(classBreakDef)asCIMPointCloudClassBreaksRenderer;//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")asColorRampStyleItem;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=newCIMColorClassBreak(){UpperBound=upper,Label=string.Format("{0:#0.0#} - {1:#0.0#}",lower,upper),Color=colors[b-1]};classBreaks.Add(cb);}cbr.Breaks=classBreaks.ToArray();pointCloudSceneLayer.SetRenderer(cbr);}
PointCloudSceneLayer Extended Properties
Edit Color Modulation
// Note: call within QueuedTask.Run(){vardef=pointCloudSceneLayer.GetDefinition()asCIMPointCloudLayer;//Get the ColorModulation off the renderervarmodulation=def.Renderer.ColorModulation;if(modulation==null)modulation=newCIMColorModulationInfo();//Set the minimum and maximum intensity as neededmodulation.MinValue=0;modulation.MaxValue=100.0;//apply backdef.Renderer.ColorModulation=modulation;//Commit changes back to the CIMpointCloudSceneLayer.SetDefinition(def);}
Edit The Renderer to use Fixed Size
// Note: call within QueuedTask.Run(){vardef=pointCloudSceneLayer.GetDefinition()asCIMPointCloudLayer;//Set the point shape and sizing on the rendererdef.Renderer.PointShape=PointCloudShapeType.DiskShaded;varpointSize=newCIMPointCloudFixedSizeAlgorithm(){UseRealWorldSymbolSizes=false,Size=8};def.Renderer.PointSizeAlgorithm=pointSize;//Commit changes back to the CIMpointCloudSceneLayer.SetDefinition(def);}
Edit the Renderer to Scale Size
// Note: call within QueuedTask.Run(){vardef=pointCloudSceneLayer.GetDefinition()asCIMPointCloudLayer;//Set the point shape and sizing on the rendererdef.Renderer.PointShape=PointCloudShapeType.DiskFlat;//defaultvarscaleSize=newCIMPointCloudSplatAlgorithm(){MinSize=8,ScaleFactor=1.0//100%};def.Renderer.PointSizeAlgorithm=scaleSize;//Commit changes back to the CIMpointCloudSceneLayer.SetDefinition(def);}
Edit Density settings
// Note: call within QueuedTask.Run(){vardef=pointCloudSceneLayer.GetDefinition()asCIMPointCloudLayer;//PointsBudget - corresponds to Display Limit on the UI// - the absolute maximum # of points to displaydef.PointsBudget=1000000;//PointsPerInch - corresponds to Density Min --- Max on the UI// - the max number of points per display inch to rendererdef.PointsPerInch=15;//Commit changes back to the CIMpointCloudSceneLayer.SetDefinition(def);}