Rapid Development of a 3D Parking Lot Visualization Using the esMap Engine
This article details how to quickly create a web‑based 3D parking‑lot demo with scene modeling, dynamic lamp markers, interactive controls, editing, heat‑map overlay, and path‑planning using the esMap visualization SDK, including full code snippets and deployment steps.
Background
The author received a last‑minute request to deliver a 3‑day demo of a parking‑lot 3D scene with lamp control, requiring a fast solution without building everything from scratch.
Preparation
After evaluating options, the esMap 3D visualization engine was chosen for its ready‑made scene building service, online demo page, and comprehensive SDK.
Project Setup
1. Register a free esMap account. 2. Download the basic scene package from the official site and host it on a static server (Node.js in the example). 3. Create parkingLot.html and include the required libraries.
<html>
...
<div id="map-container"></div>
<script src="../lib/config.js"></script>
<script src="../lib/esmap-3.0.min.js"></script>
<script src="../lib/jquery-2.1.4.min.js"></script>
<script type="text/javascript">
const esmapID = '$SCENE_ID';
const container = document.getElementById('map-container');
const map = new esmap.ESMap({
mode: esmap.MapMode.Building,
container: container,
mapDataSrc: '../data',
token: '$TOKEN'
});
map.on('mapClickNode', (event) => { console.log(JSON.stringify(event.hitCoord)); });
</script>
</html>1. Scene Modeling
Using the esMap console, import the CAD .dwg file, run AI‑based auto‑generation, edit the scene, and download the resulting scene package to the data folder.
2. Dynamic Markers (Lamp Control)
Markers are added via esmap.ESTextMarker . The code creates a dedicated layer, iterates over lamp data, and adds each marker with on/off icons.
// Lamp icons
const ICON_LIGHT_ON = '/data/parkinglot1/imagelabel/light_on.png';
const ICON_LIGHT_OFF = '/data/parkinglot1/imagelabel/light_off.png';
function addMarkers(data) {
const building = map.getBuilding();
const floorLayer = building.getFloor(1);
let layer = floorLayer.getOrCreateLayerByName('dynamic', esmap.ESLayerType.TEXT_MARKER);
data.forEach(item => {
const marker = new esmap.ESTextMarker({
id: item.id,
name: item.name,
x: item.coord.x,
y: item.coord.y,
height: 2.5,
image: ICON_LIGHT_OFF,
imageSize: 64,
fixedSize: true
});
layer.addMarker(marker);
markerList.push(marker);
});
floorLayer.addLayer(layer);
}
function clearMarkers() {
const building = map.getBuilding();
const floorLayer = building.getFloor(1);
floorLayer.getLayersByNames('dynamic', layer => { if (layer) { layer.removeAll(); markerList.splice(0); } });
}3. Visualization Operations (Info Pop‑up & Control)
Clicking a lamp opens a pop‑up with details and buttons that call lightOn(id) or lightOff(id) to toggle the icon.
function popInfo(target) {
const { ID, x, y, name } = target;
const popMarker = new esmap.ESPopMarker({
mapCoord: { x, y, height: 7, fnum: 1 },
width: 340,
height: 200,
content: `
ID: ${ID}
Name: ${name || '未知'}
x: ${x}
y: ${y}
开灯
关灯
`
});
}
function lightOn(id) { const m = markerList.find(v => v.ID == id); if (m) m.image = ICON_LIGHT_ON; }
function lightOff(id) { const m = markerList.find(v => v.ID == id); if (m) m.image = ICON_LIGHT_OFF; }
function lightOnAll() { markerList.forEach(m => m.image = ICON_LIGHT_ON); }
function lightOffAll() { markerList.forEach(m => m.image = ICON_LIGHT_OFF); }4. Editing Markers (Add / Delete / Save)
Mouse clicks are used to add or remove markers. The marker list is saved to localStorage for persistence.
// Add marker on click
container.onclick = function() {
if (mapCoord && isEditNode) {
createMarker({ coord: mapCoord, id: Date.now().toString() });
}
};
function createMarker(item) {
const building = map.getBuilding();
const floorLayer = building.getFloor(1);
const layer = floorLayer.getOrCreateLayerByName('dynamic', esmap.ESLayerType.TEXT_MARKER);
const marker = new esmap.ESTextMarker({
id: item.id,
x: item.coord.x,
y: item.coord.y,
height: 2.5,
image: ICON_LIGHT_OFF,
imageSize: 64,
fixedSize: true
});
layer.addMarker(marker);
markerList.push(marker);
}
function saveMarkerData() {
const data = markerList.map((m, i) => ({
id: Date.now().toString() + i,
name: '灯具',
coord: { x: m.x, y: m.y }
}));
localStorage.setItem('esMapLightData', JSON.stringify(data));
console.info('数据缓存成功');
}Extended Development
Dynamic Model Addition
Additional GLTF models (e.g., cameras) can be placed using esmap.ES3DMarker . Models must be uploaded and bound to the scene on the server.
function addModel(item) {
const building = map.getBuilding();
const floorLayer = building.getFloor(1);
const layer = floorLayer.getOrCreateLayerByName('models', esmap.ESLayerType.MODEL3D);
const im = new esmap.ES3DMarker({
x: item.coord.x,
y: item.coord.y,
id: item.id,
name: 'tube',
url: '/model/White-Gun-Camera.gltf',
size: 5,
angle: 260 * Math.random(),
height: 2
});
layer.addMarker(im);
}Heat‑Map Overlay
Using esmap.ESHeatMap , random points are generated to visualize crowd density on a specific floor.
function initHeatMap() {
heatmapInstance = esmap.ESHeatMap.create(map, {
bid: building.id,
radius: 20,
opacity: 1,
mapOpacity: 0.2,
backgroundColor: '#FFFFFF',
max: 100
});
}
function addRandomHeatMap() {
if (!heatmapInstance) initHeatMap();
heatmapInstance.setFloorNum(1);
heatmapInstance.clearPoints();
heatmapInstance.randomPoints(200);
building.getFloor(1).applyHeatMap(heatmapInstance);
}Path Planning & Navigation
After defining walkable paths in the scene editor, the SDK can compute routes. The UI records three clicks: start point, end point, and reset.
let clickCount = 0;
let lastCoord = null;
container.onclick = function() {
if (isEditRoute) showRoute(mapCoord);
};
function showRoute(coord) {
if (!navi) return;
if (clickCount === 2) { navi.clearAll(); clickCount = 0; lastCoord = null; }
if (clickCount === 0) {
lastCoord = coord;
navi.setStartPoint({ x: coord.x, y: coord.y, fnum: 1, url: 'image/start.png', height: 1, size: 64 });
} else if (clickCount === 1) {
if (lastCoord.x === coord.x) { alert('起点和终点不能相同!'); return; }
navi.setEndPoint({ x: coord.x, y: coord.y, fnum: 1, url: 'image/end.png', height: 1, size: 64 });
navi.getRouteResult({ drawRoute: true });
}
clickCount++;
}
function startNavi() {
if (navi.isSimulating) return;
navi.followAngle = true;
navi.followPosition = true;
navi.scaleAnimate = false;
navi.simulate();
}
function resetNav() { navi.stop(); navi.clearAll(); }Conclusion
The complete solution demonstrates that with esMap a functional 3D parking‑lot demo—including scene creation, lamp control, model addition, heat‑map, and navigation—can be built in a few hours, whereas a pure three.js implementation would require days of work.
While esMap offers rapid development, some features (scene count, custom model hosting) are paid, which is acceptable given the time saved.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.