|
|
// import React, { useState, useEffect, useRef } from "react";// import FeatureLayer from "@arcgis/core/layers/FeatureLayer";// import { type AllWidgetProps } from 'jimu-core';// import "./simple_widget.css";// import { JimuMapViewComponent, type JimuMapView } from 'jimu-arcgis';// import { mapViewManager } from '../../../utils/mapview';// import Graphic from "@arcgis/core/Graphic";// import SimpleFillSymbol from "@arcgis/core/symbols/SimpleFillSymbol";// import SimpleLineSymbol from "@arcgis/core/symbols/SimpleLineSymbol";// import zoomInIcon from './assets/zoom-in.svg';// import jsPDF from "jspdf";// import autoTable from "jspdf-autotable";
// const RoadAssetFilter = (props: AllWidgetProps<any>) => {// // State variables// const [selectedZone, setSelectedZone] = useState<string>("");// const [selectedWard, setSelectedWard] = useState<string>("");// const [zoneOptions, setZoneOptions] = useState<string[]>([]);// const [wardOptions, setWardOptions] = useState<string[]>([]);// const [jimuMapView, setJimuMapView] = useState<JimuMapView>();// const [selectedLayer, setSelectedLayer] = useState<string>("");// const [selectedUtilityLayer, setSelectedUtilityLayer] = useState<string>("All");// const [utilityCount, setUtilityCount] = useState<number | null>(null);// const [tableData, setTableData] = useState<any[]>([]);// const [utilityLayerOptions, setUtilityLayerOptions] = useState<string[]>([]);// const [selectedRoadName, setSelectedRoadName] = useState<string>("");// const [roadNameOptions, setRoadNameOptions] = useState<string[]>([]);// const [isLoading, setIsLoading] = useState(false);// const [activeStep, setActiveStep] = useState<number>(0);// const addedUtilityLayerRef = useRef<any | null>(null);
// // Refs for cleanup// const highlightRef = useRef<any>(null);// const graphicsLayerRef = useRef<any>(null);
// // URLs for layers// const ZONE_LAYER_URL = "https://gisgcc.chennaicorporation.gov.in/server/rest/services/GCCDepts/GCC_Administration_Boundaries/FeatureServer/3";// const WARD_LAYER_URL = "https://gisgcc.chennaicorporation.gov.in/server/rest/services/GCCDepts/GCC_Administration_Boundaries/FeatureServer/4";// const UTILITY_GROUP_URL = "https://gisgcc.chennaicorporation.gov.in/server/rest/services/GCCDepts/Utility/FeatureServer";// const ROAD_LAYER_URL = "https://gisgcc.chennaicorporation.gov.in/server/rest/services/GCCDepts/Transporations/FeatureServer/0";
// // Utility layer configuration// const utilityLayers = [// { id: 1, title: "CCTV" },// { id: 2, title: "Gantry Sign Boards" },// { id: 3, title: "Garbage Bins" },// { id: 4, title: "Hand Pump" },// { id: 7, title: "Manhole" },// { id: 8, title: "Permanent Bunkshop" },// { id: 11, title: "Police Booth" },// { id: 12, title: "Public Taps" },// { id: 17, title: "Street Name Board" },// { id: 19, title: "Traffic Signals" },// { id: 21, title: "Transformer" },// { id: 23, title: "Water Tanks" },// { id: 30, title: "Bus Terminus" },// { id: 33, title: "Footpath" },// { id: 34, title: "Median" },// { id: 36, title: "Parking Stretch" },// { id: 40, title: "Traffic Island" }// ];
// // Utility to escape single quotes in SQL// const safe = (val: string) => val.replace(/'/g, "''");
// // Populate Zone dropdown// const populateZone = async () => {// try {// const layer = new FeatureLayer({ url: ZONE_LAYER_URL });// const query = layer.createQuery();// query.returnDistinctValues = true;// query.returnGeometry = false;// query.outFields = ["zone"];// query.where = "1=1";// const result = await layer.queryFeatures(query);// if (result) {// const zones = Array.from(new Set(result.features.map(f => f.attributes.zone).filter(Boolean)));// zones.sort((a, b) => Number(a) - Number(b));// setZoneOptions(['All', ...zones]);// }// } catch (error) {// console.error("Error fetching zones:", error);// }// }; // useEffect(() => {// populateZone();// }, []);
// // Populate Ward dropdown based on selected Zone// const populateWard = async () => {// try {// const layer = new FeatureLayer({ url: WARD_LAYER_URL });// const query = layer.createQuery();// query.returnDistinctValues = true;// query.returnGeometry = false;// query.outFields = ["ward_no"];// if (selectedZone && selectedZone !== 'All') {// query.where = `zone_no = '${safe(selectedZone)}'`;// } else {// query.where = "1=1";// }// const result = await layer.queryFeatures(query);// if (result) {// const wards = Array.from(new Set(result.features.map(f => f.attributes.ward_no).filter(Boolean)));// wards.sort((a, b) => Number(a) - Number(b));// setWardOptions(['All', ...wards]);// setSelectedWard(""); // Reset ward when zone changes// setActiveStep(1); // Move to next step// }// } catch (error) {// console.error("Error fetching wards:", error);// }// }; // useEffect(() => {// if (selectedZone) {// populateWard();// }// }, [selectedZone]);
// // Populate utility layer options// useEffect(() => {// if (selectedLayer === "Utility") {// const titles = utilityLayers.map(l => l.title);// setUtilityLayerOptions(["All", ...titles]);// setSelectedUtilityLayer("All");// setActiveStep(2); // Move to next step// }// }, [selectedLayer]);
// // Populate road names for selected utility layer// const populateRoadNames = async (utilityLayer: string) => {// if (!selectedZone || !selectedWard || !utilityLayer || utilityLayer === "All") return; // const layerInfo = utilityLayers.find(l => l.title === utilityLayer);// if (!layerInfo) return;
// const layerUrl = `${UTILITY_GROUP_URL}/${layerInfo.id}`;// const layer = new FeatureLayer({ url: layerUrl });
// try {// const query = layer.createQuery();// query.returnDistinctValues = true;// query.outFields = ["road_name"];// query.returnGeometry = false; // let whereClause = "";// if (selectedZone && selectedZone !== 'All') {// whereClause += `zone_number = '${safe(selectedZone)}'`;// } // if (selectedWard && selectedWard !== 'All') {// whereClause += whereClause ? ` AND ward_number = '${safe(selectedWard)}'` : `ward_number = '${safe(selectedWard)}'`;// } // query.where = whereClause || "1=1"; // const result = await layer.queryFeatures(query);// const names = Array.from(// new Set(result.features.map(f => f.attributes.road_name).filter(Boolean))// );// setRoadNameOptions(names.sort());// setActiveStep(3); // Move to next step// } catch (err) {// console.error("Failed to fetch road names:", err);// setRoadNameOptions([]);// }// };
// // Build where clause for queries// const buildWhereClause = () => {// const clauses: string[] = [];
// if (selectedZone && selectedZone !== 'All') {// clauses.push(`zone_number = '${safe(selectedZone)}'`);// }
// if (selectedWard && selectedWard !== 'All') {// clauses.push(`ward_number = '${safe(selectedWard)}'`);// }
// if (selectedRoadName) {// clauses.push(`road_name = '${safe(selectedRoadName)}'`);// }
// return clauses.length > 0 ? clauses.join(" AND ") : "1=1";// };
// // Zoom to zone geometry// const zoomToZoneGeometry = async (zoneValue: string) => {// if (!jimuMapView || !zoneValue || zoneValue === 'All') return;// const view = jimuMapView.view;// const zoneLayer = new FeatureLayer({ url: ZONE_LAYER_URL }); // try {// await zoneLayer.load();// const query = zoneLayer.createQuery();// query.where = `zone = '${safe(zoneValue)}'`;// query.outFields = ["*"];// query.returnGeometry = true; // const result = await zoneLayer.queryFeatures(query);// if (result.features.length > 0) {// const geometry = result.features[0].geometry;// await view.goTo({// target: geometry,// padding: { top: 50, bottom: 50, left: 50, right: 50 },// duration: 1000// });// }// } catch (error) {// console.error("Error zooming to zone:", error);// }// };
// // Zoom to ward geometry// const zoomToWardGeometry = async (wardValue: string) => {// if (!jimuMapView || !wardValue || wardValue === 'All') return;// const view = jimuMapView.view;// const wardLayer = new FeatureLayer({ url: WARD_LAYER_URL }); // try {// await wardLayer.load();// const query = wardLayer.createQuery();// query.where = `ward_no = '${safe(wardValue)}'`;// query.outFields = ["*"];// query.returnGeometry = true; // const result = await wardLayer.queryFeatures(query);// if (result.features.length > 0) {// const geometry = result.features[0].geometry;// await view.goTo({// target: geometry,// padding: { top: 50, bottom: 50, left: 50, right: 50 },// duration: 1000// });// }// } catch (error) {// console.error("Error zooming to ward:", error);// }// };
// // Get feature count for utility layer// const getFeatureCount = async (layerUrl: string): Promise<number> => {// const layer = new FeatureLayer({ url: layerUrl });// const query = layer.createQuery();// query.where = buildWhereClause();// query.returnGeometry = false; // try {// return await layer.queryFeatureCount(query);// } catch (error) {// console.error("Error counting features:", error);// return 0;// }// };
// // Update utility count when filters change// useEffect(() => {// const fetchCount = async () => {// if (selectedUtilityLayer && selectedUtilityLayer !== "All") {// const layerInfo = utilityLayers.find(l => l.title === selectedUtilityLayer);// if (layerInfo) {// const count = await getFeatureCount(`${UTILITY_GROUP_URL}/${layerInfo.id}`);// setUtilityCount(count);// }// } else {// setUtilityCount(null);// }// };
// if (selectedLayer === "Utility") {// fetchCount();// }// }, [selectedZone, selectedWard, selectedUtilityLayer, selectedRoadName]);
// // Function to update ward boundary visibility// const updateWardBoundaryVisibility = async (wardValue: string) => {// if (!jimuMapView?.view) return;// const map = jimuMapView.view.map; // try {// // Find the ward boundary layer in the map// const wardLayer = map.layers.find(layer => // layer.url === WARD_LAYER_URL// ) as FeatureLayer; // if (wardLayer) {// if (wardValue && wardValue !== "All") {// // Show only the selected ward// wardLayer.definitionExpression = `ward_no = '${safe(wardValue)}'`;// wardLayer.visible = true;// } else {// // Show all wards when "All" is selected// wardLayer.definitionExpression = "1=1";// wardLayer.visible = true;// }// }// } catch (error) {// console.error("Error updating ward boundary visibility:", error);// }// };
// //Apply filters to utility layers// const applyFilters = async () => {// if (!jimuMapView?.view) return;// const view = jimuMapView.view;// const map = view.map;// const defExp = buildWhereClause() || "1=1";// const selected = (selectedUtilityLayer ?? "").toString().trim();
// // ✅ Instead of hiding Asset & Road groups, keep them visible and just apply filters// const roadLayerTitles = ["Road", "CarraigeWay"];// const assetLayerTitles = ["Asset Point", "Asset Building"];
// // --- Road group ---// const roadGroup = map.layers.find(l => l.title === "Transporations") as __esri.GroupLayer;// if (roadGroup && "layers" in roadGroup) {// roadGroup.visible = true;// roadGroup.layers.forEach((sub: any) => {// if (roadLayerTitles.includes(sub.title) && "definitionExpression" in sub) {// // Road schema field is `ward`// sub.definitionExpression = defExp.replace(/ward_number/gi, "ward");// }// sub.visible = true;// });// }
// // --- Asset group ---// const assetGroup = map.layers.find(l => l.title === "GCC Asset Layers") as __esri.GroupLayer;// if (assetGroup && "layers" in assetGroup) {// assetGroup.visible = true;// assetGroup.layers.forEach((sub: any) => {// if (assetLayerTitles.includes(sub.title) && "definitionExpression" in sub) {// // Asset schema field is `ward_no`// sub.definitionExpression = defExp.replace(/ward_number/gi, "ward_no");// }// sub.visible = true;// });// }
// // helper// const safeTitle = (t?: string | null) => (t ?? "").toString().trim();// const lower = (s?: string) => safeTitle(s).toLowerCase();
// try {// // 1) Find Utility group and hide all its children (so we start clean)// // const utilityGroup = map.layers.find(// // (l: any) => safeTitle(l.title).toLowerCase().includes("utility")// // );// // if (utilityGroup && "layers" in utilityGroup && typeof utilityGroup.layers?.toArray === "function") {// // const children = utilityGroup.layers.toArray();// // for (const child of children) {// // try {// // child.visible = false;// // if ("definitionExpression" in child) {// // try { child.definitionExpression = "1=0"; } catch (e) { /* some sublayers may not accept */ }// // }// // } catch (e) {// // console.warn("Error hiding utility child", child, e);// // }// // }// // // hide group itself to avoid stray visuals until we show the selected layer// // utilityGroup.visible = false;// // }// // 1) Find Utility group and hide all its children (start clean)// const utilityGroup = map.layers.find(// (l: any) => safeTitle(l.title).toLowerCase().includes("utility")// );// if (utilityGroup && "layers" in utilityGroup && typeof utilityGroup.layers?.toArray === "function") {// const children = utilityGroup.layers.toArray();// for (const child of children) {// try {// child.visible = false;// if ("definitionExpression" in child) {// child.definitionExpression = "1=0";// }// } catch (e) {// console.warn("Error hiding utility child", child, e);// }// }// // ❌ Don’t hide the group itself// utilityGroup.visible = true; // keep group visible so Transportation & Assets stay untouched// }
// // 2) Remove any previously added temporary FeatureLayer (we'll add a new one if needed)// if (addedUtilityLayerRef.current) {// try {// // remove from map// map.layers.remove(addedUtilityLayerRef.current);// } catch (e) { /* ignore */ }// addedUtilityLayerRef.current = null;// }
// // If "All" -> show all relevant children (try to show children and apply defExp where possible)// if (selected === "" || lower(selected) === "all") {// if (utilityGroup && "layers" in utilityGroup && typeof utilityGroup.layers?.toArray === "function") {// const children = utilityGroup.layers.toArray();// for (const child of children) {// try {// child.visible = true;// if ("definitionExpression" in child) {// try { child.definitionExpression = defExp; } catch (e) { /* ignore */ }// }// } catch (e) { console.warn("Error enabling child for All", child, e); }// }// utilityGroup.visible = true;// }// return;// }
// // 3) Find the utilityLayers entry and service URL for the selected utility// const layerInfo = utilityLayers.find(u => lower(u.title) === lower(selected));// if (!layerInfo) {// console.warn("Selected utility not found in utilityLayers config:", selected);// return;// }// const serviceUrl = `${UTILITY_GROUP_URL}/${layerInfo.id}`;
// // 4) Try to find an existing layer in the map whose .url matches the service url// // or whose sublayer has that url (some map configs attach FeatureLayer as child).// let foundLayer: any = null;
// const walkAndFindByUrl = (top: any) => {// // top-level url// if (top?.url && top.url === serviceUrl) return top;// // child layers (.layers)// if ("layers" in top && typeof top.layers?.toArray === "function") {// for (const c of top.layers.toArray()) {// if (c?.url && c.url === serviceUrl) return c;// }// }// // sublayers (.sublayers)// if ("sublayers" in top && typeof top.sublayers?.toArray === "function") {// for (const s of top.sublayers.toArray()) {// if (s?.url && s.url === serviceUrl) return s;// }// }// return null;// };
// for (const topLayer of map.layers.toArray()) {// try {// const match = walkAndFindByUrl(topLayer);// if (match) { foundLayer = match; break; }// } catch (e) { /* ignore per-layer errors */ }// }
// // 5) If found in map, show and apply definitionExpression (best-case)// if (foundLayer) {// try {// foundLayer.visible = true;// if ("definitionExpression" in foundLayer) {// try { foundLayer.definitionExpression = defExp; } catch (e) { /* ignore */ }// }// // If it has a parent group, make parent visible too:// // (some APIs provide foundLayer.parent or you can set utilityGroup.visible)// if (utilityGroup) utilityGroup.visible = true;// return;// } catch (err) {// console.warn("Error enabling existing layer:", err);// }// }
// // 6) If no matching layer found in map, add a new client FeatureLayer that points to the service endpoint.// // Client FeatureLayer is guaranteed to allow definitionExpression (unless service denies).// try {// const fl = new FeatureLayer({// url: serviceUrl,// title: `${selected} (temp)`,// visible: true,// definitionExpression: defExp// });// await fl.load();// map.layers.add(fl); // add top-level// addedUtilityLayerRef.current = fl;// // ensure Utility group is visible so user sees it in TOC if needed// if (utilityGroup) utilityGroup.visible = true;// return;// } catch (err) {// console.error("Failed to add FeatureLayer for selected utility:", err);// }
// } catch (err) {// console.error("applyFilters error:", err);// }// };
// const applyWardFilterToLayers = async (wardValue: string, viewMap: __esri.Map) => {// if (!wardValue || wardValue === "All") {// // Reset → show all features in all layers// viewMap.layers.forEach(layer => {// if ("layers" in layer) {// layer.layers.forEach((sub: any) => {// if ("definitionExpression" in sub) {// sub.definitionExpression = "1=1"; // show all features// }// sub.visible = true; // keep all visible// });// }// });// return;// }
// const roadLayerTitles = ["Road", "CarraigeWay"];// const assetLayerTitles = ["Asset Point", "Asset Building"];
// // 🔹 Transportation group (filter by `ward`)// const roadGroup = viewMap.layers.find(l => l.title === "Transporations") as __esri.GroupLayer;// if (roadGroup) {// roadGroup.visible = true;// roadGroup.layers.forEach((sub: any) => {// if (roadLayerTitles.includes(sub.title) && "definitionExpression" in sub) {// sub.definitionExpression = `ward = '${wardValue}'`; // ✅ correct field// }// sub.visible = true;// });// }
// // 🔹 Asset group (filter by `ward_no`)// const assetGroup = viewMap.layers.find(l => l.title === "GCC Asset Layers") as __esri.GroupLayer;// if (assetGroup) {// assetGroup.visible = true;// assetGroup.layers.forEach((sub: any) => {// if (assetLayerTitles.includes(sub.title) && "definitionExpression" in sub) {// sub.definitionExpression = `ward_no = '${wardValue}'`; // ✅ correct field// }// sub.visible = true;// });// }// };
// useEffect(() => {// if (jimuMapView?.view && selectedWard) {// applyWardFilterToLayers(selectedWard, jimuMapView.view.map);// updateWardBoundaryVisibility(selectedWard); // Add this line to update ward boundary visibility// }// }, [selectedWard, jimuMapView]);
// // Apply filters when any filter changes// useEffect(() => {// if (jimuMapView && selectedLayer === "Utility") {// applyFilters();// }// }, [selectedZone, selectedWard, selectedUtilityLayer, selectedRoadName, jimuMapView]);
// const handleSubmit = async (e: React.MouseEvent) => {// e.preventDefault();// setIsLoading(true);
// if (!selectedZone) {// alert("Please select a Zone");// setIsLoading(false);// return;// }
// if (!jimuMapView?.view) {// setIsLoading(false);// return;// }
// try {// // 1. Apply filters first// await applyFilters();
// let zoomed = false;
// // 2. Query features only when a single utility is selected// if (selectedLayer === "Utility" && selectedUtilityLayer && selectedUtilityLayer !== "All") {// const layerInfo = utilityLayers.find(l => l.title === selectedUtilityLayer);// if (layerInfo) {// const layer = new FeatureLayer({ url: `${UTILITY_GROUP_URL}/${layerInfo.id}` });// const query = layer.createQuery();// query.where = buildWhereClause();// query.outFields = ["*"];// query.returnGeometry = true;
// const result = await layer.queryFeatures(query);// setTableData(result.features);
// // Zoom to filtered features if any// if (result.features.length > 0) {// const extent = result.features.reduce(// (acc, f) => acc ? acc.union(f.geometry.extent) : f.geometry.extent,// null as __esri.Extent | null// );// if (extent) {// await jimuMapView.view.goTo({ target: extent, padding: 100, duration: 1000 });// zoomed = true;// }// }// }// } else if (selectedLayer === "Utility" && selectedUtilityLayer === "All") {// // Clear table when all utilities are shown// setTableData([]);// }
// // 3. If nothing was zoomed yet, zoom to ward/zone// if (!zoomed) {// if (selectedWard && selectedWard !== "All") {// await zoomToWardGeometry(selectedWard);// } else if (selectedZone && selectedZone !== "All") {// await zoomToZoneGeometry(selectedZone);// }// }
// } catch (error) {// console.error("Error in handleSubmit:", error);// } finally {// setIsLoading(false);// }// };
// // Zoom to individual feature with highlight// const zoomToFeature = async (feature: any) => {// if (!jimuMapView?.view || !feature?.geometry) return;// const view = jimuMapView.view; // try {// // Clear previous highlight// if (highlightRef.current) {// highlightRef.current.remove();// highlightRef.current = null;// } // // Clear any existing graphics// if (graphicsLayerRef.current) {// view.graphics.remove(graphicsLayerRef.current);// }
// // Create highlight symbol based on feature type// let highlightSymbol;// if (feature.geometry.type === "point") {// highlightSymbol = {// type: "simple-marker",// color: [255, 0, 0, 0.8],// size: 12,// outline: {// color: [255, 255, 255],// width: 2// }// };// } else if (feature.geometry.type === "polyline") {// highlightSymbol = new SimpleLineSymbol({// color: [255, 0, 0, 0.8],// width: 4// });// } else {// highlightSymbol = new SimpleFillSymbol({// color: [255, 0, 0, 0.3],// outline: {// color: [255, 0, 0],// width: 2// }// });// }
// // Create highlight graphic// const highlightGraphic = new Graphic({// geometry: feature.geometry,// symbol: highlightSymbol// });
// // Add to map and store reference// view.graphics.add(highlightGraphic);// graphicsLayerRef.current = highlightGraphic;
// // Zoom to feature// await view.goTo({// target: feature.geometry,// zoom: feature.geometry.type === "point" ? 18 : 14,// duration: 1000// });// } catch (error) {// console.error("Error zooming to feature:", error);// }// };
// // Reset button handler// const handleReset = () => {// setSelectedZone("");// setSelectedWard("");// setSelectedLayer("");// setSelectedUtilityLayer("All");// setSelectedRoadName("");// setUtilityCount(null);// setTableData([]);// setRoadNameOptions([]);// setActiveStep(0);
// // Clear graphics & highlights// jimuMapView?.view?.graphics.removeAll();// highlightRef.current?.remove();// highlightRef.current = null;
// if (jimuMapView?.view) {// const map = jimuMapView.view.map;
// // Reset all utility layers// map.layers.forEach(layer => {// if (layer.type === "feature") {// (layer as __esri.FeatureLayer).definitionExpression = "1=1";// layer.visible = true;// } else if ("layers" in layer) {// layer.layers.forEach((sub: any) => {// sub.visible = true;// if (sub.type === "feature") sub.definitionExpression = "1=1";// });// }// });
// // Remove dynamically added utility layer// if (addedUtilityLayerRef.current) {// map.layers.remove(addedUtilityLayerRef.current);// addedUtilityLayerRef.current = null;// }
// // Restore GCC Asset Layers (and its subs)// const assetGroup = map.layers.find(l => l.title === "GCC Asset Layers");// if (assetGroup && "layers" in assetGroup) {// assetGroup.visible = true;// assetGroup.layers.forEach((sub: any) => sub.visible = true);// }
// // Reset ward boundary layer to show all wards// const wardLayer = map.layers.find(layer => // layer.url === WARD_LAYER_URL// ) as FeatureLayer;// if (wardLayer) {// wardLayer.definitionExpression = "1=1";// wardLayer.visible = true;// }// }// };
// // Active view change handler// const activeViewChangeHandler = (jmv: JimuMapView) => {// if (jmv) setJimuMapView(jmv);// };
// // Initialize map view// useEffect(() => {// const interval = setInterval(() => {// const view = mapViewManager.getMapView();// if (view) {// setJimuMapView(view);// clearInterval(interval);// }// }, 500);// return () => clearInterval(interval);// }, []);
// return (// <div className="property-widget-parent">// <div className="property-widget">// <JimuMapViewComponent// useMapWidgetId={props.useMapWidgetIds?.[0]}// onActiveViewChange={activeViewChangeHandler}// />
// <h3 className="widget-title">Utility Asset Filter</h3> // {/* Zone selection */}// <div className="property_form">// <label>Zone:<span style={{ color: 'red' }}>*</span></label>// <select// value={selectedZone}// onChange={e => {// const value = e.target.value;// setSelectedZone(value);// setSelectedWard("");// setSelectedRoadName("");// setRoadNameOptions([]);// if (value && value !== "All") {// zoomToZoneGeometry(value);// }// }}// required// >// <option value="">--Select Zone--</option>// {zoneOptions.map(zone => (// <option key={zone} value={zone}>{zone}</option>// ))}// </select>// </div>
// {/* Ward selection */}// <div className="property_form">// <label>Ward:</label>// <select// value={selectedWard}// onChange={e => {// const value = e.target.value;// setSelectedWard(value);// setSelectedRoadName("");// setRoadNameOptions([]);
// if (value && value !== "All") {// zoomToWardGeometry(value);// if (jimuMapView?.view) {// applyWardFilterToLayers(value, jimuMapView.view.map);// updateWardBoundaryVisibility(value); // ✅ finish call here// }// } else {// // Reset when "All" is selected// if (jimuMapView?.view) {// applyWardFilterToLayers("All", jimuMapView.view.map);// updateWardBoundaryVisibility("All"); // ✅ reset visibility// }// }// }}// >// <option value="">--Select Ward--</option>// {wardOptions.map(ward => (// <option key={ward} value={ward}>{ward}</option>// ))}// </select>// </div>
// {/* Layer selection */}// <div className="property_form">// <label>Select Layer:</label>// <select// value={selectedLayer}// onChange={e => {// const layer = e.target.value;// setSelectedLayer(layer);// setSelectedUtilityLayer("All");// setSelectedRoadName("");// setRoadNameOptions([]);// setUtilityCount(null);// }}// >// <option value="">--Select Layer--</option>// <option value="Utility">Utility</option>// </select>// </div>
// {/* Show filtering options when Utility is selected */}// {selectedLayer === "Utility" && (// <>// {/* Utility Layer selection */}// <div className="property_form">// <label>Select Utility Type:</label>// <select// value={selectedUtilityLayer}// onChange={e => {// const value = e.target.value;// setSelectedUtilityLayer(value);// setSelectedRoadName("");// if (value !== "All") {// populateRoadNames(value);// } else {// setRoadNameOptions([]);// }// }}// disabled={!selectedZone || selectedZone === "All"}// >// <option value="All">All Utilities</option>// {utilityLayerOptions.map(name => (// <option key={name} value={name}>// {name}{selectedUtilityLayer === name && utilityCount !== null ? ` (${utilityCount})` : ""}// </option>// ))}// </select>// </div>
// {/* Road Name selection */}// {selectedUtilityLayer !== "All" && roadNameOptions.length > 0 && (// <div className="property_form">// <label>Select Road Name:</label>// <select// value={selectedRoadName}// onChange={e => setSelectedRoadName(e.target.value)}// >// <option value="">--All Roads--</option>// {roadNameOptions.map(name => (// <option key={name} value={name}>{name}</option>// ))}// </select>// </div>// )}// </>// )}
// {/* Action buttons */}// <div className="button-group">// <button // type="button"// className={`submit-button ${isLoading ? 'loading' : ''}`} // onClick={handleSubmit}// disabled={isLoading || !selectedZone || !selectedLayer}// >// {isLoading ? <div className="spinner"></div> : "Apply Filters"}// </button>// <button // type="button"// className="reset-button" // onClick={handleReset}// >// Reset All// </button>// </div>
// {/* Results table */}// {tableData.length > 0 && (// <div className="attribute-table">// <h4>Asset Details ({tableData.length} features)</h4>// <div className="table-container">// <table>// <thead>// <tr>// <th>ID</th>// <th>Zone</th>// <th>Ward</th>// <th>Road Name</th>// <th>Action</th>// </tr>// </thead>// <tbody>// {tableData.map((feature, index) => {// const attr = feature.attributes;// return (// <tr key={feature.attributes.OBJECTID || index}>// <td>{attr.OBJECTID || index + 1}</td>// <td>{attr.zone_number || 'N/A'}</td>// <td>{attr.ward_number || 'N/A'}</td>// <td>{attr.road_name || 'N/A'}</td>// <td>// <button// onClick={() => zoomToFeature(feature)}// className="zoom-button"// title="Zoom to feature"// >// <img src={zoomInIcon} alt="Zoom" width={16} height={16} />// </button>// </td>// </tr>// );// })}// </tbody>// </table>// </div>// </div>// )}
// {tableData.length === 0 && selectedLayer && (// <div className="no-results">// <p>No assets found with the current filters.</p>// </div>// )}// </div>// </div>// );// };
// export default RoadAssetFilter;
import React, { useState, useEffect, useRef } from "react"; import FeatureLayer from "@arcgis/core/layers/FeatureLayer";import { type AllWidgetProps } from 'jimu-core';import "./simple_widget.css";import { JimuMapViewComponent, type JimuMapView } from 'jimu-arcgis';import { mapViewManager } from '../../../utils/mapview';import Graphic from "@arcgis/core/Graphic";import SimpleFillSymbol from "@arcgis/core/symbols/SimpleFillSymbol";import SimpleLineSymbol from "@arcgis/core/symbols/SimpleLineSymbol";import zoomInIcon from './assets/zoom-in.svg';import jsPDF from "jspdf";import autoTable from "jspdf-autotable";
const RoadAssetFilter = (props: AllWidgetProps<any>) => { // State variables const [selectedZone, setSelectedZone] = useState<string>(""); const [selectedWard, setSelectedWard] = useState<string>(""); const [zoneOptions, setZoneOptions] = useState<string[]>([]); const [wardOptions, setWardOptions] = useState<string[]>([]); const [jimuMapView, setJimuMapView] = useState<JimuMapView>(); const [selectedLayer, setSelectedLayer] = useState<string>(""); const [selectedUtilityLayer, setSelectedUtilityLayer] = useState<string>("All"); const [utilityCount, setUtilityCount] = useState<number | null>(null); const [tableData, setTableData] = useState<any[]>([]); const [utilityLayerOptions, setUtilityLayerOptions] = useState<string[]>([]); const [selectedRoadName, setSelectedRoadName] = useState<string>(""); const [roadNameOptions, setRoadNameOptions] = useState<string[]>([]); const [isLoading, setIsLoading] = useState(false); const [activeStep, setActiveStep] = useState<number>(0); const addedUtilityLayerRef = useRef<any | null>(null);
// Refs for cleanup const highlightRef = useRef<any>(null); const graphicsLayerRef = useRef<any>(null);
// URLs for layers const ZONE_LAYER_URL = "https://gisgcc.chennaicorporation.gov.in/server/rest/services/GCCDepts/GCC_Administration_Boundaries/FeatureServer/3"; const WARD_LAYER_URL = "https://gisgcc.chennaicorporation.gov.in/server/rest/services/GCCDepts/GCC_Administration_Boundaries/FeatureServer/4"; const UTILITY_GROUP_URL = "https://gisgcc.chennaicorporation.gov.in/server/rest/services/GCCDepts/Utility/FeatureServer"; const ROAD_LAYER_URL = "https://gisgcc.chennaicorporation.gov.in/server/rest/services/GCCDepts/Transporations/FeatureServer/0";
// Utility layer configuration const utilityLayers = [ { id: 1, title: "CCTV" }, { id: 2, title: "Gantry Sign Boards" }, { id: 3, title: "Garbage Bins" }, { id: 4, title: "Hand Pump" }, { id: 7, title: "Manhole" }, { id: 8, title: "Permanent Bunkshop" }, { id: 11, title: "Police Booth" }, { id: 12, title: "Public Taps" }, { id: 17, title: "Street Name Board" }, { id: 19, title: "Traffic Signals" }, { id: 21, title: "Transformer" }, { id: 23, title: "Water Tanks" }, { id: 30, title: "Bus Terminus" }, { id: 33, title: "Footpath" }, { id: 34, title: "Median" }, { id: 36, title: "Parking Stretch" }, { id: 40, title: "Traffic Island" } ];
// Utility to escape single quotes in SQL const safe = (val: string) => val.replace(/'/g, "''");// Apply selected utility layer visibility + filterconst applyUtilityLayerSelection = async ( map: __esri.Map, selectedUtilityLayer: string, defExp: string, utilityLayers: { id: number; title: string }[], UTILITY_GROUP_URL: string, addedUtilityLayerRef: React.MutableRefObject<any | null>) => { const safeTitle = (t?: string | null) => (t ?? "").toString().trim(); const lower = (s?: string) => safeTitle(s).toLowerCase();
// 1) Find Utility group and hide all its children first const utilityGroup = map.layers.find( (l: any) => lower(l.title).includes("utility") ); if (utilityGroup && "layers" in utilityGroup && typeof utilityGroup.layers?.toArray === "function") { const children = utilityGroup.layers.toArray(); for (const child of children) { child.visible = false; if ("definitionExpression" in child) { child.definitionExpression = "1=0"; // hide } } utilityGroup.visible = true; }
// 2) Remove any previously added temporary FeatureLayer if (addedUtilityLayerRef.current) { try { map.layers.remove(addedUtilityLayerRef.current); } catch {} addedUtilityLayerRef.current = null; }
// 3) If "All" → show all children if (!selectedUtilityLayer || lower(selectedUtilityLayer) === "all") { if (utilityGroup && "layers" in utilityGroup && typeof utilityGroup.layers?.toArray === "function") { const children = utilityGroup.layers.toArray(); for (const child of children) { child.visible = true; if ("definitionExpression" in child) { child.definitionExpression = defExp; } } utilityGroup.visible = true; } return; }
// 4) Find service url for selected utility const layerInfo = utilityLayers.find(u => lower(u.title) === lower(selectedUtilityLayer)); if (!layerInfo) return; const serviceUrl = `${UTILITY_GROUP_URL}/${layerInfo.id}`;
// 5) Try to find in map by URL let foundLayer: any = null; const walkAndFindByUrl = (top: any): any => { if (top?.url && top.url === serviceUrl) return top; if ("layers" in top && typeof top.layers?.toArray === "function") { for (const c of top.layers.toArray()) { const match = walkAndFindByUrl(c); if (match) return match; } } if ("sublayers" in top && typeof top.sublayers?.toArray === "function") { for (const s of top.sublayers.toArray()) { if (s?.url && s.url === serviceUrl) return s; } } return null; };
for (const topLayer of map.layers.toArray()) { const match = walkAndFindByUrl(topLayer); if (match) { foundLayer = match; break; } }
if (foundLayer) { foundLayer.visible = true; if ("definitionExpression" in foundLayer) { foundLayer.definitionExpression = defExp; } if (utilityGroup) utilityGroup.visible = true; return; }
// 6) If not found → add as new FeatureLayer const fl = new FeatureLayer({ url: serviceUrl, title: `${selectedUtilityLayer} (temp)`, visible: true, definitionExpression: defExp }); await fl.load(); map.layers.add(fl); addedUtilityLayerRef.current = fl; if (utilityGroup) utilityGroup.visible = true;};
// Populate Zone dropdown const populateZone = async () => { try { const layer = new FeatureLayer({ url: ZONE_LAYER_URL }); const query = layer.createQuery(); query.returnDistinctValues = true; query.returnGeometry = false; query.outFields = ["zone"]; query.where = "1=1"; const result = await layer.queryFeatures(query); if (result) { const zones = Array.from(new Set(result.features.map(f => f.attributes.zone).filter(Boolean))); zones.sort((a, b) => Number(a) - Number(b)); setZoneOptions(['All', ...zones]); } } catch (error) { console.error("Error fetching zones:", error); } }; useEffect(() => { populateZone(); }, []);
// Populate Ward dropdown based on selected Zone const populateWard = async () => { try { const layer = new FeatureLayer({ url: WARD_LAYER_URL }); const query = layer.createQuery(); query.returnDistinctValues = true; query.returnGeometry = false; query.outFields = ["ward_no"]; if (selectedZone && selectedZone !== 'All') { query.where = `zone_no = '${safe(selectedZone)}'`; } else { query.where = "1=1"; } const result = await layer.queryFeatures(query); if (result) { const wards = Array.from(new Set(result.features.map(f => f.attributes.ward_no).filter(Boolean))); wards.sort((a, b) => Number(a) - Number(b)); setWardOptions(['All', ...wards]); setSelectedWard(""); // Reset ward when zone changes setActiveStep(1); // Move to next step } } catch (error) { console.error("Error fetching wards:", error); } }; useEffect(() => { if (selectedZone) { populateWard(); } }, [selectedZone]);
// Populate utility layer options useEffect(() => { if (selectedLayer === "Utility") { const titles = utilityLayers.map(l => l.title); setUtilityLayerOptions(["All", ...titles]); setSelectedUtilityLayer("All"); setActiveStep(2); // Move to next step } }, [selectedLayer]);
// Populate road names for selected utility layer const populateRoadNames = async (utilityLayer: string) => { if (!selectedZone || !selectedWard || !utilityLayer || utilityLayer === "All") return; const layerInfo = utilityLayers.find(l => l.title === utilityLayer); if (!layerInfo) return;
const layerUrl = `${UTILITY_GROUP_URL}/${layerInfo.id}`; const layer = new FeatureLayer({ url: layerUrl });
try { const query = layer.createQuery(); query.returnDistinctValues = true; query.outFields = ["road_name"]; query.returnGeometry = false; let whereClause = ""; if (selectedZone && selectedZone !== 'All') { whereClause += `zone_number = '${safe(selectedZone)}'`; } if (selectedWard && selectedWard !== 'All') { whereClause += whereClause ? ` AND ward_number = '${safe(selectedWard)}'` : `ward_number = '${safe(selectedWard)}'`; } query.where = whereClause || "1=1"; const result = await layer.queryFeatures(query); const names = Array.from( new Set(result.features.map(f => f.attributes.road_name).filter(Boolean)) ); setRoadNameOptions(names.sort()); setActiveStep(3); // Move to next step } catch (err) { console.error("Failed to fetch road names:", err); setRoadNameOptions([]); } };
// Build where clause for queries const buildWhereClause = () => { const clauses: string[] = [];
if (selectedZone && selectedZone !== 'All') { clauses.push(`zone_number = '${safe(selectedZone)}'`); }
if (selectedWard && selectedWard !== 'All') { clauses.push(`ward_number = '${safe(selectedWard)}'`); }
if (selectedRoadName) { clauses.push(`road_name = '${safe(selectedRoadName)}'`); }
return clauses.length > 0 ? clauses.join(" AND ") : "1=1"; };
// Zoom to zone geometry const zoomToZoneGeometry = async (zoneValue: string) => { if (!jimuMapView || !zoneValue || zoneValue === 'All') return; const view = jimuMapView.view; const zoneLayer = new FeatureLayer({ url: ZONE_LAYER_URL }); try { await zoneLayer.load(); const query = zoneLayer.createQuery(); query.where = `zone = '${safe(zoneValue)}'`; query.outFields = ["*"]; query.returnGeometry = true; const result = await zoneLayer.queryFeatures(query); if (result.features.length > 0) { const geometry = result.features[0].geometry; await view.goTo({ target: geometry, padding: { top: 50, bottom: 50, left: 50, right: 50 }, duration: 1000 }); } } catch (error) { console.error("Error zooming to zone:", error); } };
// Zoom to ward geometry const zoomToWardGeometry = async (wardValue: string) => { if (!jimuMapView || !wardValue || wardValue === 'All') return; const view = jimuMapView.view; const wardLayer = new FeatureLayer({ url: WARD_LAYER_URL }); try { await wardLayer.load(); const query = wardLayer.createQuery(); query.where = `ward_no = '${safe(wardValue)}'`; query.outFields = ["*"]; query.returnGeometry = true; const result = await wardLayer.queryFeatures(query); if (result.features.length > 0) { const geometry = result.features[0].geometry; await view.goTo({ target: geometry, padding: { top: 50, bottom: 50, left: 50, right: 50 }, duration: 1000 }); } } catch (error) { console.error("Error zooming to ward:", error); } };
// Get feature count for utility layer const getFeatureCount = async (layerUrl: string): Promise<number> => { const layer = new FeatureLayer({ url: layerUrl }); const query = layer.createQuery(); query.where = buildWhereClause(); query.returnGeometry = false; try { return await layer.queryFeatureCount(query); } catch (error) { console.error("Error counting features:", error); return 0; } };
// Update utility count when filters change useEffect(() => { const fetchCount = async () => { if (selectedUtilityLayer && selectedUtilityLayer !== "All") { const layerInfo = utilityLayers.find(l => l.title === selectedUtilityLayer); if (layerInfo) { const count = await getFeatureCount(`${UTILITY_GROUP_URL}/${layerInfo.id}`); setUtilityCount(count); } } else { setUtilityCount(null); } };
if (selectedLayer === "Utility") { fetchCount(); } }, [selectedZone, selectedWard, selectedUtilityLayer, selectedRoadName]);
// Function to update ward boundary visibility const updateWardBoundaryVisibility = async (wardValue: string) => { if (!jimuMapView?.view) return; const map = jimuMapView.view.map; try { // Find the ward boundary layer in the map const wardLayer = map.layers.find(layer => layer.url === WARD_LAYER_URL ) as FeatureLayer; if (wardLayer) { if (wardValue && wardValue !== "All") { // Show only the selected ward wardLayer.definitionExpression = `ward_no = '${safe(wardValue)}'`; wardLayer.visible = true; } else { // Show all wards when "All" is selected wardLayer.definitionExpression = "1=1"; wardLayer.visible = true; } } } catch (error) { console.error("Error updating ward boundary visibility:", error); } };const applyFilters = async () => { if (!jimuMapView?.view) return; const view = jimuMapView.view; const map = view.map;
const defExp = buildWhereClause() || "1=1"; const selected = (selectedUtilityLayer ?? "").toString().trim().toLowerCase();
// --- Keep Road group visible & filtered --- const roadGroup = map.layers.find(l => l.title === "Transporations") as __esri.GroupLayer; if (roadGroup && "layers" in roadGroup) { roadGroup.visible = true; roadGroup.layers.forEach((sub: any) => { if (["Road", "CarraigeWay"].includes(sub.title) && "definitionExpression" in sub) { sub.definitionExpression = defExp.replace(/ward_number/gi, "ward"); } sub.visible = true; }); }
// --- Keep Asset group visible & filtered --- const assetGroup = map.layers.find(l => l.title === "GCC Asset Layers") as __esri.GroupLayer; if (assetGroup && "layers" in assetGroup) { assetGroup.visible = true; assetGroup.layers.forEach((sub: any) => { if (["Asset Point", "Asset Building"].includes(sub.title) && "definitionExpression" in sub) { sub.definitionExpression = defExp.replace(/ward_number/gi, "ward_no"); } sub.visible = true; }); }
// --- Handle Utility layers --- const utilityGroup = map.layers.find( (l: any) => (l.title ?? "").toString().toLowerCase().includes("utility") ) as __esri.GroupLayer;
if (!utilityGroup || !("layers" in utilityGroup)) return;
utilityGroup.visible = true; const children = utilityGroup.layers.toArray();
for (const child of children) { const layerTitle = (child.title ?? "").toLowerCase(); const layerInfo = utilityLayers.find(u => u.title.toLowerCase() === layerTitle);
if (!layerInfo) continue;
if (selected === "all" || layerTitle === selected) { child.visible = true; if (typeof child.definitionExpression !== "undefined") { child.definitionExpression = defExp; // apply filters } } else { child.visible = false; // hide all other utility layers } }};// --- Toggle Utility layer visibility (no filter change) ---// const toggleUtilityLayerVisibility = (// map: __esri.Map,// selectedUtilityLayer: string// ) => {// const utilityGroup = map.layers.find(// (l: any) => (l.title ?? "").toString().toLowerCase().includes("utility")// ) as __esri.GroupLayer;
// if (!utilityGroup || !("layers" in utilityGroup)) return;
// utilityGroup.visible = true;// const children = utilityGroup.layers.toArray();
// for (const child of children) {// const layerTitle = (child.title ?? "").toLowerCase();
// // Case 1: "all" → show all utility layers// if (selectedUtilityLayer === "all") {// child.visible = true;// }// // Case 2: specific layer selected → only show that one// else if (layerTitle === selectedUtilityLayer) {// child.visible = true;// }// // Hide everything else// else {// child.visible = false;// }// }// };
// const toggleUtilityLayerVisibility = (// map: __esri.Map,// selectedUtilityLayer: string// ) => {// // find the right group layer (adjust title match if needed)// const utilityGroup = map.layers.find(// (l: any) =>// (l.title ?? "").toString().trim().toLowerCase() === "utility" || // (l.title ?? "").toString().toLowerCase().includes("utility")// ) as __esri.GroupLayer;
// if (!utilityGroup || !("layers" in utilityGroup)) {// console.warn("Utility group not found");// return;// }
// utilityGroup.visible = true;// const children = utilityGroup.layers.toArray();
// children.forEach(child => {// const layerTitle = (child.title ?? "").toLowerCase();
// if (selectedUtilityLayer === "all") {// child.visible = true;// } else if (layerTitle === selectedUtilityLayer.toLowerCase()) {// child.visible = true;// } else {// child.visible = false;// }// });// };// const toggleUtilityLayerVisibility = (// map: __esri.Map,// selectedUtilityLayer: string// ) => {// const utilityGroup = map.layers.find(// (l: any) =>// (l.title ?? "").toString().trim().toLowerCase() === "utility" ||// (l.title ?? "").toString().toLowerCase().includes("utility")// ) as __esri.GroupLayer;
// if (!utilityGroup || !("layers" in utilityGroup)) {// console.warn("Utility group not found");// return;// }
// utilityGroup.visible = true;// const children = utilityGroup.layers.toArray();
// children.forEach(child => {// const layerTitle = (child.title ?? "").toLowerCase();// console.log("Comparing:", layerTitle, "with", selectedUtilityLayer.toLowerCase());
// if (selectedUtilityLayer === "all") {// child.visible = true;// } else if (layerTitle === selectedUtilityLayer.toLowerCase()) {// child.visible = true;// } else {// // don't touch visibility if not matching// console.log("Skipping:", layerTitle);// }// });// };// const toggleUtilityLayerVisibility = (// map: __esri.Map,// selectedUtilityLayer: string// ) => {// const utilityGroup = map.layers.find(// (l: any) =>// (l.title ?? "").toString().trim().toLowerCase() === "utility" ||// (l.title ?? "").toLowerCase().includes("utility")// ) as __esri.GroupLayer;
// if (!utilityGroup || !("layers" in utilityGroup)) {// console.warn("Utility group not found");// return;// }
// utilityGroup.visible = true;
// // Recursive function to check children (FeatureLayers or GroupLayers)// const toggleChildren = (layers: __esri.Collection<__esri.Layer>) => {// layers.forEach((child: any) => {// const childTitle = (child.title ?? "").toLowerCase();
// if ("layers" in child) {// // child is a subgroup → recurse// toggleChildren(child.layers);// } else {// if (selectedUtilityLayer === "all") {// child.visible = true;// console.log("Showing:", child.title);// } else if (childTitle === selectedUtilityLayer.toLowerCase()) {// child.visible = true;// console.log("Matched:", child.title);// } else {// child.visible = false;// console.log("Hiding:", child.title);// }// }// });// };
// toggleChildren(utilityGroup.layers);// };// const toggleUtilityLayerVisibility = (// map: __esri.Map,// selectedUtilityLayer: string// ) => {// const utilityGroup = map.layers.find(// (l: any) =>// (l.title ?? "").toString().trim().toLowerCase() === "utility" ||// (l.title ?? "").toLowerCase().includes("utility")// ) as __esri.GroupLayer;
// if (!utilityGroup || !("layers" in utilityGroup)) {// console.warn("Utility group not found");// return;// }
// // keep the utility group itself visible// utilityGroup.visible = true;
// // 🚨 Do NOT touch other top-level layers like Road, Asset, etc.// // Only change children inside Utility group// const toggleChildren = (layers: __esri.Collection<__esri.Layer>) => {// layers.forEach((child: any) => {// const childTitle = (child.title ?? "").trim().toLowerCase();
// if ("layers" in child) {// // recurse if subgroup// toggleChildren(child.layers);// } else {// if (selectedUtilityLayer === "all") {// child.visible = true; // show all utility sublayers// } else if (childTitle === selectedUtilityLayer.toLowerCase()) {// child.visible = true; // show only selected// } else {// child.visible = false; // hide other utility sublayers// }// }// });// };
// toggleChildren(utilityGroup.layers);// };const toggleUtilityLayerVisibility = ( map: __esri.Map, selectedUtilityLayer: string) => { const utilityGroup = map.layers.find( (l: any) => (l.title ?? "").toString().trim().toLowerCase() === "utility" || (l.title ?? "").toLowerCase().includes("utility") ) as __esri.GroupLayer;
if (!utilityGroup || !("layers" in utilityGroup)) { console.warn("Utility group not found"); return; }
utilityGroup.visible = true;
// names of layers we DON'T want to affect const exclude = ["road", "asset"];
const toggleChildren = (layers: __esri.Collection<__esri.Layer>) => { layers.forEach((child: any) => { const childTitle = (child.title ?? "").trim().toLowerCase();
if ("layers" in child) { toggleChildren(child.layers); } else { if (exclude.includes(childTitle)) { // 🚫 skip road/asset etc. child.visible = true; return; }
if (selectedUtilityLayer === "all") { child.visible = true; } else if (childTitle === selectedUtilityLayer.toLowerCase()) { child.visible = true; } else { child.visible = false; } } }); };
toggleChildren(utilityGroup.layers);};
//Apply filters to utility layers// const applyFilters = async () => {// if (!jimuMapView?.view) return;// const view = jimuMapView.view;// const map = view.map;// const defExp = buildWhereClause() || "1=1";// const selected = (selectedUtilityLayer ?? "").toString().trim();
// // Keep Road & Asset groups visible & filtered// const roadLayerTitles = ["Road", "CarraigeWay"];// const assetLayerTitles = ["Asset Point", "Asset Building"];
// // --- Road group ---// const roadGroup = map.layers.find(l => l.title === "Transporations") as __esri.GroupLayer;// if (roadGroup && "layers" in roadGroup) {// roadGroup.visible = true;// roadGroup.layers.forEach((sub: any) => {// if (roadLayerTitles.includes(sub.title) && "definitionExpression" in sub) {// // Road schema field is `ward`// sub.definitionExpression = defExp.replace(/ward_number/gi, "ward");// }// sub.visible = true;// });// }
// // --- Asset group ---// const assetGroup = map.layers.find(l => l.title === "GCC Asset Layers") as __esri.GroupLayer;// if (assetGroup && "layers" in assetGroup) {// assetGroup.visible = true;// assetGroup.layers.forEach((sub: any) => {// if (assetLayerTitles.includes(sub.title) && "definitionExpression" in sub) {// // Asset schema field is `ward_no`// sub.definitionExpression = defExp.replace(/ward_number/gi, "ward_no");// }// sub.visible = true;// });// }
// const safeTitle = (t?: string | null) => (t ?? "").toString().trim();// const lower = (s?: string) => safeTitle(s).toLowerCase();
// try {// // Find Utility group (do NOT hide it by default) and ensure it's visible// const utilityGroup = map.layers.find(// (l: any) => safeTitle(l.title).toLowerCase().includes("utility")// );// if (utilityGroup) utilityGroup.visible = true;
// // Remove any previously added temporary FeatureLayer (we'll add a new one if needed)// if (addedUtilityLayerRef.current) {// try {// map.layers.remove(addedUtilityLayerRef.current);// } catch (e) { /* ignore */ }// addedUtilityLayerRef.current = null;// }
// // If "All" -> show all relevant children and apply defExp where possible// if (selected === "" || lower(selected) === "all") {// if (utilityGroup && "layers" in utilityGroup && typeof utilityGroup.layers?.toArray === "function") {// const children = utilityGroup.layers.toArray();// for (const child of children) {// try {// if ("definitionExpression" in child) {// try { child.definitionExpression = defExp; } catch (e) { /* ignore */ }// }// child.visible = true;// } catch (e) { console.warn("Error enabling child for All", child, e); }// }// utilityGroup.visible = true;// }// return;// }
// // 3) Find the utilityLayers entry and service URL for the selected utility// const layerInfo = utilityLayers.find(u => lower(u.title) === lower(selected));// if (!layerInfo) {// console.warn("Selected utility not found in utilityLayers config:", selected);// return;// }// const serviceUrl = `${UTILITY_GROUP_URL}/${layerInfo.id}`;
// // 4) Try to find an existing layer in the map whose .url matches the service url// let foundLayer: any = null;
// const walkAndFindByUrl = (top: any) => {// if (top?.url && top.url === serviceUrl) return top;// if ("layers" in top && typeof top.layers?.toArray === "function") {// for (const c of top.layers.toArray()) {// if (c?.url && c.url === serviceUrl) return c;// }// }// if ("sublayers" in top && typeof top.sublayers?.toArray === "function") {// for (const s of top.sublayers.toArray()) {// if (s?.url && s.url === serviceUrl) return s;// }// }// return null;// };
// for (const topLayer of map.layers.toArray()) {// try {// const match = walkAndFindByUrl(topLayer);// if (match) { foundLayer = match; break; }// } catch (e) { /* ignore per-layer errors */ }// }
// // 5) If found in map, show and apply definitionExpression (best-case)// // if (foundLayer) {// // try {// // // Make sure road & asset groups remain visible (don't hide them)// // foundLayer.visible = true;// // if ("definitionExpression" in foundLayer) {// // try { foundLayer.definitionExpression = defExp; } catch (e) { /* ignore */ }// // }// // if (utilityGroup) utilityGroup.visible = true;
// // // Hide other utility children (optional) but keep ROAD/ASSET visible. We'll hide other utilities so user sees selected one clearly.// // if (utilityGroup && "layers" in utilityGroup && typeof utilityGroup.layers?.toArray === "function") {// // const children = utilityGroup.layers.toArray();// // for (const child of children) {// // try {// // if (child?.url && child.url === serviceUrl) {// // child.visible = true;// // if ("definitionExpression" in child) child.definitionExpression = defExp;// // } else {// // // hide other utility types (still keep groups for Road/Asset untouched)// // try { child.visible = false; } catch (e) { /* ignore */ }// // }// // } catch (e) { /* ignore */ }// // }// // }// // return;// // } catch (err) {// // console.warn("Error enabling existing layer:", err);// // }// // }// if (foundLayer) {// try {// foundLayer.visible = true;// const where = buildWhereClause(); // uses ward_number by design// // apply to the found layer// try { if (typeof foundLayer.definitionExpression !== 'undefined') foundLayer.definitionExpression = where; } catch(e){}// // if it has sublayers (MapImageLayer), apply to sublayers too// if (foundLayer.sublayers && typeof foundLayer.sublayers.toArray === 'function') {// foundLayer.sublayers.toArray().forEach((sl: any) => {// try { if (typeof sl.definitionExpression !== 'undefined') sl.definitionExpression = where; } catch(e){}// });// }// if (utilityGroup) utilityGroup.visible = true;
// // Optionally hide other utility children so the selected one stands out (comment/uncomment)// if (utilityGroup && "layers" in utilityGroup && typeof utilityGroup.layers?.toArray === "function") {// const children = utilityGroup.layers.toArray();// for (const child of children) {// try {// if (child?.url && child.url === serviceUrl) {// child.visible = true;// if (typeof child.definitionExpression !== 'undefined') child.definitionExpression = where;// } else {// // hide other utility children (if you want)// try { child.visible = false; } catch(e) {}// }// } catch (e) { /* ignore */ }// }// }// return;// } catch (err) {// console.warn("Error enabling existing layer:", err);// }// }
// // 6) If no matching layer found in map, add a new client FeatureLayer that points to the service endpoint.// try {// // const fl = new FeatureLayer({// // url: serviceUrl,// // title: `${selected} (temp)`,// // visible: true,// // definitionExpression: defExp// // });// // await fl.load();// // map.layers.add(fl); // add top-level// // addedUtilityLayerRef.current = fl;// const fl = new FeatureLayer({// url: serviceUrl,// title: `${selected} (temp)`,// visible: true// });// await fl.load();// // apply the same ward-level where clause (buildWhereClause uses ward_number)// try { fl.definitionExpression = buildWhereClause(); } catch(e) {}// map.layers.add(fl);// addedUtilityLayerRef.current = fl;// if (utilityGroup) utilityGroup.visible = true;// return;// } catch (err) {// console.error("Failed to add FeatureLayer for selected utility:", err);// }
// } catch (err) {// console.error("applyFilters error:", err);// }// };
// Replace your existing applyWardFilterToLayers with this implementation// const applyWardFilterToLayers = async (wardValue: string, viewMap: __esri.Map) => {// if (!viewMap) return;
// // Reset: show everything and clear any dynamically added utility layer// if (!wardValue || wardValue === "All") {// const topLayers: any[] = (viewMap.layers && typeof (viewMap.layers as any).toArray === "function")// ? (viewMap.layers as any).toArray()// : Array.from((viewMap.layers as any) || []);
// topLayers.forEach((layer: any) => {// try {// // Group layers with children// if (layer.layers && typeof layer.layers.toArray === 'function') {// const children = layer.layers.toArray();// children.forEach((child: any) => {// try {// if (typeof child.definitionExpression !== 'undefined') {// child.definitionExpression = "1=1";// } else if (child.sublayers && typeof child.sublayers.toArray === 'function') {// child.sublayers.toArray().forEach((sl: any) => {// try { if (typeof sl.definitionExpression !== 'undefined') sl.definitionExpression = "1=1"; } catch(e) {}// });// }// try { child.visible = true; } catch(e) {}// } catch(e) {}// });// return;// }
// // Top-level feature layers// try { if (typeof (layer as any).definitionExpression !== 'undefined') (layer as any).definitionExpression = "1=1"; } catch(e) {}// try { layer.visible = true; } catch(e) {}// } catch(e) {}// });
// // Remove any temporary utility layer we added before// if (addedUtilityLayerRef.current) {// try { viewMap.layers.remove(addedUtilityLayerRef.current); } catch(e) {}// addedUtilityLayerRef.current = null;// }// return;// }
// // Specific ward -> apply ward filter to each group/sub-layer.// const wardFilter = `ward_number = '${safe(wardValue)}'`;
// const topLayers: any[] = (viewMap.layers && typeof (viewMap.layers as any).toArray === "function")// ? (viewMap.layers as any).toArray()// : Array.from((viewMap.layers as any) || []);
// for (const layer of topLayers) {// try {// // Transportation group: filter by ward (field name is ward)// if ((layer.title || '').toString().toLowerCase().includes('transpor') && layer.layers && typeof layer.layers.toArray === 'function') {// layer.visible = true;// layer.layers.toArray().forEach((sub: any) => {// try {// if (typeof sub.definitionExpression !== 'undefined') {// // Road layer uses 'ward' field in your schema// sub.definitionExpression = wardFilter.replace(/ward_number/gi, 'ward');// }// sub.visible = true;// } catch (e) {}// });// continue;// }
// // Asset group: filter by ward_no (if required)// if ((layer.title || '').toString().toLowerCase().includes('asset') && layer.layers && typeof layer.layers.toArray === 'function') {// layer.visible = true;// layer.layers.toArray().forEach((sub: any) => {// try {// if (typeof sub.definitionExpression !== 'undefined') {// sub.definitionExpression = wardFilter.replace(/ward_number/gi, 'ward_no');// }// sub.visible = true;// } catch (e) {}// });// continue;// }
// // Utility group: apply ward_number filter to every child (they all use ward_number)// if ((layer.title || '').toString().toLowerCase().includes('utility')) {// layer.visible = true;// // If the utility layer is a GroupLayer with .layers// if (layer.layers && typeof layer.layers.toArray === 'function') {// const children = layer.layers.toArray();// for (const child of children) {// try {// if (typeof child.definitionExpression !== 'undefined') {// // APPLY ward_number directly (you said all utility layers have ward_number)// child.definitionExpression = wardFilter;// } else if (child.sublayers && typeof child.sublayers.toArray === 'function') {// // MapImageLayer sublayers// child.sublayers.toArray().forEach((sl: any) => {// try { if (typeof sl.definitionExpression !== 'undefined') sl.definitionExpression = wardFilter; } catch(e) {}// });// } else {// // fallback: just make sure child is visible// try { child.visible = true; } catch(e) {}// }// } catch (e) {// console.warn('applyWardFilterToLayers: error applying to utility child', e);// }// }// } else {// // If the utility layer itself is a FeatureLayer// try { if (typeof (layer as any).definitionExpression !== 'undefined') (layer as any).definitionExpression = wardFilter; } catch(e) {}// try { layer.visible = true; } catch(e) {}// }// }// } catch (err) {// console.warn('applyWardFilterToLayers: loop error', err);// }// } // end for// };const applyWardFilterToLayers = async (wardValue: string, viewMap: __esri.Map) => { if (!viewMap) return;
// Helper to safely apply ward filter recursively const applyWardToLayer = (layer: any, filter: string) => { try { // Skip ZoneBoundary and WardBoundary layers const title = (layer.title || '').toLowerCase(); if (title.includes('zoneboundary') || title.includes('wardboundary')) { layer.visible = true; return; }
// FeatureLayer if (layer.type === 'feature' && typeof layer.definitionExpression !== 'undefined') { layer.definitionExpression = filter; layer.visible = true; } // MapImageLayer or GroupLayer with sublayers else if (layer.sublayers && typeof layer.sublayers.toArray === 'function') { layer.sublayers.toArray().forEach((sl: any) => applyWardToLayer(sl, filter)); } else if (layer.layers && typeof layer.layers.toArray === 'function') { layer.layers.toArray().forEach((child: any) => applyWardToLayer(child, filter)); } else { layer.visible = true; // fallback } } catch (e) { console.warn('applyWardToLayer error', e); } };
// Reset all layers if "All" is selected if (!wardValue || wardValue === "All") { const topLayers: any[] = (viewMap.layers && typeof (viewMap.layers as any).toArray === "function") ? (viewMap.layers as any).toArray() : Array.from((viewMap.layers as any) || []);
topLayers.forEach((layer: any) => applyWardToLayer(layer, "1=1"));
if (addedUtilityLayerRef.current) { try { viewMap.layers.remove(addedUtilityLayerRef.current); } catch(e) {} addedUtilityLayerRef.current = null; } return; }
// Specific ward filter const wardFilter = `ward_number = '${safe(wardValue)}'`;
const topLayers: any[] = (viewMap.layers && typeof (viewMap.layers as any).toArray === "function") ? (viewMap.layers as any).toArray() : Array.from((viewMap.layers as any) || []);
for (const layer of topLayers) { try { const title = (layer.title || '').toLowerCase();
// Skip zone/ward boundary layers if (title.includes('zoneboundary') || title.includes('wardboundary')) continue;
// Transportation group: field is 'ward' if (title.includes('transpor')) { layer.visible = true; if (layer.layers && typeof layer.layers.toArray === 'function') { layer.layers.toArray().forEach((sub: any) => { applyWardToLayer(sub, wardFilter.replace(/ward_number/gi, 'ward')); }); } continue; }
// Asset group: field is 'ward_no' if (title.includes('asset')) { layer.visible = true; if (layer.layers && typeof layer.layers.toArray === 'function') { layer.layers.toArray().forEach((sub: any) => { applyWardToLayer(sub, wardFilter.replace(/ward_number/gi, 'ward_no')); }); } continue; }
// Utility group: all use 'ward_number' if (title.includes('utility')) { layer.visible = true; applyWardToLayer(layer, wardFilter); continue; }
// Other top-level layers: fallback applyWardToLayer(layer, wardFilter); } catch (err) { console.warn('applyWardFilterToLayers loop error', err); } }};
// const applyWardFilterToLayers = async (wardValue: string, viewMap: __esri.Map) => { // if (!wardValue || wardValue === "All") { // // Reset → show all features in all layers // viewMap.layers.forEach(layer => { // if ("layers" in layer) { // layer.layers.forEach((sub: any) => { // if ("definitionExpression" in sub) { // sub.definitionExpression = "1=1"; // show all features // } // sub.visible = true; // keep all visible // }); // } else if ((layer as any).type === 'feature') { // try { (layer as any).definitionExpression = "1=1"; } catch(e){} // layer.visible = true; // } // });
// // Also reset any dynamically added utility layer // if (addedUtilityLayerRef.current) { // try { // viewMap.layers.remove(addedUtilityLayerRef.current); // } catch (e) { /* ignore */ } // addedUtilityLayerRef.current = null; // } // return; // }
// const roadLayerTitles = ["Road", "CarraigeWay"]; // const assetLayerTitles = ["Asset Point", "Asset Building"];
// // 🔹 Transportation group (filter by `ward`) // const roadGroup = viewMap.layers.find(l => l.title === "Transporations") as __esri.GroupLayer; // if (roadGroup) { // roadGroup.visible = true; // roadGroup.layers.forEach((sub: any) => { // if (roadLayerTitles.includes(sub.title) && "definitionExpression" in sub) { // sub.definitionExpression = `ward = '${wardValue}'`; // ✅ correct field // } // sub.visible = true; // }); // }
// // 🔹 Asset group (filter by `ward_no`) // const assetGroup = viewMap.layers.find(l => l.title === "GCC Asset Layers") as __esri.GroupLayer; // if (assetGroup) { // assetGroup.visible = true; // assetGroup.layers.forEach((sub: any) => { // if (assetLayerTitles.includes(sub.title) && "definitionExpression" in sub) { // sub.definitionExpression = `ward_no = '${wardValue}'`; // ✅ correct field // } // sub.visible = true; // }); // }
// // 🔹 Utility group (filter utilities by ward_number) — ensure we DON'T hide the group // const utilityGroup = viewMap.layers.find((l: any) => (l.title ?? '').toString().toLowerCase().includes('utility')) as __esri.GroupLayer; // if (utilityGroup && "layers" in utilityGroup && typeof utilityGroup.layers?.toArray === 'function') { // utilityGroup.visible = true; // const children = utilityGroup.layers.toArray(); // for (const child of children) { // try { // if ("definitionExpression" in child) { // // apply ward filter on utility children using ward_number // child.definitionExpression = `ward_number = '${safe(wardValue)}'`; // } // // Keep utilities visible so user can see them filtered by ward // child.visible = true; // } catch (e) { // console.warn('Error applying ward filter to utility child', child, e); // } // } // } // };
useEffect(() => { if (jimuMapView?.view && selectedWard) { applyWardFilterToLayers(selectedWard, jimuMapView.view.map); updateWardBoundaryVisibility(selectedWard); // Add this line to update ward boundary visibility } }, [selectedWard, jimuMapView]);
// Apply filters when utility-related filters change useEffect(() => { if (jimuMapView && selectedLayer === "Utility") { applyFilters(); } }, [selectedZone, selectedWard, selectedUtilityLayer, selectedRoadName, jimuMapView]);
const handleSubmit = async (e: React.MouseEvent) => { e.preventDefault(); setIsLoading(true);
if (!selectedZone) { alert("Please select a Zone"); setIsLoading(false); return; }
if (!jimuMapView?.view) { setIsLoading(false); return; }
try { // 1. Apply filters first await applyFilters();
let zoomed = false;
// 2. Query features only when a single utility is selected if (selectedLayer === "Utility" && selectedUtilityLayer && selectedUtilityLayer !== "All") { const layerInfo = utilityLayers.find(l => l.title === selectedUtilityLayer); if (layerInfo) { const layer = new FeatureLayer({ url: `${UTILITY_GROUP_URL}/${layerInfo.id}` }); const query = layer.createQuery(); query.where = buildWhereClause(); query.outFields = ["*"]; query.returnGeometry = true;
const result = await layer.queryFeatures(query); setTableData(result.features);
// Zoom to filtered features if any if (result.features.length > 0) { const extent = result.features.reduce( (acc, f) => acc ? acc.union(f.geometry.extent) : f.geometry.extent, null as __esri.Extent | null ); if (extent) { await jimuMapView.view.goTo({ target: extent, padding: 100, duration: 1000 }); zoomed = true; } } } } else if (selectedLayer === "Utility" && selectedUtilityLayer === "All") { // Clear table when all utilities are shown setTableData([]); }
// 3. If nothing was zoomed yet, zoom to ward/zone if (!zoomed) { if (selectedWard && selectedWard !== "All") { await zoomToWardGeometry(selectedWard); } else if (selectedZone && selectedZone !== "All") { await zoomToZoneGeometry(selectedZone); } }
} catch (error) { console.error("Error in handleSubmit:", error); } finally { setIsLoading(false); } };
// Zoom to individual feature with highlight const zoomToFeature = async (feature: any) => { if (!jimuMapView?.view || !feature?.geometry) return; const view = jimuMapView.view; try { // Clear previous highlight if (highlightRef.current) { highlightRef.current.remove(); highlightRef.current = null; } // Clear any existing graphics if (graphicsLayerRef.current) { view.graphics.remove(graphicsLayerRef.current); }
// Create highlight symbol based on feature type let highlightSymbol; if (feature.geometry.type === "point") { highlightSymbol = { type: "simple-marker", color: [255, 0, 0, 0.8], size: 12, outline: { color: [255, 255, 255], width: 2 } }; } else if (feature.geometry.type === "polyline") { highlightSymbol = new SimpleLineSymbol({ color: [255, 0, 0, 0.8], width: 4 }); } else { highlightSymbol = new SimpleFillSymbol({ color: [255, 0, 0, 0.3], outline: { color: [255, 0, 0], width: 2 } }); }
// Create highlight graphic const highlightGraphic = new Graphic({ geometry: feature.geometry, symbol: highlightSymbol });
// Add to map and store reference view.graphics.add(highlightGraphic); graphicsLayerRef.current = highlightGraphic;
// Zoom to feature await view.goTo({ target: feature.geometry, zoom: feature.geometry.type === "point" ? 18 : 14, duration: 1000 }); } catch (error) { console.error("Error zooming to feature:", error); } };
// Reset button handler const handleReset = () => { setSelectedZone(""); setSelectedWard(""); setSelectedLayer(""); setSelectedUtilityLayer("All"); setSelectedRoadName(""); setUtilityCount(null); setTableData([]); setRoadNameOptions([]); setActiveStep(0);
// Clear graphics & highlights jimuMapView?.view?.graphics.removeAll(); highlightRef.current?.remove(); highlightRef.current = null;
if (jimuMapView?.view) { const map = jimuMapView.view.map;
// Reset all utility layers and other groups map.layers.forEach(layer => { if (layer.type === "feature") { try { (layer as __esri.FeatureLayer).definitionExpression = "1=1"; } catch(e){} layer.visible = true; } else if ("layers" in layer) { layer.layers.forEach((sub: any) => { sub.visible = true; if (sub.type === "feature") try { sub.definitionExpression = "1=1"; } catch(e){} }); } });
// Remove dynamically added utility layer if (addedUtilityLayerRef.current) { try { map.layers.remove(addedUtilityLayerRef.current); } catch(e){} addedUtilityLayerRef.current = null; }
// Restore GCC Asset Layers (and its subs) const assetGroup = map.layers.find(l => l.title === "GCC Asset Layers"); if (assetGroup && "layers" in assetGroup) { assetGroup.visible = true; assetGroup.layers.forEach((sub: any) => sub.visible = true); }
// Reset ward boundary layer to show all wards const wardLayer = map.layers.find(layer => layer.url === WARD_LAYER_URL ) as FeatureLayer; if (wardLayer) { wardLayer.definitionExpression = "1=1"; wardLayer.visible = true; } } };
// Active view change handler const activeViewChangeHandler = (jmv: JimuMapView) => { if (jmv) setJimuMapView(jmv); };
// Initialize map view useEffect(() => { const interval = setInterval(() => { const view = mapViewManager.getMapView(); if (view) { setJimuMapView(view); clearInterval(interval); } }, 500); return () => clearInterval(interval); }, []);
return ( <div className="property-widget-parent"> <div className="property-widget"> <JimuMapViewComponent useMapWidgetId={props.useMapWidgetIds?.[0]} onActiveViewChange={activeViewChangeHandler} />
<h3 className="widget-title">Utility Asset Filter</h3> {/* Zone selection */} <div className="property_form"> <label>Zone:<span style={{ color: 'red' }}>*</span></label> <select value={selectedZone} onChange={e => { const value = e.target.value; setSelectedZone(value); setSelectedWard(""); setSelectedRoadName(""); setRoadNameOptions([]); if (value && value !== "All") { zoomToZoneGeometry(value); } }} required > <option value="">--Select Zone--</option> {zoneOptions.map(zone => ( <option key={zone} value={zone}>{zone}</option> ))} </select> </div>
{/* Ward selection */}<div className="property_form"> <label>Ward:</label> <select value={selectedWard} onChange={e => { const value = e.target.value; setSelectedWard(value); setSelectedRoadName(""); setRoadNameOptions([]);
if (value && value !== "All") { zoomToWardGeometry(value); if (jimuMapView?.view) { applyWardFilterToLayers(value, jimuMapView.view.map); updateWardBoundaryVisibility(value); // ✅ finish call here } } else { // Reset when "All" is selected if (jimuMapView?.view) { applyWardFilterToLayers("All", jimuMapView.view.map); updateWardBoundaryVisibility("All"); // ✅ reset visibility } } }} > <option value="">--Select Ward--</option> {wardOptions.map(ward => ( <option key={ward} value={ward}>{ward}</option> ))} </select></div>
{/* Layer selection */} <div className="property_form"> <label>Select Layer:</label> <select value={selectedLayer} onChange={e => { const layer = e.target.value; setSelectedLayer(layer); setSelectedUtilityLayer("All"); setSelectedRoadName(""); setRoadNameOptions([]); setUtilityCount(null); }} > <option value="">--Select Layer--</option> <option value="Utility">Utility</option> </select> </div>
{/* Show filtering options when Utility is selected */} {selectedLayer === "Utility" && ( <> {/* Utility Layer selection */} <div className="property_form"> <label>Select Utility Type:</label> <select value={selectedUtilityLayer} onChange={e => { const value = e.target.value; setSelectedUtilityLayer(value); setSelectedRoadName(""); if (value !== "All") { populateRoadNames(value); } else { setRoadNameOptions([]); } if (jimuMapView?.view) { const map = jimuMapView.view.map; toggleUtilityLayerVisibility(map, value.toLowerCase()); // function from earlier } }} disabled={!selectedZone || selectedZone === "All"} > <option value="All">All Utilities</option> {utilityLayerOptions.map(name => ( <option key={name} value={name}> {name}{selectedUtilityLayer === name && utilityCount !== null ? ` (${utilityCount})` : ""} </option> ))} </select> </div>
{/* Road Name selection */} {selectedUtilityLayer !== "All" && roadNameOptions.length > 0 && ( <div className="property_form"> <label>Select Road Name:</label> <select value={selectedRoadName} onChange={e => setSelectedRoadName(e.target.value)} > <option value="">--All Roads--</option> {roadNameOptions.map(name => ( <option key={name} value={name}>{name}</option> ))} </select> </div> )} </> )}
{/* Action buttons */} <div className="button-group"> <button type="button" className={`submit-button ${isLoading ? 'loading' : ''}`} onClick={handleSubmit} disabled={isLoading || !selectedZone || !selectedLayer} > {isLoading ? <div className="spinner"></div> : "Apply Filters"} </button> <button type="button" className="reset-button" onClick={handleReset} > Reset All </button> </div>
{/* Results table */} {tableData.length > 0 && ( <div className="attribute-table"> <h4>Asset Details ({tableData.length} features)</h4> <div className="table-container"> <table> <thead> <tr> <th>ID</th> <th>Zone</th> <th>Ward</th> <th>Road Name</th> <th>Action</th> </tr> </thead> <tbody> {tableData.map((feature, index) => { const attr = feature.attributes; return ( <tr key={feature.attributes.OBJECTID || index}> <td>{attr.OBJECTID || index + 1}</td> <td>{attr.zone_number || 'N/A'}</td> <td>{attr.ward_number || 'N/A'}</td> <td>{attr.road_name || 'N/A'}</td> <td> <button onClick={() => zoomToFeature(feature)} className="zoom-button" title="Zoom to feature" > <img src={zoomInIcon} alt="Zoom" width={16} height={16} /> </button> </td> </tr> ); })} </tbody> </table> </div> </div> )}
{tableData.length === 0 && selectedLayer && ( <div className="no-results"> <p>No assets found with the current filters.</p> </div> )} </div> </div> );};
export default RoadAssetFilter;
|