Filter 3D layer by intersect of 2D layer

481
2
01-20-2020 11:39 AM
KennethLindhardt1
Occasional Contributor II

Hi, I had an idea of hacking the lack of 3D time enabling in the time widget, by using the 2D time enabled layer and filter on intersect to the 3D models.

In this example https://developers.arcgis.com/javascript/latest/sample-code/layers-scenelayer-feature-masking/index.... it uses a geometry to use either intersect or disjoint. So I was wondering how I could make it possible to filter by another layer, that is automated rolled out by the time widget.

Has anyone tried to do similar?

The example doesn’t really let me understand how to split it a part for the use of own layers:

<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<title>Filter features by geometry with SceneLayer - 4.14</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}

#infoDiv {
background-color: white;
padding: 10px;
width: 260px;
margin: 5px;
position: absolute;
bottom: 15px;
right: 10px;
font-size: 14px;
display: none;
}

.geometry-options {
display: flex;
flex-direction: row;
}

.geometry-button {
flex: 1;
border-style: solid;
border-width: 1px;
border-image: none;
}

.geometry-button-selected {
background: #4c4c4c;
color: #fff;
}

.options {
max-width: 260px;
width: 100%;
height: 25px;
}

#bufferNum {
width: 90%;
margin: 2.5em auto 0;
}
</style>

<link
rel="stylesheet"
href="https://js.arcgis.com/4.14/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.14/"></script>

<script>
require([
"esri/WebScene",
"esri/views/SceneView",
"esri/layers/GraphicsLayer",
"esri/widgets/Sketch/SketchViewModel",
"esri/widgets/Slider",
"esri/views/layers/support/FeatureFilter",
"esri/geometry/geometryEngine",
"esri/Graphic"
], function(
WebScene,
SceneView,
GraphicsLayer,
SketchViewModel,
Slider,
FeatureFilter,
geometryEngine,
Graphic
) {
// Load webscene and display it in a SceneView
const webscene = new WebScene({
portalItem: {
id: "47241277f5c249d6b1c13840192a7cb0"
}
});

// create the SceneView
const view = new SceneView({
container: "viewDiv",
map: webscene
});

// add a GraphicsLayer for the sketches and the buffer
let sketchLayer = new GraphicsLayer();
let bufferLayer = new GraphicsLayer();
view.map.addMany([bufferLayer, sketchLayer]);

// create the layerView's to add the filter
let sceneLayerView = null;
let featureLayerView = null;
view.map.load().then(function() {
// loop through webmap's operational layers
view.map.layers.forEach(function(layer, index) {
view
.whenLayerView(layer)
.then(function(layerView) {
if (layer.type === "feature") {
featureLayerView = layerView;
}
if (layer.type === "scene") {
sceneLayerView = layerView;
}
})
.catch(console.error);
});
});

const bufferNumSlider = new Slider({
container: "bufferNum",
min: 0,
max: 1000,
steps: 1,
labelsVisible: true,
precision: 0,
labelFormatFunction: function(value, type) {
return value.toString() + "m";
},
values: [0]
});

let bufferSize = 0;
bufferNumSlider.on(
["thumb-change", "thumb-drag"],
bufferVariablesChanged
);
function bufferVariablesChanged(event) {
bufferSize = event.value;
updateFilter();
}

// use SketchViewModel to draw polygons that are used as a filter
let sketchGeometry = null;
const sketchViewModel = new SketchViewModel({
layer: sketchLayer,
view: view,
pointSymbol: {
type: "simple-marker",
style: "circle",
size: 10,
color: [255, 255, 255, 0.8],
outline: {
color: [211, 132, 80, 0.7],
size: 10
}
},
polylineSymbol: {
type: "simple-line",
color: [211, 132, 80, 0.7],
width: 6
},
polygonSymbol: {
type: "polygon-3d",
symbolLayers: [
{
type: "fill",
material: {
color: [255, 255, 255, 0.8]
},
outline: {
color: [211, 132, 80, 0.7],
size: "10px"
}
}
]
}
});

sketchViewModel.on(["create"], function(event) {
// update the filter every time the user finishes drawing the filtergeometry
if (event.state == "complete") {
sketchGeometry = event.graphic.geometry;
updateFilter();
}
});

sketchViewModel.on(["update"], function(event) {
const eventInfo = event.toolEventInfo;
// update the filter every time the user moves the filtergeometry
if (eventInfo && eventInfo.type.includes("move")) {
if (eventInfo.type === "move-stop") {
sketchGeometry = event.graphics[0].geometry;
updateFilter();
}
}
// update the filter every time the user changes the vertices of the filtergeometry
if (eventInfo && eventInfo.type.includes("reshape")) {
if (eventInfo.type === "reshape-stop") {
sketchGeometry = event.graphics[0].geometry;
updateFilter();
}
}
});

// select the layer to filter on
let featureLayerViewFilterSelected = true;
document
.getElementById("featureLayerViewFilter")
.addEventListener("change", function(ev) {
featureLayerViewFilterSelected = !!ev.target.checked;
updateFilter();
});
let sceneLayerViewFilterSelected = true;
document
.getElementById("sceneLayerViewFilter")
.addEventListener("change", function(ev) {
sceneLayerViewFilterSelected = !!ev.target.checked;
updateFilter();
});

// draw geometry buttons - use the selected geometry to sktech
document.getElementById(
"point-geometry-button"
).onclick = geometryButtonsClickHandler;
document.getElementById(
"line-geometry-button"
).onclick = geometryButtonsClickHandler;
document.getElementById(
"polygon-geometry-button"
).onclick = geometryButtonsClickHandler;
function geometryButtonsClickHandler(event) {
const geometryType = event.target.value;
clearFilter();
sketchViewModel.create(geometryType);
}

// get the selected spatialRelationship
let selectedFilter = "disjoint";
document
.getElementById("relationship-select")
.addEventListener("change", function(event) {
var select = event.target;
selectedFilter = select.options[select.selectedIndex].value;
updateFilter();
});

// remove the filter
document
.getElementById("clearFilter")
.addEventListener("click", function() {
clearFilter();
});

function clearFilter() {
sketchGeometry = null;
filterGeometry = null;
sketchLayer.removeAll();
bufferLayer.removeAll();
sceneLayerView.filter = null;
featureLayerView.filter = null;
}

// set the geometry filter on the visible FeatureLayerView
function updateFilter() {
updateFilterGeometry();
featureFilter = {
// autocasts to FeatureFilter
geometry: filterGeometry,
spatialRelationship: selectedFilter
};

if (featureLayerView) {
if (featureLayerViewFilterSelected) {
featureLayerView.filter = featureFilter;
} else {
featureLayerView.filter = null;
}
}
if (sceneLayerView) {
if (sceneLayerViewFilterSelected) {
sceneLayerView.filter = featureFilter;
} else {
sceneLayerView.filter = null;
}
}
}

// update the filter geometry depending on bufferSize
let filterGeometry = null;
function updateFilterGeometry() {
// add a polygon graphic for the bufferSize
if (sketchGeometry) {
if (bufferSize > 0) {
var bufferGeometry = geometryEngine.geodesicBuffer(
sketchGeometry,
bufferSize,
"meters"
);
if (bufferLayer.graphics.length === 0) {
bufferLayer.add(
new Graphic({
geometry: bufferGeometry,
symbol: sketchViewModel.polygonSymbol
})
);
} else {
bufferLayer.graphics.getItemAt(0).geometry = bufferGeometry;
}
filterGeometry = bufferGeometry;
} else {
bufferLayer.removeAll();
filterGeometry = sketchGeometry;
}
}
}

document.getElementById("infoDiv").style.display = "block";
});
</script>
</head>

