The map service uses REST APIs. Frontend development can use the Service class in the vjmap JS SDK to call the service. To get map metadata, use:
letsvc=newvjmap.Service(env.serviceUrl,env.accessToken);letres=awaitsvc.metadata("sys_zp"/*map id*/,"v1"/*map version, empty for latest*/);console.log(res)
When this code runs in the browser, F12 Network shows an http request to the map service.
image-20230524140958184
For backend calls, send the same http request.
Test in Postman:
The map service REST API returns correct results when called from Postman.
Note: All service calls must use application/json and include the Token header (or token=xxxx query parameter for GET requests) for authentication. To open password-protected maps without a password prompt, add secretKey or accessKey in the request header. (This is not repeated in examples below; remember to add Token in practice.)
Common Service APIs
(For brevity, only relative URLs are shown. E.g. https://vjmap.com/server/api/v1/map/cmd/metadata/sys_zp/v1 is abbreviated as /api/v1/map/cmd/metadata/sys_zp/v1)
Upload Drawing
Service URL
/api/v1/map/uploads
Request Type
Post
Request Parameter
Parameter Description
Parameter Example
fileToUpload
File binary content
Create or Open Drawing
Service URL
/api/v1/map/openmap/{mapid}
Request Type
Get or Post
Request Parameter
Parameter Description
Parameter Example
version
Map version
layer
Style
geom
Open with geometry render mode
true
fileid
File unique ID. Pass fileid when opening map for first time
cfbe8dc085fb
imageLeft
Image top-left x. Valid when opening image type for first time
undefined
imageTop
Image top-left y. Valid when opening image type for first time
imageResolution
Image resolution. Valid when opening image type for first time
uploadname
Upload filename
zp.dwg
mapfrom
Map source (for collaborative drawings)
mapdependencies
Map dependencies (for collaborative drawings)
subfrom
Map source for sub-items (for collaborative drawings)
subdependencies
Map dependencies for sub-items (for collaborative drawings)
stylename
Style layer name
layeron
Layer indices to turn on, e.g. [0,1,3]
layeroff
Layer indices to turn off, e.g. [2,4]
clipbounds
Map clip bounds, e.g. [x1,y1,x2,y2]
bkcolor
Background color
0
lineweight
Line weight, e.g. [1,1,1,1,0]
expression
Style expression
secretKey
Set when encrypting map on first creation
accessKey
Access key when map has password
Note: When setting a password on first upload, set secretKey. Compute as: MD5(MD5(MD5(MD5(password)))), take first 16 chars.
Node.js backend upload and create/open example
// app.js
constfs=require('fs')constFormData=require('form-data')constaxios=require('axios')constprompt=require('prompt');(async()=>{constdefaultServiceUrl='https://vjmap.com/server/api/v1';constdefaultAccessToken='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M';letformData=newFormData();let{filepath}=awaitprompt.get('filepath');// Enter file path to upload
if(!filepath)return;letfile=fs.createReadStream(filepath)formData.append('fileToUpload',file);letlen=awaitnewPromise((resolve,reject)=>{returnformData.getLength((err,length)=>(err?reject(err):resolve(length)));});letres=awaitaxios({url:`${defaultServiceUrl}/map/uploads?token=${defaultAccessToken}`,method:'POST',data:formData,headers:{...formData.getHeaders(),'Content-Length':len,},});/*
{
fileid: 'c036d8ca23eb',
mapid: 'c036d8ca23eb', // mapid is suggested; if previously uploaded/opened, use that mapid
uploadname: 'base1.dwg'
}
*/console.log(res.data);// Upload sends file to server. On first open, pass fileid to openMap and assign mapid. Afterwards open by mapid.
let{mapid}=awaitprompt.get('mapid');// Enter mapid
if(!mapid){// If no mapid, use default from upload response
mapid=res.data.mapid;}leturl=`${defaultServiceUrl}/map/openmap/${res.data.mapid}?version=&layer=&geom=true&fileid=${res.data.fileid}&uploadname=${res.data.uploadname}&mapfrom=&mapdependencies=&subfrom=&subdependencies=&stylename=&layeron=&layeroff=&clipbounds=&bkcolor=0&lineweight=&expression=`;// To set password, e.g. 123456
// MD5 four times, strMd5 is MD5 helper, implement as needed
// let secretKey = strMd5(strMd5(strMd5(strMd5("123456")))).substr(0, 16);
// url += "&secretKey=" + secretKey
letresult=awaitaxios({url:url,method:'GET',headers:{token:defaultAccessToken,},});console.log(result)})();
To create drawings from parameters in the backend, generate a JSON object from the entity objects, serialize to string (or base64), and pass as fileid.
Node.js parameter-based drawing creation:
constaxios=require("axios");(async()=>{constdefaultServiceUrl="https://vjmap.com/server/api/v1";constdefaultAccessToken="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M";letdoc={};letentitys=[];// See official docs https://vjmap.com/doc/Class_DbDocument.html for parameters
entitys.push({typename:"DbLine",start:[14,0],end:[14,15],colorIndex:1,});entitys.push({typename:"DbCircle",center:[14,7.5],radius:1.83,color:0xff0000,});entitys.push({typename:"DbText",position:[14,16],contents:"Basketball court diagram",colorIndex:1,horizontalMode:4,height:1,});doc.entitys=entitys;// Base64 encoding also works but not recommended; JSON string is fine
//var bytes = new Buffer(JSON.stringify(doc));
//var base64 = "data:application/dwg;base64," + bytes.toString('base64')// let fileid = base64;
letfileid=JSON.stringify(doc);// Upload sends file to server. On first open, pass fileid to openMap and assign mapid. Afterwards open by mapid.
letmapid="mybackendmap"leturl=`${defaultServiceUrl}/map/updatemap/${mapid}`;letdata={fileid:fileid,clipbounds:'',bkcolor:0,geom:true,uploadname:""}// To set password (e.g. 123456): MD5 four times; implement strMd5 as needed
// let secretKey = strMd5(strMd5(strMd5(strMd5("123456")))).substr(0, 16);
// url += "&secretKey=" + secretKey
letresult=awaitaxios({url:url,method:"POST",headers:{token:defaultAccessToken,},data});console.log(result);})();
Result:
image-20230524172844505
Check If File Already Uploaded
Service URL
api/v1/map/mapfile
Request Type
Get
Service Description
Check if file exists on server before upload; if so return fileid without uploading. Can be skipped in practice.
Request Parameter
Parameter Description
Parameter Example
md5
File MD5 value
fbe8dc085fbcdac502c640cf2dd8065c
Node.js implementation for file MD5 (other backends can adapt):
importSparkMD5=require("spark-md5");/**
* Get file MD5 value
* @param file
* @return {Promise<any>}
*/functionfileMd5(file:File):Promise<any>{returnnewPromise((resolve,reject)=>{constblobSlice=// @ts-ignore
File.prototype.slice||File.prototype.mozSlice||File.prototype.webkitSlice;constchunkSize=2097152;// Read in chunks of 2MB
constchunks=Math.ceil(file.size/chunkSize);letcurrentChunk=0;constspark=newSparkMD5.ArrayBuffer();constfileReader=newFileReader();fileReader.onload=function(e){// @ts-ignore
spark.append(e.target.result);// Append array buffer
currentChunk++;if(currentChunk<chunks){loadNext();}else{varmd5=spark.end();resolve(md5);}};fileReader.onerror=function(){reject("file reader error");};functionloadNext(){varstart=currentChunk*chunkSize;varend=start+chunkSize>=file.size?file.size:start+chunkSize;fileReader.readAsArrayBuffer(blobSlice.call(file,start,end));}loadNext();});}
cmdname: command name; mapid: map id; version: version
Request Parameter
Parameter Description
Parameter Example
Parameters vary by command name
Get Map Metadata
Service URL
api/v1/map/cmd/metadata/${mapid}/${version}
Request Type
Get or Post
Service Description
mapid: map id; version: version
Request Parameter
Parameter Description
Parameter Example
geom
Whether geometry rendering
Update Map Metadata
Service URL
api/v1/map/cmd/updateMetadata/${mapid}/${version}
Request Type
Get or Post
Service Description
mapid: map id; version: version
Request Parameter
Parameter Description
Parameter Example
Metadata key
Value for the metadata key
Proactively Close Open Map
Service URL
api/v1/map/cmd/closemap/${mapid}/${version}
Request Type
Get or Post
Service Description
mapid: map id; version: version
Request Parameter
Parameter Description
Parameter Example
(none)
(none)
Get Map List
Service URL
api/v1/map/cmd/listmaps/${mapid}/${version}
Request Type
Get or Post
Service Description
mapid: map id (empty = get all); pass map ID array to get specified maps; use pagination e.g. {curPage: 1, pageCount: 10}; version: version (empty = latest, * = all versions)
Request Parameter
Parameter Description
Parameter Example
mapIds
When mapid is empty, specify map ID array to query
Comma-separated field list to return, e.g. "name,objectid"
geom
Whether geometry query
true
includegeom
Return geometry: when realgeom is false, if count>1 returns bounding rect per entity, if count=1 returns real GeoJSON; when realgeom is true returns GeoJSON per entity
true
isContains
Contain vs intersect: true=contain, false=intersect (default false). Used when bounds is set
false
layername
Style name (required). Use stylename from createMapStyle, e.g. api/v1/map/cmd/createMapStyle/yourMapid/v1?geom=true&token=xxx
maxReturnCount
Max records to return
50000
realgeom
Return real entity geometry GeoJSON. Used with includegeom
true
expr
Expression
x
Point query x
y
Point query y
pixelsize
Point query pixel size
zoom
Current zoom level
1
toMapCoordinate
By default query returns Mercator; result is converted to CAD. Set true to return CAD coordinates directly.
Relevant SDK query source in JS:
/**
* Query entity parameters
*/exportinterfaceIQueryBaseFeatures{/** Current zoom level. */zoom?:number;/** Map ID (empty = current map). */mapid?:string;/** Map version (empty = current). */version?:string;/** Layer name (empty = current map layer). */layer?:string;/** Max records to return. */limit?:number;/** Comma-separated field list to return, e.g. "name,objectid" */fields?:string;/** Whether geometry query. */geom?:boolean;/** GeoJSON simplify tolerance in Mercator; 0 = no simplify. */simplifyTolerance?:boolean;/** Enable cache (for in-memory maps). */useCache?:boolean;/** By default returns Mercator; set true to return CAD coordinates. */toMapCoordinate?:boolean;}/**
* Point query entity parameters
*/exportinterfaceIPointQueryFeaturesextendsIQueryBaseFeatures{/** Query X. */x:number;/** Query Y. */y:number;/** Pixel size. */pixelsize?:number;/** Condition. */condition?:string;/** Max geometry bytes to return. */maxGeomBytesSize?:number;/** Geo length per pixel; if set, overrides zoom-based calculation. */pixelToGeoLength?:number;}/**
* Rect query entity parameters
*/exportinterfaceIRectQueryFeaturesextendsIQueryBaseFeatures{/** X1 (omit x1,y1,x2,y2 to query full map). */x1?:number;/** Y1. */y1?:number;/** X2. */x2?:number;/** Y2. */y2?:number;/** Condition. */condition?:string;/** Max geometry bytes to return. */maxGeomBytesSize?:number;}/**
* Expression query entity parameters
*/exportinterfaceIExprQueryFeaturesextendsIQueryBaseFeatures{/** Expression. */expr:string;/** Record start position. */beginpos?:number;}/**
* Condition query entity parameters
*/exportinterfaceIConditionQueryFeaturesextendsIQueryBaseFeatures{/** Condition. */condition:string;/** Bounds. */bounds?:[number,number,number,number];/** Record start position. */beginpos?:number;/** Include geometry; when realgeom false, multiple results get bbox only, single gets GeoJSON; when realgeom true each gets GeoJSON. */includegeom?:boolean;/** Return real entity GeoJSON; used with includegeom. */realgeom?:boolean;/** Contain (true) vs intersect (false) when bounds set; default false. */isContains?:boolean;}/**
* Point query entities
* @param param Parameters
* @param cb Callback for each point in result; null = default handling
*/asyncpointQueryFeature(param:IPointQueryFeatures,cb?:(point:[number,number])=>[number,number]|null|undefined):Promise<any>{constres=awaitthis.execCommand("queryFeatures",{querytype:"point",zoom:param.zoom??1,pixelToGeoLength:param.pixelToGeoLength??undefined,layername:param?.layer??this._cur_map_param?.layer,x:param.x,y:param.y,pixelsize:param.pixelsize??5,condition:param.condition??undefined,maxReturnCount:param.limit??undefined,maxGeomBytesSize:param.maxGeomBytesSize??undefined,fields:param.fields??"",geom:param.geom??undefined,simplifyTolerance:param.simplifyTolerance??undefined,useCache:param.useCache,toMapCoordinate:param.toMapCoordinate,},param.mapid,param.version);returnthis._processQueryResult(res,cb);}/**
* Rect query entities
* @param param Parameters
* @param cb Callback for each point; null = default handling
*/asyncrectQueryFeature(param:IRectQueryFeatures,cb?:(point:[number,number])=>[number,number]|null|undefined):Promise<any>{constres=awaitthis.execCommand("queryFeatures",{querytype:"rect",zoom:param.zoom??1,layername:param?.layer??this._cur_map_param?.layer,x1:param.x1,y1:param.y1,x2:param.x2,y2:param.y2,condition:param.condition??undefined,maxReturnCount:param.limit??undefined,maxGeomBytesSize:param.maxGeomBytesSize??undefined,fields:param.fields??"",geom:param.geom??undefined,simplifyTolerance:param.simplifyTolerance??undefined,useCache:param.useCache,toMapCoordinate:param.toMapCoordinate,},param.mapid,param.version);returnthis._processQueryResult(res,cb);}/**
* Expression query entities
* @param param Parameters
* @param cb Callback for each point; null = default handling
*/asyncexprQueryFeature(param:IExprQueryFeatures,cb?:(point:[number,number])=>[number,number]|null|undefined):Promise<any>{constres=awaitthis.execCommand("queryFeatures",{querytype:"expresion",zoom:param.zoom??1,layername:param?.layer??this._cur_map_param?.layer,expr:param.expr,beginpos:param.beginpos,maxReturnCount:param.limit??undefined,limit:param.limit??undefined,fields:param.fields??"",useCache:param.useCache??false,geom:param.geom??undefined,simplifyTolerance:param.simplifyTolerance??undefined,toMapCoordinate:param.toMapCoordinate,},param.mapid,param.version);returnthis._processQueryResult(res,cb);}/**
* Condition query entities
* @param param Parameters
* @param cb Callback for each point; null = default handling
*/asyncconditionQueryFeature(param:IConditionQueryFeatures,cb?:(point:[number,number])=>[number,number]|null|undefined):Promise<any>{constres=awaitthis.execCommand("queryFeatures",{querytype:"condition",zoom:param.zoom??1,layername:param?.layer??this._cur_map_param?.layer,condition:param.condition,beginpos:param.beginpos,maxReturnCount:param.limit??undefined,limit:param.limit??undefined,includegeom:param.includegeom??undefined,realgeom:param.realgeom??undefined,isContains:param.isContains??undefined,fields:param.fields??"",bounds:param.bounds?JSON.stringify(param.bounds):"",geom:param.geom??undefined,simplifyTolerance:param.simplifyTolerance??undefined,useCache:param.useCache,toMapCoordinate:param.toMapCoordinate,},param.mapid,param.version);returnthis._processQueryResult(res,cb);}// _processQueryResult converts returned Mercator coordinates to CAD coordinates. For backend queries returning CAD coordinates directly, set toMapCoordinate to true.
You can also inspect request parameters via the visual interface: