Frontend Development 17 min read

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.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rapid Development of a 3D Parking Lot Visualization Using the esMap Engine

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.

frontendJavaScriptWebGL3D visualizationesMapParking lot
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.