<body>
<div id="viewDiv"></div>
<div id="infoDiv" class="esri-widget">
<b>Filter by geometry</b><br /><br />
Select the layers to filter on:<br />
<input id="sceneLayerViewFilter" type="checkbox" checked />
Buildings<br />
<input id="featureLayerViewFilter" type="checkbox" checked /> Trees<br />
<br />Draw a geometry to filter by:
<div class="geometry-options">
<button
class="esri-widget--button esri-icon-map-pin geometry-button"
id="point-geometry-button"
value="point"
title="Filter by point"
></button>
<button
class="esri-widget--button esri-icon-polyline geometry-button"
id="line-geometry-button"
value="polyline"
title="Filter by line"
></button>
<button
class="esri-widget--button esri-icon-polygon geometry-button"
id="polygon-geometry-button"
value="polygon"
title="Filter by polygon"
></button>
</div>
<br />
<label for="relationship-select">Spatial relationship:</label>
<select id="relationship-select" class="options">
<option value="intersects">intersects</option>
<option value="contains">contains</option>
<option value="disjoint" selected>disjoint</option> </select
><br /><br />
<label for="bufferNum">Set a geometry buffer size:</label>
<div id="bufferNum"></div>
<br /><br />
<button class="esri-button" id="clearFilter" type="button">
Clear filter
</button>
</div>
</body>
</html>
0 Kudos
2 Replies
RalucaNicola1
Esri Contributor

Hi,

that would be possible, but I'm not sure it's optimal, there are a lot of queries involved and I'm not sure you need them. Can you tell me more about your use case?

If you have time attributes in the SceneLayer I guess you could use a watch on the timeExtent for the TimeSlider and then when it changes, you could set a filter on the SceneLayerView where you filter by those attributes. 

Also if you can share the data then I could create an example.

Thanks,

Raluca

0 Kudos
KennethLindhardt1
Occasional Contributor II

Hi, thank you for your reply.

I got multiple layers, and not all are very suitable for having a time field, since it would be too much work, when I’m going to update the time field.

So I thought of using a time enabled feature layer as the spatial filter. An on top of that, I thought I could then use the time widget on the feature layer.

So in the final setup the timeline would play a transparent time enabled feature layer, and the the webscenes layer would only show when they are touched by the “growing” feature layer. But yes, for me the sandbox example, get’s too complicated for me, to split into doing what I wan’t.

But if I could just change the graphic layer to a feature layer, connect that to the time widget, and update the scene layer, as the time interval runs, then it would be amazing, but all my test fails.

Thanks.

0 Kudos