500行JavaScript代碼在前端根據數據生成CAD工程剖面圖

来源:https://www.cnblogs.com/vjmap/archive/2023/03/05/17179837.html
-Advertisement-
Play Games

前言 用數據生成CAD圖,一般採用的ObjectArx對CAD二次開發完成。ObjectARX是AutoDesk公司針對AutoCAD平臺上的二次開發而推出的一個開發軟體包,它提供了以C++為基礎的面向對象的開發環境及應用程式介面,能訪問和創建AutoCAD圖形資料庫。而由於現在懂C++的人少,很多 ...


前言

用數據生成CAD圖,一般採用的ObjectArx對CAD二次開發完成。ObjectARX是AutoDesk公司針對AutoCAD平臺上的二次開發而推出的一個開發軟體包,它提供了以C++為基礎的面向對象的開發環境及應用程式介面,能訪問和創建AutoCAD圖形資料庫。而由於現在懂C++的人少,很多人對C++有點望而生畏。則JavaScript 是互聯網上最流行的腳本語言,用戶群體很大。那有沒有可能利用JavaScript來進行數據成圖?

今天和大家聊聊,怎麼用500行JavaScript代碼,根據數據在前端創建一個Dwg格式的工程剖面圖。

效果

先上效果圖 image-20230303195106114

datatodwg.gif

它支持哪些功能?

  • 支持CAD的27種實體類型的創建,如線、文字、填充等

  • 支持對DWG圖中實體進行修改、克隆、刪除等操作

  • 支持創建CAD圖層、線型、塊定義、文字樣式

  • 支持從外部圖形中拷貝實體到當前創建的CAD圖中

  • 支持塊屬性文字的創建和設置

  • 對創建好的CAD圖形數據能以GeoJson的格式在前端直接展示,同時能選中移動等操作

  • 對創建好的CAD圖形能在前端展示,同時能點擊彈出實體類型等屬性

  • 能導出成DWG圖形

實現原理

image-20230303202058333

(1) 對剖面圖中不變的元素如圖例做成模板。創建圖時直接拷貝這些實體即可。對於圖簽可以外部圖形插入,同時圖簽中需要修改的文字內容如製圖人或日期等欄位,可以塊屬性文字的方式在創建時以屬性賦值的方式來進行創建。如上面生成的剖面圖的模板來源於下麵這兩個模板圖形。 剖面圖模板:image-20230303202523238

圖簽模板:(其中單位和日期是塊屬性文字,支持插入的時候輸入屬性值進行修改)

image-20230303202615419

(2) 獲取要創建的繪圖數據,示例中對數據進行了模擬生成。

(3) 根據唯傑地圖https://vjmap.com/ SDK中提供創建CAD實體類型的方法創建相關實體。 唯傑地圖SDK支持的實體類型有DbLine直線、DbCurve曲線、Db2dPolyline二維折線、Db3dPolyline三維多段線、DbPolyline多段線、BlockReference塊參照、DbArc圓弧、DbCircle圓、DbEllipse橢圓、DbHatch填充、Text單行文本、DbMText多行文本、RasterImage柵格圖片、DbShape型實體、Spline樣條曲線、Wipeout遮罩實體、Dimension標註、Db2LineAngularDimension角度標註[兩條線]、Db3PointAngularDimension角度標註[三點]、DbAlignedDimension對齊標註、DbArcDimension圓弧標註、DbDiametricDimension直徑標註、DbOrdinateDimension坐標標註、DbRadialDimension半徑標註、DbRadialDimensionLarge半徑折線標註、DbRotatedDimension轉角標註、AcDbAttributeDefinition屬性註記、AcDbAttribute塊屬性、DbLayer圖層、DbTextStyle文字樣式、DbDimStyle標註樣式、DbLinetypeStyle線型樣式、DbBlock塊定義、DbDocument資料庫文檔。

如何減少代碼量可以用如下方法:

  • 技巧一:可以直接拷貝模板中的實體,對實體的屬性進行修改。這樣能少賦值參數,減少代碼量。

  • 技巧二:對於重覆的對象,可以創建塊,變化的文字,以塊屬性文字定義。再重覆創建塊參照,修改屬性文字。

