Quick start
Quick start
This guide takes you from zero to your first CAD shape on the web in a few minutes.
Prerequisites
- Node.js >= 16 (18+ recommended)
- vjmap SDK — vjmapext is an extension; use it together with vjmap
- A vjmap service URL and Access Token (official test env is fine)
Access Token
Register at vjmap to get a test URL and token. Sample DWGs are included for development.
Install
npm (recommended)
npm install vjmap vjmapextCDN / script tag
Without a bundler you can load scripts directly:
<!-- vjmap SDK -->
<script src="https://vjmap.com/demo/js/vjmap/vjmap.min.js"></script>
<link rel="stylesheet" href="https://vjmap.com/demo/js/vjmap/vjmap.min.css" />
<!-- vjmapext -->
<script src="https://vjmap.com/demo/js/vjmapext/vjmapext.min.js"></script>Version pairing
Use compatible vjmap (>=1.0.161) and vjmapext versions from the same release cycle.
Map container sizing
The vjmap.Map container (#map / #map-container) must have non-zero width and height before mount, or the map may flash blank / show only a dark background:
/* A: full viewport (good for demos / fullscreen) */
#map { width: 100vw; height: 100vh; }
/* B: absolute fill of a positioned ancestor */
.page-wrap { position: relative; }
#map { position: absolute; inset: 0; }createUI and container positioning
mapcadLayer.createUI() overlays toolbar/panels with position:absolute. Since v0.1, only when the container’s computedStyle.position is static, the SDK temporarily sets position:relative and restores it on dispose(). Existing absolute / fixed / relative / sticky values are preserved (older builds could collapse height to 0).
Vite project (recommended)
Example: Vite + vanilla JS (Vue / React are similar).
1. Create project
npm create vite@latest my-cad-app -- --template vanilla
cd my-cad-app
npm install vjmap vjmapext2. HTML template
Add the map container in index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>vjmapext quick start</title>
<style>
html, body, #map { margin: 0; padding: 0; width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>3. Entry file
In src/main.js:
import vjmap from 'vjmap';
import 'vjmap/dist/vjmap.min.css';
import { MapCadLayer, LineEnt, CircleEnt, TextEnt } from 'vjmapext';
async function main() {
const svc = new vjmap.Service(
'https://vjmap.com/server/api/v1',
'YOUR_ACCESS_TOKEN'
);
const mapExtent = vjmap.GeoBounds.fromArray([-1000, -1000, 1000, 1000]);
const prj = new vjmap.GeoProjection(mapExtent);
const map = new vjmap.Map({
container: 'map',
style: svc.rasterBlankStyle(0, 24),
center: prj.toLngLat(mapExtent.center()),
zoom: 1,
pitch: 0,
renderWorldCopies: false
});
map.attach(svc, prj);
await map.onLoad();
const mapcadLayer = new MapCadLayer();
map.addControl(mapcadLayer);
mapcadLayer.createUI({ theme: 'dark' });
const line = new LineEnt([-200, 0], [200, 0]);
line.color = 0xff4444;
line.lineWidth = 2;
mapcadLayer.addEntity(line);
const circle = new CircleEnt([0, 200], 100);
circle.color = 0x00ffff;
circle.lineWidth = 2;
mapcadLayer.addEntity(circle);
const text = new TextEnt([-200, -150], 'Hello vjmapext!', 40);
text.color = 0x44ff44;
mapcadLayer.addEntity(text);
}
main();4. Run
npm run devYou should see a red line, a cyan circle, and green text.
Core initialization flow
Key idea
MapCadLayer is an IControl. After map.addControl(mapcadLayer), it hooks into projection, render loop, and input. Entities added with addEntity() render on the map automatically.
Drawing on a CAD basemap
Besides a blank map, a typical workflow opens a DWG first:
import vjmap from 'vjmap';
import { MapCadLayer, HatchEnt, TextEnt, CircleEnt } from 'vjmapext';
async function main() {
const svc = new vjmap.Service(
'https://vjmap.com/server/api/v1',
'YOUR_ACCESS_TOKEN'
);
const res = await svc.openMap({
mapid: 'YOUR_MAP_ID',
mapopenway: vjmap.MapOpenWay.GeomRender,
style: vjmap.openMapDarkStyle()
});
if (res.error) { throw new Error(res.error); }
const mapExtent = vjmap.GeoBounds.fromString(res.bounds);
const prj = new vjmap.GeoProjection(mapExtent);
const map = new vjmap.Map({
container: 'map',
style: svc.rasterStyle(),
center: prj.toLngLat(mapExtent.center()),
zoom: 2,
renderWorldCopies: false
});
map.attach(svc, prj);
map.fitMapBounds();
await map.onLoad();
const mapcadLayer = new MapCadLayer();
map.addControl(mapcadLayer);
mapcadLayer.createUI({ theme: 'dark' });
const cx = mapExtent.center().x;
const cy = mapExtent.center().y;
const w = mapExtent.width() / 8;
const zone = new HatchEnt([
[cx - w, cy - w],
[cx + w, cy - w],
[cx + w, cy + w],
[cx - w, cy + w]
]);
zone.fillColor = 0x00ffff;
zone.fillOpacity = 0.15;
zone.color = 0x00ffff;
zone.lineWidth = 2;
mapcadLayer.addEntity(zone);
const label = new TextEnt([cx - w, cy + w * 1.2], 'Focus area', w * 0.3);
label.color = 0x00ffff;
mapcadLayer.addEntity(label);
}
main();Service
svc.openMap() needs a reachable vjmap backend. Ensure mapid exists on the server.
Create UI
Toolbar, property panel, command line — one call:
mapcadLayer.createUI({ theme: 'dark' });| Option | Description |
|---|---|
theme: 'dark' | Dark chrome for dark basemaps |
theme: 'light' | Light chrome for light basemaps |
After createUI(), users can run 30+ built-in draw/edit commands from the toolbar.
Optional
createUI() is optional. Skip it if you only drive drawing from code without on-screen UI.
Edit vs browse mode
- Edit (
edit): select, move, modify entities, run commands - Browse (
browse): read-only
const mapcadLayer = new MapCadLayer({ mode: 'browse' });
mapcadLayer.setMode('edit');
mapcadLayer.setMode('browse');Default shortcut Ctrl + Shift + E toggles modes.
Common MapCadLayer options
const mapcadLayer = new MapCadLayer({
locale: 'en', // 'zh' | 'en'
defaultColor: 0x00ff00,
mode: 'edit', // 'edit' | 'browse'
modeToggleShortcut: 'ctrl+shift+e',
nudgePixels: 1,
nudgeRotateDeg: 1,
nudgeScaleFactor: 1.02,
});Vue 3 + Vite
Two rules: sized container, and createUI() after the map is ready in onMounted.
1. index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>vjmapext · Vue 3</title>
<style>
html, body, #app { margin: 0; padding: 0; width: 100%; height: 100%; }
body { background: #022b4f; overflow: hidden; }
</style>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>2. src/Map.vue
<script setup lang="ts">
import 'vjmap/dist/vjmap.min.css'
import vjmap, { type Map } from 'vjmap'
import vjmapext from 'vjmapext'
import { onMounted, onUnmounted } from 'vue'
const { MapCadLayer, CircleEnt, TextEnt } = vjmapext
let map: Map | null = null
onMounted(async () => {
const svc = new vjmap.Service(
'https://vjmap.com/server/api/v1',
'YOUR_ACCESS_TOKEN',
)
const res = await svc.openMap({
mapid: 'YOUR_MAP_ID',
mapopenway: vjmap.MapOpenWay.GeomRender,
style: vjmap.openMapDarkStyle(),
})
if (res.error) throw new Error(String(res.error))
const mapExtent = vjmap.GeoBounds.fromString(res.bounds)
const prj = new vjmap.GeoProjection(mapExtent)
map = new vjmap.Map({
container: 'map-container',
style: svc.rasterStyle(),
center: prj.toLngLat(mapExtent.center()),
zoom: 2,
renderWorldCopies: false,
})
map.attach(svc, prj)
map.fitMapBounds()
await map.onLoad()
const cad = new MapCadLayer()
map.addControl(cad)
cad.createUI({ theme: 'dark' })
const c = new CircleEnt([mapExtent.center().x, mapExtent.center().y], mapExtent.width() / 8)
c.color = 0x00ffff
cad.addEntity(c)
const t = new TextEnt([mapExtent.center().x, mapExtent.center().y], 'Hello vjmapext', mapExtent.width() / 40)
t.color = 0x00ffff
cad.addEntity(t)
})
onUnmounted(() => {
map?.remove()
map = null
})
</script>
<template>
<div id="map-container" />
</template>
<style scoped>
#map-container {
position: absolute;
inset: 0;
background: #022b4f;
}
</style>Dependencies
npm install vjmap vjmapextPlain HTML <script>
Without a bundler, vjmap / vjmapext attach to globals:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>vjmapext · plain HTML</title>
<link rel="stylesheet" href="./vjmap/vjmap.min.css">
<script src="./vjmap/vjmap.min.js"></script>
<script src="./vjmapext/vjmapext.min.js"></script>
</head>
<body style="margin:0;overflow:hidden;background:#022B4F;">
<div id="map" style="position:absolute;inset:0;background:#022B4F;"></div>
<script>
(async () => {
const svc = new vjmap.Service(
'https://vjmap.com/server/api/v1',
'YOUR_ACCESS_TOKEN',
);
const res = await svc.openMap({
mapid: 'YOUR_MAP_ID',
mapopenway: vjmap.MapOpenWay.GeomRender,
style: vjmap.openMapDarkStyle(),
});
if (res.error) { console.error(res.error); return; }
const mapExtent = vjmap.GeoBounds.fromString(res.bounds);
const prj = new vjmap.GeoProjection(mapExtent);
const map = new vjmap.Map({
container: 'map',
style: svc.rasterStyle(),
center: prj.toLngLat(mapExtent.center()),
zoom: 2,
renderWorldCopies: false,
});
map.attach(svc, prj);
map.fitMapBounds();
await map.onLoad();
const { MapCadLayer, CircleEnt, TextEnt } = vjmapext;
const cad = new MapCadLayer();
map.addControl(cad);
cad.createUI({ theme: 'dark' });
const c = new CircleEnt([mapExtent.center().x, mapExtent.center().y], mapExtent.width() / 8);
c.color = 0x00ffff;
cad.addEntity(c);
})();
</script>
</body>
</html>Asset paths
./vjmap/* and ./vjmapext/* are placeholders — point to your CDN or static folder. Load vjmap.min.js before vjmapext.min.js.
Live demos
See vjmapext Demo Playground for full samples.
Next steps
- Architecture — internals and data flow
- Entity basics — create and manage entities
- Commands & input — commands and user input