Convert an existing 2D ArcGIS map in a Next.js web application to 3D SceneView, displaying stacked highway segments organized by year. Each year's segments will be positioned at a different elevation to create a vertical "history stack" that visualizes highway changes over time.
- Replace 2D MapView with 3D SceneView in Next.js application
- Display highway segments from TxDOT/similar data source
- Stack segments by year using elevation (z-values)
- Enable 3D interaction (rotation, tilt, zoom)
- Maintain existing data structure and feature service integration
- Next.js application running on web
- 2D ArcGIS map (MapView) displaying highway segments
- Feature service or layer containing highway geometry
- Next.js application with 3D ArcGIS map (SceneView)
- Highway segments stacked vertically by year
- 3D basemap with elevation surface
- Interactive 3D navigation and visualization
Next.js App
├── SceneView Component (replaces MapView)
├── Map Object (with 3D basemap + elevation ground)
├── FeatureLayer (highways with elevation info)
├── Renderer (3D symbol styling)
└── UI Controls (optional: year filter, legend, camera)
- Framework: Next.js (React)
- GIS Library: ArcGIS Maps SDK for JavaScript 4.x
- View Type: SceneView (3D)
- Data Source: Feature Service (HTTP REST endpoint)
- Styling: 3D Symbols (LineSymbol3D, PolygonSymbol3D)
Goal: Prepare Next.js project for ArcGIS 3D development
-
Verify ArcGIS SDK Installation
- Confirm
@arcgis/corepackage is installed - Update to latest version if needed:
npm install @arcgis/core@latest - Verify version is 4.25+
- Confirm
-
Create/Update Map Component
- Create new component:
components/HighwayScene3D.jsx - Use
'use client'directive for Next.js 13+ App Router - Set up
useEffecthook for view initialization
- Create new component:
-
Configure Module Imports
- Import SceneView (not MapView)
- Import FeatureLayer
- Import 3D symbol classes (LineSymbol3D, PolygonSymbol3D, etc.)
- Test that imports resolve without errors
Deliverable: Component template with proper imports, no errors
Goal: Replace 2D map with 3D scene
-
Update Map Configuration
- Change basemap from 2D (e.g., 'streets') to 3D (e.g., 'topo-3d')
- Add
ground: 'world-elevation'to map constructor - Verify basemap displays with terrain/elevation
-
Replace View Constructor
- Remove MapView import/instantiation
- Create SceneView instead:
const view = new SceneView({ map, container: mapDiv.current, center: [-99.9, 33.5], // TxDOT focus area zoom: 10, tilt: 45, heading: 0 });
-
Set Initial Camera Position
- Center on highway area (Texas coordinates: ~-99.9°, 33.5°)
- Set zoom level appropriate for highway visibility (zoom 10-12)
- Set tilt to show 3D perspective (45° recommended)
- Optional: set heading for directional view
-
Verify 3D Rendering
- Check that terrain/elevation displays
- Confirm camera tilt/rotation works
- Test zoom and pan interactions
Deliverable: Functional 3D map with terrain, correct center/zoom
Goal: Ensure highway data has year information for stacking
-
Audit Current Feature Service
- Document feature service URL
- List all available attributes/fields
- Identify year-related field (e.g., YEAR_COMPLETED, SURVEY_YEAR, CONSTRUCTION_YEAR)
- Check data type (numeric vs. string) and value range
-
Verify Geometry Type
- Confirm segments are line geometry (not point or polygon)
- Check for any invalid geometries
- Test query to feature service returns expected data
-
Add Year Attribute if Missing
- If no year field exists, create one in ArcGIS Pro or data source
- Populate with historical construction/modification dates
- Options:
- Import from external database
- Manually attribute in GIS
- Use field calculation if pattern exists
- Republish feature service after changes
-
Document Data Mapping
- Create reference table: Year → Z-height formula
- Example: Year 1980 = 0m, Year 1981 = 100m, Year 1982 = 200m
- Confirm no duplicate years for same segment (or decide how to handle)
Deliverable: Data audit document + validated feature service with year field
Goal: Position segments at different elevations by year
-
Add FeatureLayer with Elevation Info
const highwayLayer = new FeatureLayer({ url: 'https://your-service-url/FeatureServer/0', elevationInfo: { mode: 'absolute-height', // or 'relative-to-ground' featureExpressionInfo: { expression: `(($feature.YEAR - 1980) * 100)` } } });
-
Define Elevation Formula
- Base year: 1980 (first year of data, or adjust as needed)
- Height increment per year: 100m (adjustable)
- Formula:
(Year - BaseYear) * Increment - Test with sample years to verify correct spacing
-
Choose Elevation Mode
absolute-height: Use z-values directly (ground = 0m, stack above)relative-to-ground: Add z-value to terrain elevation- Recommendation: Use
absolute-heightfor consistent stacking
-
Test Layer Addition
- Add layer to map:
map.add(highwayLayer) - Verify segments appear in 3D at expected heights
- Check for rendering errors in console
- Add layer to map:
Deliverable: Highway layer displays stacked by year with correct z-positions
Goal: Make stacked segments visually distinct and recognizable
-
Define 3D Symbol (LineSymbol3D)
const renderer = new SimpleRenderer({ symbol: new LineSymbol3D({ symbolLayers: [ new LineSymbolLayer({ material: { color: [0, 120, 215] }, size: 6 // width in pixels }) ] }) });
-
Apply Renderer to Layer
- Set renderer on FeatureLayer
- Verify lines appear with correct color/width in 3D
-
Optional: Color-Code by Year
- Create ClassBreaksRenderer or UniqueValueRenderer
- Assign different colors to year ranges
- Example: 1980-1990 = Blue, 1990-2000 = Green, etc.
- Helps distinguish stacked layers visually
-
Optional: Add Extrusion
- Create vertical walls/ribbons instead of lines
- Use PolygonSymbol3D for buffer geometry
- Experiment with transparency/opacity
-
Test Visual Appearance
- Zoom in/out to verify symbol scaling
- Rotate camera to see layers from different angles
- Adjust color/size for clarity
Deliverable: Visually distinct 3D segments, color-coded or styled appropriately
Goal: Integrate 3D component into Next.js application
-
Import Component in Page/Layout
- Add HighwayScene3D component to main page
- Set container to full viewport (
width: 100%,height: 100vh) - Verify no layout issues
-
Test Performance
- Load map with all years of data
- Measure frame rate (aim for 60 FPS)
- If slow, implement layer filtering or data reduction
- Monitor memory usage in DevTools
-
Browser Compatibility
- Test on Chrome, Firefox, Safari, Edge
- Check mobile responsiveness (if applicable)
- Verify touch controls work for 3D navigation
-
Error Handling
- Add try/catch around view initialization
- Log errors to console for debugging
- Handle case where feature service is unavailable
Deliverable: Fully integrated, tested, performant 3D map
Goal: Add interactive controls and visualizations
-
Year Filter/Slider
- Create UI slider to select year range
- Filter FeatureLayer using
definitionExpression - Example:
definitionExpression = "YEAR >= 2000 AND YEAR <= 2020" - Update view dynamically as user adjusts slider
-
Legend
- Display year ranges and corresponding colors
- Show what each stacked layer represents
- Update legend based on active filters
-
Camera Controls
- Add buttons to rotate/tilt camera
- Preset views (top-down, angled, side view)
- Auto-fly to feature on selection
-
Layer Toggling
- Add checkboxes to show/hide specific years
- Useful for comparing two years side-by-side
-
Popup/Info Panel
- Click segment to show highway details
- Display year, road name, length, etc.
- Add hyperlinks or more actions
Deliverable: Enhanced UI with controls, legend, and interactivity
'use client';
import { useEffect, useRef } from 'react';
import { Map } from '@arcgis/core/Map';
import { SceneView } from '@arcgis/core/views/SceneView';
import { FeatureLayer } from '@arcgis/core/layers/FeatureLayer';
import { SimpleRenderer } from '@arcgis/core/renderers/SimpleRenderer';
import { LineSymbol3D } from '@arcgis/core/symbols/LineSymbol3D';
import { LineSymbolLayer } from '@arcgis/core/symbols/LineSymbolLayer';
export default function HighwayScene3D() {
const mapDiv = useRef(null);
useEffect(() => {
const map = new Map({
basemap: 'topo-3d',
ground: 'world-elevation'
});
const view = new SceneView({
map,
container: mapDiv.current,
center: [-99.9, 33.5],
zoom: 10,
tilt: 45
});
const highways = new FeatureLayer({
url: 'https://your-service-url/FeatureServer/0',
renderer: new SimpleRenderer({
symbol: new LineSymbol3D({
symbolLayers: [
new LineSymbolLayer({
material: { color: [0, 120, 215] },
size: 6
})
]
})
}),
elevationInfo: {
mode: 'absolute-height',
featureExpressionInfo: {
expression: '(($feature.YEAR - 1980) * 100)'
}
}
});
map.add(highways);
return () => view.destroy();
}, []);
return <div ref={mapDiv} style={{ width: '100%', height: '100vh' }} />;
}// Option 1: Simple year-based (each year = 100m)
expression: '(($feature.YEAR - 1980) * 100)'
// Option 2: Custom scaling (smaller increments)
expression: '(($feature.YEAR - 1980) * 50)'
// Option 3: Logarithmic scaling (less cramped for large year ranges)
expression: 'Log(($feature.YEAR - 1980 + 1)) * 200'
// Option 4: Conditional (different heights for different road types)
expression: `
var roadType = $feature.ROAD_TYPE;
var yearHeight = ($feature.YEAR - 1980) * 100;
return roadType == 'Interstate' ? yearHeight + 100 : yearHeight;
`Feature Service (REST API)
↓
├─ Geometry (Line)
├─ YEAR Attribute
└─ Other attributes (name, length, etc.)
↓
FeatureLayer
↓
Elevation Calculation
├─ Formula: (YEAR - 1980) * 100
└─ Result: Z-height per segment
↓
3D Renderer
├─ Symbol: LineSymbol3D
├─ Color: Based on year or fixed
└─ Size: 6px width
↓
SceneView
├─ Basemap: topo-3d
├─ Ground: world-elevation
└─ Segments stacked vertically by year
↓
User Interaction
├─ Rotate/Tilt camera
├─ Filter by year (optional)
├─ Click for details (optional)
└─ Explore 3D history
| Package | Version | Purpose |
|---|---|---|
@arcgis/core |
4.25+ | ArcGIS Maps SDK for JavaScript |
react |
18+ | UI framework (Next.js requirement) |
next |
13+ | App Router support |
node |
18+ | Runtime |
project-root/
├── app/
│ ├── page.jsx
│ └── layout.jsx
├── components/
│ ├── HighwayScene3D.jsx ← Main 3D map component
│ ├── YearSlider.jsx ← (Optional) Year filter
│ ├── Legend.jsx ← (Optional) Legend
│ └── ControlPanel.jsx ← (Optional) UI controls
├── utils/
│ └── arcgis-config.js ← Service URLs, constants
├── public/
└── package.json
Create utils/arcgis-config.js:
export const ARCGIS_CONFIG = {
// Feature Service
HIGHWAY_SERVICE_URL: 'https://services.arcgis.com/...',
// Map settings
BASEMAP: 'topo-3d',
CENTER: [-99.9, 33.5], // TxDOT area
ZOOM: 10,
TILT: 45,
// Elevation stacking
BASE_YEAR: 1980,
HEIGHT_INCREMENT: 100, // meters per year
// Styling
LINE_COLOR: [0, 120, 215],
LINE_WIDTH: 6
};- 3D map renders without errors
- Terrain/elevation displays correctly
- Highway segments appear stacked by year
- Camera tilt/rotation works smoothly
- Zoom in/out functions correctly
- Layer responds to filtering (if implemented)
- No console errors or warnings
- Performance acceptable (60+ FPS)
- Mobile-responsive (if applicable)
- Works in target browsers (Chrome, Firefox, Safari, Edge)
- Solution: Verify elevationInfo mode is correct, check feature service URL, inspect network tab for failed requests
- Solution: Check YEAR attribute has numeric values, verify elevation formula in expression, ensure baseYear matches data
- Solution: Filter to fewer years initially, reduce symbol complexity, implement clustering for dense areas
- Solution: Verify LineSymbol3D import, check that symbolLayers array has at least one layer, test with basic color value
- Solution: Confirm ref is attached to div, check view constructor has correct container, verify map has basemap assigned
- Ensure feature service is accessible from production environment (check CORS headers)
- Test 3D performance on target hardware
- Optimize bundle size (ArcGIS SDK can be large—consider lazy loading)
- Monitor CDN usage if using esri-hosted resources
- Keep ArcGIS Maps SDK version consistent across environments
-
Immediate (Week 1):
- Set up component template (Phase 1)
- Replace MapView with SceneView (Phase 2)
- Verify 3D basemap renders
-
Short-term (Week 1-2):
- Audit and prepare data (Phase 3)
- Implement elevation stacking (Phase 4)
- Apply 3D styling (Phase 5)
-
Medium-term (Week 2-3):
- Integration and testing (Phase 6)
- Bug fixes and optimization
- Deployment to staging
-
Long-term (Week 3+):
- Enhanced features (Phase 7): year filter, legend, popups
- User feedback and refinement
- Production deployment
- ArcGIS Maps SDK for JavaScript – SceneView
- ArcGIS Maps SDK for JavaScript – Scenes (3D)
- Building ArcGIS Apps with Next.js
- Extrude Features to 3D Symbology
- Z-Values and Elevation Info
- Component Setup Issues: Check ArcGIS SDK imports and Next.js useEffect patterns
- Data Questions: Verify feature service structure and year attribute mapping
- Performance Issues: Use Chrome DevTools Performance tab to profile rendering
- Deployment Issues: Test CORS headers on feature service, check production URLs