(4) 把創建的數據生成一個JSON對象,調用唯傑地圖服務,後臺創建DWG圖形。

(5) 把後臺創建的DWG圖形數據以GeoJson數據或GIS瓦片的格式返回給前端進行展示。對於圖不大的情況,可用GeoJson數據進行展示。如果圖大時,GeoJson數據量大,數據返回慢,渲染也會受影響,這時建議用GIS柵格瓦片或矢量瓦片的時候進行繪製。

線上體驗地址

https://vjmap.com/demo/#/demo/map/comprehensive/03datatodwgmap

應用場景

能在前端通過JavaScript創建CAD格式的DWG圖形,極大的降低了數據生成CAD圖的門檻,具有很廣泛的應用場景。例如,在建築和工程領域,DWG文件是廣泛使用的標準文件格式,如工程中常用的一些等值線圖、剖面圖、水點陣圖等;建築、交通等不同行業中的相關圖紙都可以用這個來生成DWG圖形。偷個懶,讓目前很火的ChatGPT來總結下吧:

image-20230303213354454

全部實現代碼

// --數據自動生成CAD工程剖面圖--根據數據在前端創建生成CAD格式的工程剖面圖形
// 剖面圖模板來源地圖id和版本
let templateSectId = "template_sect";
let templateSecVersion = "v1";
// 圖框模板來源id和版本
const templateTkMapId = "template_tk";
const templateTkVersion = "v1";
// 註:以下所的有objectid來源方法為:
// 在唯傑雲端管理平臺 https://vjmap.com/app/cloud 裡面以記憶體方式打開模板圖,然後點擊相應實體,在屬性面板中獲取object值
// 或者以幾何渲染方式打開模板圖,點擊相應實體,在屬性面板中獲取object值,如果是塊實體(objectid中有多個_),取第一個_前面的字元串
let svc = new vjmap.Service(env.serviceUrl, env.accessToken);
// 獲取模板信息
let tplInfo;
// 獲取模板中的信息
const getTemplateInfo = async (templateSectId, version) => {
    let features = await getTemplateData(templateSectId, version);
    // 獲取所有填充符號。先獲取 填充符號 圖層中的所有文字,文字上面的hatch就是填充符號
    let hatchInfos = features.filter(f => f.layername == "填充符號" && f.name == "AcDbMText").map(t => {
        let hatch = features.filter(f => f.layername == "填充符號" && f.name == "AcDbHatch").find(h =>
            // 填充垂直方向位於文字上方,並且距離不能超過文字高度兩倍,水平方向包含文字中心點水平方向
            h.envelop.min.y > t.envelop.max.y &&
            h.envelop.min.y - t.envelop.max.y < t.envelop.height() * 2 &&
            h.envelop.min.x <= t.envelop.center().x &&
            h.envelop.max.x >= t.envelop.center().x
        )
        if (!hatch) return;
        return {
            name: t.text,
            hatchObjectId: hatch.objectid
        }
    })
    // 獲取繪製開始的位置線
    let lineInfo = features.filter(f => f.layername == "線" && f.name == "AcDbLine");
    let startLine;
    if (lineInfo.length > 0) {
        startLine = {
            objectId: lineInfo[0].objectid,
            positon: [lineInfo[0].envelop.min.x, lineInfo[0].envelop.min.y]
        }
    }
    return {
        startLine,
        hatchInfos
    }
}
​
// 模擬數據
const mockData = (hatchNames, minCount) => {
    // 對填充符號次序先隨機排序下,這樣每次生成次序就不一樣了
    hatchNames.sort(() => Math.random() - 0.5);
    let data = [];
    // 孔口個數
    let kongCount = vjmap.randInt(minCount, minCount * 2);
    for(let i = 0; i < kongCount; i++) {
        let item = {
            name: '孔' + (i + 1),
            x: 15 * (i + 1) + vjmap.randInt(0, 10) + 1000, // 孔口坐標x 生成隨機數x
            y: vjmap.randInt(100, 105), // 孔口坐標y 生成隨機數y
            stratums: [] // 分層數據
        }
        // 生成每層的信息
        let stratumCount = vjmap.randInt(5, hatchNames.length - 1);
        let stratumAllThickness = 0;
        for(let k = 0; k < stratumCount; k++) {
            const thickness = vjmap.randInt(2, 6) // 隨機生成一個厚度
            item.stratums.push({
                hatch: hatchNames[k],
                thickness: thickness
            })
            stratumAllThickness += thickness;
        }
        item.stratumsThickness = stratumAllThickness; // 所有的厚度
        data.push(item);
    }
    return data;
}
// 創建剖面圖
const createSectDoc = async (sectData) => {
    // 獲取要繪製的數據
    let drawData = sectData;
    // 獲取最大和最小值
    let minX = Math.min(...drawData.map(d => d.x));
    let maxX = Math.max(...drawData.map(d => d.x));
    let minY = Math.min(...drawData.map(d => d.y));
    let maxY = Math.max(...drawData.map(d => d.y + d.stratumsThickness));
    minY = Math.floor(minY / 10) * 10; // 往10取整,刻度以10為單位
    maxY = Math.ceil(maxY / 10) * 10 + 10; // 往10取整,刻度以10為單位,稍長點
    let posMaxX = maxX - minX + 20; //x繪製位置,相對距離從標尺偏移十個像素
    let posMinX = 10;//x繪製位置,相對距離從標尺偏移十個像素
​
    const startPoint = tplInfo.startLine.positon;
​
    let doc = new vjmap.DbDocument();
    // 數據來源
    doc.from = `${templateSectId}/${templateSecVersion}`;
​
    // 把來源圖的數據最後都清空,(這裡的模板不需要清空,直接用了)
    // doc.isClearFromDb = true;
    let entitys = [];
​
    // 左邊刻度
    entitys.push(new vjmap.DbLine({
        objectid: "169A2",
        start: startPoint,
        end: [startPoint[0], startPoint[1] + (maxY - minY)]
    }))
    for(let y = minY; y < maxY; y += 10) {
        let pt = [startPoint[0], startPoint[1] + maxY - y];
        entitys.push(new vjmap.DbLine({
            start: pt,
            end: [pt[0] - 2, pt[1]]
        }))
        // 刻度值
​
        entitys.push(new vjmap.DbText({
            cloneObjectId: '168C8',
            position: [pt[0] - 1, pt[1] + 0.2],
            text: y + ''
        }))
    }
    // 右邊刻度
    entitys.push(new vjmap.DbLine({
        cloneObjectId: "169A2", // 不是修改了,是克隆左邊的刻度線
        start: [startPoint[0] + posMaxX, startPoint[1]],
        end: [startPoint[0] + posMaxX, startPoint[1] + (maxY - minY)]
    }))
    for(let y = minY; y < maxY; y += 10) {
        let pt = [startPoint[0], startPoint[1] + maxY - y];
        entitys.push(new vjmap.DbLine({
            start: [pt[0] + posMaxX , pt[1]],
            end: [pt[0]  + posMaxX + 2, pt[1]]
        }))
        // 刻度值
        entitys.push(new vjmap.DbText({
            cloneObjectId: '168C8',
            position: [pt[0] + posMaxX + 1, pt[1] + 0.2],
            text: y + ''
        }))
    }
​
    // 修改線坐標
    entitys.push(new vjmap.DbLine({
        cloneObjectId:  tplInfo.startLine.objectId,
        start: [startPoint[0], startPoint[1]],
        end: [startPoint[0] + posMaxX, startPoint[1]]
    }))
​
​
    // 演示下塊及屬性欄位的使用,這裡用塊創建一個孔口名稱和x坐標,中間用橫線隔開
    const blockName = "nameAndx";
    let block = new vjmap.DbBlock();
    block.name = blockName;
    block.origin = [0, 0]
    block.entitys = [
        new vjmap.DbAttributeDefinition({
            position: [0, 0.2],
            contents: "名稱",
            tag: "NAME",
            colorIndex: 7, // 自動反色
            horizontalMode: vjmap.DbTextHorzMode.kTextCenter,
            verticalMode: vjmap.DbTextVertMode.kTextBottom, // kTextBottom,
            height: 0.5,
        }),
        new vjmap.DbLine({
            start: [-2, 0],
            end: [2, 0]
        }),
        new vjmap.DbAttributeDefinition({
            position: [0, -0.2],
            contents: "X坐標",
            tag: "POSX",
            colorIndex: 7, // 自動反色
            horizontalMode: vjmap.DbTextHorzMode.kTextCenter,
            verticalMode: vjmap.DbTextVertMode.kTextTop, // kTextBottom,
            height: 0.5,
        })
    ];
    doc.appendBlock(block);
    // 繪製每一個孔
    for(let i = 0; i < drawData.length; i++) {
        // 開始繪製的位置點
        let x = posMinX + drawData[i].x - minX;
        let y = startPoint[1] + maxY - drawData[i].y;
        // 名稱和x,用上面的塊創建塊參照
        let blockRef = new vjmap.DbBlockReference();
        blockRef.blockname = blockName;
        blockRef.position =  [x + 1.5,  y + 3];
        // 修改屬性定義值
        blockRef.attribute = {
            NAME: drawData[i].name,
            POSX: drawData[i].x
        }
        entitys.push(blockRef);
​
        // 一層一層繪製
        for(let k = 0; k < drawData[i].stratums.length; k++) {
            let y2 = y - drawData[i].stratums[k].thickness;
            let bounds = vjmap.GeoBounds.fromArray([x, y, x + 3, y2]);
            let points = bounds.toPointArray(); // 轉成點坐標格式
            // 閉合
            points.push(points[0]);
            // 填充
            entitys.push(new vjmap.DbHatch({
                cloneObjectId: drawData[i].stratums[k].hatch.hatchObjectId,
                points: points,
                patternScale: 1.5
            }))
            // 邊框
            entitys.push(new vjmap.Db2dPolyline({
                points: points
            }))
​
            // 繪製連接下一個孔的線
            if (i != drawData.length - 1) {
                const nextKongStratums = drawData[i + 1].stratums;
                let nextX = posMinX + drawData[i + 1].x - minX;
                let nextY = startPoint[1] + maxY - drawData[i + 1].y;
                if (k < nextKongStratums.length) {
                    for(let n = 0; n <= k; n++) {
                        nextY = nextY - drawData[i + 1].stratums[n].thickness;
                    }
                    entitys.push(new vjmap.DbLine({
                        start: [x + 3, y2],
                        end: [nextX, nextY]
                    }))
                }
                // 水平間距
                entitys.push(new vjmap.DbLine({
                    start: [x, startPoint[1]],
                    end: [x, startPoint[1] - 2]
                }))
                entitys.push(new vjmap.DbLine({
                    start: [nextX, startPoint[1]],
                    end: [nextX, startPoint[1] - 2]
                }))
                entitys.push(new vjmap.DbLine({
                    start: [x, startPoint[1] - 2],
                    end: [nextX, startPoint[1] - 2]
                }))
                // 間距值
                entitys.push(new vjmap.DbText({
                    cloneObjectId: '168C8',
                    position: [(x + nextX) / 2, startPoint[1] - 1],
                    text: nextX - x,
                    horizontalMode: vjmap.DbTextHorzMode.kTextCenter, // kTextCenter
                    verticalMode: vjmap.DbTextVertMode.kTextVertMid // kTextVertMid,
                }))
            }
            y = y2;
        }
        // 最下麵寫上累計厚度值
        entitys.push(new vjmap.DbText({
            cloneObjectId: '168C8',
            position: [x + 1.5, y - 0.2],
            text: drawData[i].stratumsThickness,
            horizontalMode: vjmap.DbTextHorzMode.kTextCenter, // kTextCenter
            verticalMode: vjmap.DbTextVertMode.kTextTop // kTextTop,
        }))
​
​
    }
​
    entitys.push(new vjmap.DbText({
        objectid: '1687C',
        position: [(posMinX + posMaxX) / 2.0, startPoint[1] + maxY - minY + 10],
        /* 如果是相對位置,可以利用矩陣
        matrix: [
            {
                op: "translation",
                vector: [相對偏移x, 相對偏移y]
            }
        ],*/
        text: `剖面圖${Date.now()}`
    }))
​
    // 繪製圖框
    let bounds = vjmap.GeoBounds.fromArray([posMinX - 20, startPoint[1] + maxY - minY + 15, posMaxX + 10, startPoint[1] - 20]);
    let labelPos = [bounds.max.x, bounds.min.y];
    let points = bounds.toPointArray(); // 轉成點坐標格式
    // 閉合
    points.push(points[0]);
    // 邊框
    entitys.push(new vjmap.Db2dPolyline({
        points: points
    }))
    bounds = bounds.scale(1.02);
    points = bounds.toPointArray(); // 轉成點坐標格式
    // 閉合
    points.push(points[0]);
    // 邊框
    entitys.push(new vjmap.Db2dPolyline({
        points: points,
        lineWidth: 30 // mm
    }))
​
    let date = new Date();
    // 圖框從其他模板插入,並修改塊屬性文字
    entitys.push(new vjmap.DbBlockReference({
        cloneObjectId: '6A1',
        cloneFromDb: `${templateTkMapId}/${templateTkVersion}`,
        position: labelPos,
        attribute: {
            // 修改塊中的屬性欄位
            DATETIME: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`,
            COMPANY: {
                text: "唯傑地圖VJMAP",
                color: 0x00FFFF
            }
        }
    }))
​
    entitys.push(new vjmap.DbLine({
        objectid: "168C8", // 這個模板文字不用了,直接刪除了
        delete: true
    }))
    doc.entitys = entitys;
    return doc;
}
​
​
// 先得設置一個要圖形的所有範圍,這個範圍是隨便都沒有有關係的。最後導出dwg時,會根據實體的所有坐標去自動計算真實的範圍。
let mapBounds = '[-10000,-10000,10000,10000]'
let mapExtent = vjmap.GeoBounds.fromString(mapBounds);
mapExtent = mapExtent.square(); // 要轉成正方形
​
svc.setCurrentMapParam({
    darkMode: true, // 由於沒有打開過圖,所以主動設置黑色模式
    bounds: mapExtent.toString()
})
// 建立坐標系
let prj = new vjmap.GeoProjection(mapExtent);
​
// 新建地圖對象
let map = new vjmap.Map({
    container: 'map', // container ID
    style: {
        version: svc.styleVersion(),
        glyphs: svc.glyphsUrl(),
        sources: {},
        layers: []
    },// 矢量瓦片樣式
    center: [0,0], // 中心點
    zoom: 2,
    renderWorldCopies: false
});
// 地圖關聯服務對象和坐標系
map.attach(svc, prj);
​
// 使地圖全部可見
map.fitMapBounds();
await map.onLoad();
​
​
// 創建一個幾何對象
const createGeomData = async (map, doc) => {
    let svc = map.getService();
    let res = await svc.cmdCreateEntitiesGeomData({
        filedoc: doc.toDoc()
    });
    if (res.error) {
        message.error(res.error);
        return {
            type: "FeatureCollection",
            features: []
        };
    }
    if (res.metadata && res.metadata.mapBounds) {
        // 如果返回的元數據裡面有當前地圖的範圍,則更新當前地圖的坐標範圍
        map.updateMapExtent(res.metadata.mapBounds);
    }
​
    const features = [];
    if (res && res.result && res.result.length > 0) {
        for (let ent of res.result) {
            if (ent.geom && ent.geom.geometries) {
                let clr = map.entColorToHtmlColor(ent.color); // 實體顏色轉html顏色
                let featureAttr = {};
                // 因為要組合成一個組合實體,所以線和多邊形的顏色得區分
                if (ent.isPolygon) {
                    featureAttr.color = clr; // 填充色,只對多邊形有效
                    featureAttr.noneOutline = true; // 不顯示多邊形邊框,只對多邊形有效
                } else {
                    featureAttr.color = clr; // 顏色
                    featureAttr.line_width = ent.lineWidth; // 線寬
                }
                let ft = {
                    id: vjmap.RandomID(10),
                    type: "Feature",
                    properties: {
                        objectid: ent.objectid,
                        opacity: ent.alpha / 255,
                        ...featureAttr,
                    }
                }
                if (ent.geom.geometries.length == 1) {
                    features.push({
                        ...ft,
                        geometry: ent.geom.geometries[0],
                    });
                } else {
                    features.push({
                        ...ft,
                        geometry: {
                            geometries: ent.geom.geometries,
                            type: "GeometryCollection"
                        },
                    });
                }
​
            }
        }
    }
    return {
        type: "FeatureCollection",
        features: features,
    };
};
​
// 清空之前的地圖數據
const clearMapData = () => {
    svc.setCurrentMapParam({
        darkMode: true, // 由於沒有打開過圖,所以主動設置黑色模式
        bounds: mapExtent.toString()
    })
    map.disableLayerClickHighlight();
    map.removeDrawLayer();
    let sources = map.getStyle().sources;
    for(let source in sources) {
        map.removeSourceEx(source);
    }
}
​
// 創建一個有數據的地圖
const createDataMap = async (doc) => {
    clearMapData();
    let geojson = await createGeomData(map, doc);
    const opts = vjmap.Draw.defaultOptions();
    // 修改預設樣式,把點的半徑改成1,沒有邊框,預設為5
    let pointIdx = opts.styles.findIndex(s => s.id === "gl-draw-point-point-stroke-inactive");
    if (pointIdx >= 0) {
        opts.styles[pointIdx]['paint']['circle-radius'][3][3] = 0
    }
    pointIdx = opts.styles.findIndex(s => s.id === "gl-draw-point-inactive");
    if (pointIdx >= 0) {
        opts.styles[pointIdx]['paint']['circle-radius'][3][3] = 1
    }
    map.getDrawLayer(opts).set(geojson);
}
​
// 創建一個dwg的地圖
const createDwgMap = async (doc) => {
    // 先清空之前繪製的
    clearMapData();
    // js代碼
    let res = await svc.updateMap({
        // 獲取一個臨時的圖id(臨時圖形只會用臨時查看,過期會自動刪除)
        mapid: vjmap.getTempMapId(1), // 臨時圖形不瀏覽情況下過期自動刪除時間,單位分鐘。預設30
        filedoc: doc.toDoc(),
        mapopenway: vjmap.MapOpenWay.Memory,
        style: {
            backcolor: 0 // 如果div背景色是淺色,則設置為oxFFFFFF
        }
    })
    if (res.error) {
        message.error(res.error)
    }
    await map.switchMap(res);
}
​
​
let curDoc;
const exportDwgOpen = async () => {
    if (!curDoc) return;
    const mapid = 'exportdwgmap';
    let res = await svc.updateMap({
        mapid: mapid,
        filedoc: curDoc.toDoc(),
        mapopenway: vjmap.MapOpenWay.Memory,
        style: {
            backcolor: 0 // 如果div背景色是淺色,則設置為oxFFFFFF
        }
    })
    if (res.error) {
        message.error(res.error)
    } else{
        window.open(`https://vjmap.com/app/cloud/#/map/${res.mapid}?version=${res.version}&mapopenway=Memory&vector=false`)
    }
}
​
// 獲取模板的所有數據
const getTemplateData = async (mapid, version) => {
    let res = await svc.rectQueryFeature({
        mapid,
        version,
        fields: "",
        geom: false, // 以記憶體方式打開,獲取真正的objectid
        maxGeomBytesSize: 0, // 不需要幾何坐標
        useCache: true, // 因為是以記憶體方式打開,後臺先把查詢的數據保存進緩存,下次直接去緩存查找,提高效率
        // x1,y1,x2,y2同時不輸的話,表示是查詢整個圖的範圍  這範圍不輸入,表示是全圖範圍
    })
    // 把實體的範圍字元串轉成對象
    res.result.map(f => f.envelop = vjmap.GeoBounds.fromString(f.bounds));
    console.log(res.result)
    return res.result;
}
​
​
const creatSectDataMap = async () => {
    let sectData = mockData(tplInfo.hatchInfos, 5);
    const doc = await createSectDoc(sectData);
    await createDataMap(doc);
    map.fitMapBounds();
    curDoc = doc;
}
const creatSectDwgMap = async () => {
    let sectData = mockData(tplInfo.hatchInfos, 15);
    const doc = await createSectDoc(sectData);
    await createDwgMap(doc);
    map.fitMapBounds();
    // 點擊有高亮狀態(滑鼠點擊地圖元素上時,會高亮)
    map.enableLayerClickHighlight(svc, e => {
        if (!e) return;
        let msg = {
            content: `type: ${e.name}, id: ${e.objectid}, layer: ${e.layerindex}`,
            key: "layerclick",
            duration: 5
        }
        e && message.info(msg);
    })
    curDoc = doc;
}
// 先獲取模板信息
tplInfo = await getTemplateInfo(templateSectId, templateSecVersion);
// 隨機生成一個剖面圖
creatSectDataMap();
// UI界面
const App = () => {
    return (
        <div>
            <div className="info" style={{width: '430px'}}>
                <div className="input-item">
                    <button className="btn btn-full mr0" onClick={creatSectDataMap}>隨機生成一個剖面圖[前端直接繪製,適合於生成圖不大的情況]</button>
                    <button className="btn btn-full mr0" onClick={creatSectDwgMap}>隨機生成剖面圖[後臺生成DWG前端展示,適合於生成圖大的情況]</button>
                    <button className="btn btn-full mr0" onClick={exportDwgOpen}>導出成DWG圖並打開</button>
                </div>
            </div>
        </div>
    );
}
ReactDOM.render(<App />, document.getElementById('ui'));
​
​
const mousePositionControl = new vjmap.MousePositionControl();
map.addControl(mousePositionControl, "bottom-left");

  


 
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 以下代碼的功能是用戶可以實現業務中用戶虛擬錢包的錢提現到用戶銀行卡,其實本質上是把商戶的賬戶資金劃轉到用戶銀行卡,其實就是銀行轉賬,相關代碼如下: ///此方法存在部分業務代碼,核心方法是doProcess方法且基本是可以復用的,其它的代碼可以根據自身業務場景靈活處理;public async Ta ...
  • 前言 記得在360實習的時候,聽到過一句話,大部分的安全問題並不是黑客造成的,而是內部人員。 epbf可以避免代碼侵入,像樁子一樣監控應用,同時也帶來了一些安全問題。 下麵簡單演示一下如何利用bpftrace竊取Mysql的賬號密碼。 步驟 Go程式 這個程式只是利用xorm連接資料庫,然後判斷st ...
  • 1、工作應用場景 統計得到每個小時的UV、PV、IP的個數,構建如下表結構: 但是表中數據的存儲格式不利於直接查詢展示,需要進行調整:(以時間分區,去重、聚合等……對結果進行行列轉換) 2、行轉列 (1)多行轉多列 case when函數 功能:用於實現對數據的判斷,根據條件,不同的情況返回不同的結 ...
  • 依賴的類: 1 "use strict"; 2 3 var __emptyPoint = null, __emptyPointA = null; 4 5 const ColorRefTable = { 6 "aliceblue": "#f0f8ff", 7 "antiquewhite": "#fae ...
  • 初探富文本之CRDT協同實例 在前邊初探富文本之CRDT協同演算法一文中我們探討了為什麼需要協同、分散式的最終一致性理論、偏序集與半格的概念、為什麼需要有偏序關係、如何通過數據結構避免衝突、分散式系統如何進行同步調度等等,這些屬於完成協同所需要瞭解的基礎知識,實際上當前有很多成熟的協同實現,例如aut ...
  • 功能介紹 平臺採用B/S結構,後端採用主流的Python語言進行開發,前端採用主流的Vue.js進行開發。 整個平臺包括前臺和後臺兩個部分。 前臺功能包括:首頁、圖書詳情頁、用戶中心模塊。 後臺功能包括:總覽、借閱管理、圖書管理、分類管理、標簽管理、評論管理、用戶管理、運營管理、日誌管理、系統信息模 ...
  • Canvas var canvas = document.querySelector('.myCanvas'); var width = canvas.width = window.innerWidth; var height = canvas.height = window.innerHeight ...
  • 離職在家 閑來無事看最近ChatGPT很火 花了一天時間 用Uniapp寫了一個小程式端的ChatGPT,實在是看不慣~ 一大堆利用ChatGPT收費的應用(小程式) 整個開源的玩玩 另外主要是自己本身是後端開發人員~ 剛好用uniapp實踐一下小程式開發 先看看主頁面效果圖吧 演示視頻-> 做一個 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...