I’m facing an issue while trying to apply a UniqueValueRenderer on a 3D FeatureLayer in a SceneView. My goal is to change the color of the features based on an alert field coming from a CSV file.
The interesting part is that, as the date on the timeslider changes, the color filling the polygons changes based on the alert value.
I have managed to get it working perfectly in 2D (MapView), but when switching to 3D, the layer is not displayed, and I get the following error in the developer tab:
Graphic in layer 19227fb6798-layer-0 has no symbol and will not render.
In some combinations I’ve tried by changing parameters in the code below, I managed to display the layer at some point, but all the entities had the same color.
What I’m doing:
-
Loading a CSV file containing alert data (values like “A5”, “A4”, etc.) linked to an ID (comarca_sg) field in the layer.
-
Using a UniqueValueRenderer to change the color of the features based on the comarca_sg field and the alert value from the CSV.
-
The function getColorBasedOnAlert assigns a color depending on the alert value (“A5” = dark red, “A4” = light red, etc.).
The problem:
• In 2D, the colors change correctly for each feature.
• In 3D (SceneView), **the layer is not displayed ** or all the features render with the same color, even though the same UniqueValueRenderer is applied.
Here’s the code:
require([
"esri/Map",
"esri/views/MapView",
"esri/views/SceneView",
"esri/layers/FeatureLayer",
"esri/widgets/TimeSlider",
"esri/geometry/Extent",
"esri/core/watchUtils",
"esri/request"
], function(Map, MapView,SceneView, FeatureLayer, TimeSlider, Extent, watchUtils, esriRequest) {
// Crear el mapa y la vista de escena
var map = new Map({
basemap: "gray-vector", // Basemap actualizado
ground: "world-elevation"
});
// Create the SceneView and set initial camera
const view = new SceneView({
container: "viewDiv",
map: map,
camera: {
position: {
latitude: 40.5,
longitude: -3.7,
z: 3500000
},
tilt: 0,
heading: 1
},
highlightOptions: {
color: "cyan"
}
});
// VISOR EN 2D
// const view = new MapView({
// container: "viewDiv",
// map: map,
// zoom: 5, // Ajusta el zoom para un nivel adecuado de detalle
// center: [-3.7038, 40.4168] // Coordenadas de Madrid, España (longitud, latitud)
// });
layer.elevationInfo = {
mode: "relative-to-ground",
offset: 50,
unit: "meters"
}
// Cargar la capa COMARCAS
var featureLayer = new FeatureLayer({
url: "url_to_layer", // Sorry but it is private
outFields: ["*"]
});
featureLayer.when(function() {
console.log("Capa cargada correctamente:");
}, function(error) {
console.error("Error al cargar la capa.", error);
});
map.add(featureLayer);
// Cargar el CSV con los datos de simbología y fecha
esriRequest("path_to_file", { // Sorry again but it is private
responseType: "text"
}).then(function(response) {
console.log("CSV cargado con éxito."); // Verifica si el CSV fue cargado
const csvData = parseCSV(response.data); // Parsear el CSV
// Crear y configurar el TimeSlider
var timeSlider = new TimeSlider({
container: "timeSliderDiv",
view: view,
mode: "instant", // Selecciona una fecha a la vez
fullTimeExtent: {
start: new Date(2020, 11, 28), //OJO QUE LOS MESES VAN DE 0 A 11
end: new Date(2023, 11, 31)
},
stops: {
interval: {
value: 7,
unit: "days"
}
}
});
view.ui.add(timeSlider, "bottom-left");
// Vincular el cambio de fecha del TimeSlider con la capa
timeSlider.watch("timeExtent", function(timeExtent) {
const selectedDate = timeSlider.timeExtent.end;
// Filtrar el CSV para obtener los elementos correspondientes a la fecha seleccionada
const filteredData = csvData.filter(row => {
return new Date(row.fecha).toDateString() === selectedDate.toDateString();
});
// Si hay datos correspondientes a esa fecha, actualizar simbología
if (filteredData.length > 0) {
updateLayerSymbology(filteredData);
} else {
console.log("No se encontraron datos para la fecha seleccionada.");
}
});
// Función para actualizar la simbología en la capa
function updateLayerSymbology(data) {
const uniqueValues = data.map(function(element) {
return {
value: element.comarca_sg, // Asumimos que "id" es el campo que relaciona los elementos del CSV con los polígonos en la capa
symbol: {
type: "polygon-3d", // Tipo de símbolo para polígonos 3D
symbolLayers: [{
type: "extrude",
size: 5,
material: {color: getColorBasedOnAlert(element.alerta)}, // Asignar el color según la alerta
outline: { color: "black" }
}]
}
};
});
console.log("Valores únicos aplicados a los polígonos:", uniqueValues);
const renderer = {
type: "uniqueValue",
field: "comarca_sg",
uniqueValueInfos: uniqueValues,
};
// Asignar el renderer a la capa
featureLayer.renderer = renderer;
}
// Función para obtener el color basado en el valor de alerta
function getColorBasedOnAlert(alerta) {
alerta = alerta.trim();
// Verificar el valor de alerta
if (alerta === "A5") {
return "#d73027"; // Rojo oscuro
} else if (alerta === "A4") {
return "#fc8d59"; // Rojo claro
} else if (alerta === "A3") {
return "#fee08b"; // Amarillo
} else if (alerta === "A2") {
return "#d9ef8b"; // Amarillo verdoso
} else if (alerta === "A1") {
return "#91cf60"; // Verde claro
} else if (alerta === "A0" || alerta === "NO ALERTA") {
return "#1a9850"; // Verde intenso
} else {
return "#1a9850"; // Verde por defecto para valores desconocidos
}
}
// Función para parsear el CSV
function parseCSV(csvText) {
const rows = csvText.split("n").map(row => row.trim()); // Eliminar espacios innecesarios
const headers = rows[0].split(";").map(header => header.trim()); // Obtener los encabezados y eliminar espacios
// Mapear los encabezados a los índices que necesitamos
const indexId = headers.indexOf("comarca_sg");
const indexFecha = headers.indexOf("date");
const indexAlerta = headers.indexOf("riesgo");
return rows.slice(1).map(row => {
const columns = row.split(";").map(item => item.trim()); // Dividir por punto y coma
return {
comarca_sg: columns[indexId],
fecha: new Date(columns[indexFecha]),
alerta: columns[indexAlerta]
};
});
}
}).catch(function(error) {
console.error("Error al cargar el archivo CSV:", error); // Mostrar error en caso de que el CSV no se cargue
});
}); // LLAVES DEL FIN DEL CODIGO
Questions:
-
Is there anything different I should consider when applying a UniqueValueRenderer in a SceneView compared to a MapView?
-
Could there be a limitation or difference in how renderers are applied in 3D?
-
I’m sure there is an error in the code that prevents the layer from displaying, but I can’t locate it.
I would appreciate any advice or suggestions on how to solve this problem. Thanks in advance!
What I’ve tried:
• Verified that the alert values are passed correctly using console.log().
• Tried removing the defaultSymbol to ensure it wasn’t overriding the renderer.
• Ensured that the comarca_sg field is correctly aligned between the CSV and the layer.
• I placed the layer in a scene view in AGOL, and it displays without issues, allowing me to visualize unique colors for unique values.