| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634 |
- import * as THREE from "../../libs/three.js/build/three.module.js";
- import {Line2} from "../../libs/three.js/lines/Line2.js";
- import {LineGeometry} from "../../libs/three.js/lines/LineGeometry.js";
- import {LineMaterial} from "../../libs/three.js/lines/LineMaterial.js";
- import {LineDraw} from "../custom/utils/DrawUtil.js";
-
- /* proj4.defs("CGCS2000","+proj=tmerc +lat_0=0 +lon_0=114 +k=1 +x_0=500000 +y_0=0 +ellps=WGS84 +units=m +no_defs")
-
- function transformCGCS2000ToWGS84([lng, lat]) {
- // Define the CGCS2000 and WGS84 coordinate systems
- const transformed = proj4("CGCS2000", "LOCAL", [lng, lat]);
- return transformed
- }
- */
- export class ShapefileLoader{
- constructor(){
- //this.transform = null;
- }
- async load(path, color){
- const matLine = new LineMaterial( {
- color: color || 0xff0000,
- lineWidth: 3, // in pixels
- resolution: new THREE.Vector2(1000, 1000),
- dashed: false
- } );
-
- const features = await this.loadShapefileFeatures(path);
- if(!features){
- console.error('no features', path)
- return
- }
- const node = new THREE.Object3D();
-
-
- let transform = await this.loadProj(path, node);
- if(!transform){
- transform = this.analyseTransform(features, path, node ) //自己根据数字来猜测,经纬度一般能准,但数字很大的话就不对了。用户自己移动吧
- }
-
-
- let jump = 0
- for(const feature of features){//5是碎的
- //if(feature.geometry.type!= 'MultiLineString' || ++jump != 5) continue
- const fnode = this.featureToSceneNode(feature, matLine, transform);
- fnode && node.add(fnode);
- }
- let setResolution = (x, y) => {
- matLine.resolution.set(x, y);
- };
- const result = {
- features: features,
- node: node,
- setResolution: setResolution,
- };
- return result;
- }
-
- featureToSceneNode(feature, matLine, transform){
- //console.log(feature)
-
- let geometry = feature.geometry;
-
- let color = new THREE.Color(1, 1, 1);
-
- if(!transform){
- transform = (v)=> v //{forward: (v) => v};
- }
-
- if(geometry.type === "Point"){
- let sg = new THREE.SphereGeometry(1, 18, 18);
- let sm = new THREE.MeshNormalMaterial();
- let s = new THREE.Mesh(sg, sm);
-
- let [long, lat] = geometry.coordinates;
- let pos = transform/* .forward */([long, lat]);
-
- s.position.set(...pos, 20);
-
- s.scale.set(10, 10, 10);
-
- return s;
- }else if(geometry.type === "LineString"){
- let coordinates = [];
-
- let min = new THREE.Vector3(Infinity, Infinity, Infinity);
- for(let i = 0; i < geometry.coordinates.length; i++){
- let [long, lat] = geometry.coordinates[i];
- let pos = transform/* .forward */([long, lat]);
-
- min.x = Math.min(min.x, pos[0]);
- min.y = Math.min(min.y, pos[1]);
- min.z = Math.min(min.z, 20);
-
- coordinates.push(...pos, 20);
- if(i > 0 && i < geometry.coordinates.length - 1){
- coordinates.push(...pos, 20);
- }
- }
-
- for(let i = 0; i < coordinates.length; i += 3){
- coordinates[i+0] -= min.x;
- coordinates[i+1] -= min.y;
- coordinates[i+2] -= min.z;
- }
-
- const lineGeometry = new LineGeometry();
- lineGeometry.setPositions( coordinates );
- const line = new Line2( lineGeometry, matLine );
- line.computeLineDistances();
- line.scale.set( 1, 1, 1 );
- line.position.copy(min);
-
- return line;
- }else if(geometry.type === "MultiLineString"){//xzw add 江门的那个文件
- let coordinates = [];
- //console.warn('MultiLineString') //多组连续线段,组之间不连续
- let min = new THREE.Vector3(Infinity, Infinity, Infinity);
- for(let i = 0; i < geometry.coordinates.length; i++){
- let points = geometry.coordinates[i]; //有时候是两个点有时候多个
- let coordinateSlice = []
- points.forEach(point=>{
- let [long, lat] = point;
- let pos = transform/* .forward */([long, lat]);
- min.x = Math.min(min.x, pos[0]);
- min.y = Math.min(min.y, pos[1]);
- min.z = Math.min(min.z, 20);
- coordinateSlice.push(new THREE.Vector3(pos[0], pos[1], 20));
- })
- coordinates.push(coordinateSlice)
- }
-
- coordinates.forEach(coordinateSlice=> coordinateSlice.forEach(point=>point.sub(min) ))
-
- let line = LineDraw.createFatLine(coordinates,{
- uncontinuous: true,
- mat:matLine
- /* color: 0xff0000,
- dashSize: 0.5,
- gapSize: 0.2,
- lineWidth: config$1.measure.lineWidth, */
- });
- line.position.copy(min);
-
- return line;
- }else if(geometry.type === "Polygon"){
- for(let pc of geometry.coordinates){
- let coordinates = [];
-
- let min = new THREE.Vector3(Infinity, Infinity, Infinity);
- for(let i = 0; i < pc.length; i++){
- let [long, lat] = pc[i];
- let pos = transform/* .forward */([long, lat]);
-
- min.x = Math.min(min.x, pos[0]);
- min.y = Math.min(min.y, pos[1]);
- min.z = Math.min(min.z, 20);
-
- coordinates.push(...pos, 20);
- if(i > 0 && i < pc.length - 1){
- coordinates.push(...pos, 20);
- }
- }
-
- for(let i = 0; i < coordinates.length; i += 3){
- coordinates[i+0] -= min.x;
- coordinates[i+1] -= min.y;
- coordinates[i+2] -= min.z;
- }
- const lineGeometry = new LineGeometry();
- lineGeometry.setPositions( coordinates );
- const line = new Line2( lineGeometry, matLine );
- line.computeLineDistances();
- line.scale.set( 1, 1, 1 );
- line.position.copy(min);
-
- return line;
- }
- }else{
- console.log("unhandled feature: ", geometry.type);
- }
-
-
-
-
-
- function getLine(coordinates){
-
- }
- }
- async loadShapefileFeatures(file){
- let features = [];
- if(file.split('.').pop() == 'shp'){
- let source = await shapefile.open(file);
-
- while(true){
- let result = await source.read();
- if (result.done) {
- break;
- }
- //value中的properties来自dbf,但该属性没有被用到,是否可以不加载
- if (result.value && result.value.type === 'Feature' && result.value.geometry !== undefined) {
- features.push(result.value);
- }
- }
- return features;
- }else if(file.split('.').pop() == 'json'){
- return new Promise((resolve,reject)=>{
- Potree.loadFile(file, { } , (data)=>{
- console.log(data);
- resolve(data.features)
- });
- })
-
- }
-
- }
-
-
-
-
- analyseTransform(features, path, node){//xzw只能自己暂时判断下 ,最好能根据.prj判断
-
- if(viewer.transform){
- let count = 0, maxCount = 100
- let notLonLat, transform
- let prjJson = node.prjJson
-
-
- if(prjJson && prjJson.unit ){
- if(prjJson.unit.name.toLowerCase() == 'meter' ){//'degree'
- notLonLat = true
- }
- }else{
- let judge = (coord)=>{
- if(!(coord[0] >= -180 && coord[0] <= 180 && coord[1] >= -90 && coord[1] <=90 )){//如果超出经纬度范围,那就不是经纬度
- notLonLat = true; node.prjNotSure = true //不确定,不设定默认位置
-
- }
- count++
- }//数字很小的话一般都是经纬度吧,就当作确定了
-
- for(let feature of features){
- if(count > maxCount)break
- for(let arr of feature.geometry.coordinates){
-
- if(arr[0] instanceof Array){
- for(let coord of arr){
- judge(coord)
- if(count > maxCount || notLonLat)break
- }
- }else{
- judge(arr)
- if(count > maxCount || notLonLat)break
- }
-
-
- }
- }
-
- }
- if(notLonLat){
- /* transform = (v)=>{
- let a = viewer.transform.lonlatTo4550.inverse(v) //这种类型和不使用transform很接近
- return viewer.transform.lonlatToLocal.forward(a)
- } */
- console.log('该shape不使用transform', path.split('/').pop())
- }else{
- transform = viewer.transform.lonlatToLocal.forward
- console.log('根据坐标数值猜测,该shape使用经纬度', path.split('/').pop())
- }
- return transform
- //loaders.shapeLoader.transform = viewer.transform.lonlatToLocal.forward;
- }else{
- node.prjNotSure = true
- console.log('该shape没用任何transform 因为场景没有设置经纬度', path.split('/').pop() )
- }
- }
-
-
-
-
- async loadProj(path, model){
- let a = path.split('/')
- let name = a.pop().split('.')[0]
- path = a.join('/') + '/' + name + '.prj';
-
-
-
- return new Promise((resolve,reject)=>{
- Potree.loadFile(path,{returnText:true},(e)=>{
-
- name += '_proj'
- let projDef = parsePrjToProj4(e, model)
- proj4.defs(name, projDef)
- console.log('loadedProj:', name, '解析得def: ', projDef, '原:', e, 'json:', model.prjJson)
-
- try{
- let proj = proj4(name, "LOCAL")
-
- let transform = ([lng, lat])=>{
- return proj.forward([lng, lat]);
- }
- resolve(transform)
- }catch(e){
- console.warn(e, '建立proj4 transform 失败', name )
- resolve()
- }
-
- },()=>{
- resolve()
- })
-
- })
-
- return transform
-
- }
- };
- function parsePrjToProj4(prjString, model) {//将prj的内容变为proj4的transform
-
- let def = ''
- let json = model.prjJson = prjToJson(prjString)
-
- let projectionType
- let datum = '';
- let ellipsoid = '';
- let centralLon = '0', centralLat = '0';
- let scaleFactor = '1';
- let falseEasting = '0', falseNorthing = '0';
-
-
-
-
-
- if(json.projection == 'Transverse_Mercator'){
- projectionType = 'tmerc'
- }else if(!json.projection){
- projectionType = 'longlat'
- }else{
- projectionType = projection
- console.log('unknown projection:', json.projection)
- }
-
-
-
- if(json.parameters){
- falseEasting = json.parameters.false_easting
- falseNorthing = json.parameters.false_northing
- centralLon = json.parameters.central_meridian
- centralLat = json.parameters.latitude_of_origin
- scaleFactor = json.parameters.scale_factor
- }
-
- if(json.geogcs?.spheroid){
- let spheroid = json.geogcs.spheroid.name.toLowerCase().replace('_', '')
- if(spheroid.includes('wgs84') || spheroid.includes('wgs1984') )ellipsoid = 'WGS84'
- }
-
-
- def += `+proj=${projectionType} `
- def += `+lat_0=${centralLat} `;
- def += `+lon_0=${centralLon} `;
- def += `+k=${scaleFactor} `;
- def += `+x_0=${falseEasting} `;
- def += `+y_0=${falseNorthing} `;
- ellipsoid && (def += `+ellps=${ellipsoid} `);
- def += '+units=m +no_defs';
- return def
-
- }
- function prjToJson(prjString) {//将prj的内容变为json
- const result = {};
- // 提取 PROJCS 和 GEOGCS 信息
- const projcsMatch = prjString.match(/PROJCS\["([^"]+)",(.*)\]/);
- if (projcsMatch) {
- result.projcsName = projcsMatch[1];
- const geoPart = projcsMatch[2];
-
- // 解析 GEOGCS 部分
- const geoMatch = geoPart.match(/GEOGCS\["([^"]+)",DATUM\["([^"]+)",SPHEROID\["([^"]+)",([0-9.]+),([0-9.]+)\]\],PRIMEM\["([^"]+)",([0-9.]+)\],UNIT\["([^"]+)",([0-9.]+)\]/);
- if (geoMatch) {
- result.geogcs = {
- name: geoMatch[1],
- datum: geoMatch[2],
- spheroid: {
- name: geoMatch[3],
- semiMajorAxis: parseFloat(geoMatch[4]),
- inverseFlattening: parseFloat(geoMatch[5])
- },
- primeMeridian: {
- name: geoMatch[6],
- value: parseFloat(geoMatch[7])
- },
- unit: {
- name: geoMatch[8],
- conversionFactor: parseFloat(geoMatch[9])
- }
- };
- }
- // 解析 PROJECTION 部分
- const projMatch = geoPart.match(/PROJECTION\["([^"]+)"\]/);
- if (projMatch) {
- result.projection = projMatch[1];
- }
- // 解析 PARAMETER 部分
- const parameters = {};
- const paramMatches = geoPart.matchAll(/PARAMETER\["([^"]+)",([-\d.]+)\]/g);
- for (const match of paramMatches) {
- parameters[match[1]] = parseFloat(match[2]);
- }
- result.parameters = parameters;
- // 解析 UNIT 部分
- const unitMatch = geoPart.match(/UNIT\["([^"]+)",([-\d.]+)\]/);
- if (unitMatch) {
- result.unit = {
- name: unitMatch[1],
- conversionFactor: parseFloat(unitMatch[2])
- };
- }
- } else {
- // 如果没有 PROJCS,则尝试解析 GEOGCS
- const geoMatch = prjString.match(/GEOGCS\["([^"]+)",DATUM\["([^"]+)",SPHEROID\["([^"]+)",([0-9.]+),([0-9.]+)\]\],PRIMEM\["([^"]+)",([0-9.]+)\],UNIT\["([^"]+)",([0-9.]+)\]/);
- if (geoMatch) {
- result.geogcs = {
- name: geoMatch[1],
- datum: geoMatch[2],
- spheroid: {
- name: geoMatch[3],
- semiMajorAxis: parseFloat(geoMatch[4]),
- inverseFlattening: parseFloat(geoMatch[5])
- },
- primeMeridian: {
- name: geoMatch[6],
- value: parseFloat(geoMatch[7])
- },
- unit: {
- name: geoMatch[8],
- conversionFactor: parseFloat(geoMatch[9])
- }
- };
- }
- }
- return result;
- }
- /*
-
- function parsePrjToProj4(prjString) {
- // 初始化 Proj4 字符串的部分
- let proj4Def = ''
- // 提取关键信息
- const prjParts = prjString.match(/(\w+)\[(.*?)\]/g);
-
- let projectionType = '';
- let datum = '';
- let ellipsoid = '';
- let centralMeridian = '';
- let scaleFactor = '1';
- let falseEasting = '0';
- let falseNorthing = '0';
- let latitudeOfOrigin = '0';
- prjParts.forEach(part => {
- if (part.includes('PROJECTION')) {
- projectionType = part.match(/"([^"]+)"/)[1].toLowerCase().replace('_', '');
- //xzw:
- if(projectionType == 'transversemercator') projectionType = 'tmerc'
-
- }else if (part.includes('DATUM')) {
- datum = part.match(/"([^"]+)"/)[1];
- } else if (part.includes('SPHEROID')) {
- ellipsoid = part.match(/"([^"]+)"/)[1].toLowerCase();
- } else if (part.includes('PARAMETER')) {
- let paramName = part.match(/"([^"]+)"/)[1].toLowerCase().replace(/ /g, '_');
- let paramValue = part.match(/(-?\d+\.?\d*)/)[1];
- switch (paramName) {
- case 'central_meridian':
- centralMeridian = paramValue;
- break;
- case 'scale_factor':
- scaleFactor = paramValue;
- break;
- case 'false_easting':
- falseEasting = paramValue;
- break;
- case 'false_northing':
- falseNorthing = paramValue;
- break;
- case 'latitude_of_origin':
- latitudeOfOrigin = paramValue;
- break;
- }
- }
- });
- // 构建完整的 Proj4 定义字符串
- if(!projectionType && prjString.includes('GCS_WGS_1984')) projectionType = 'longlat'
-
-
- projectionType && (proj4Def += `+proj=${projectionType} `)
- proj4Def += `+lat_0=${latitudeOfOrigin} `;
- proj4Def += `+lon_0=${centralMeridian} `;
- proj4Def += `+k=${scaleFactor} `;
- proj4Def += `+x_0=${falseEasting} `;
- proj4Def += `+y_0=${falseNorthing} `;
- ellipsoid && (proj4Def += `+ellps=${ellipsoid} `);
- proj4Def += '+units=m +no_defs';
- return proj4Def;
- }
- */
- /*
- function parsePrjToProj4(prjString) {
- // 初始化 Proj4 参数对象
- let proj4Params = {};
-
- // 提取 PROJCS 和 GEOGCS 部分
- const projcsMatch = prjString.match(/PROJCS\["([^"]+)",(.*)\]/);
- if (projcsMatch){
-
- const projcsName = projcsMatch[1];
- const geoPart = projcsMatch[2];
- // 解析 GEOGCS 部分
- const datumMatch = geoPart.match(/DATUM\["([^"]+)",SPHEROID\["([^"]+)",([0-9.]+),([0-9.]+)\]\]/);
- if (datumMatch) {
- proj4Params.datum = datumMatch[1].toLowerCase();
- proj4Params.ellps = datumMatch[2].toLowerCase();
- proj4Params.a = parseFloat(datumMatch[3]); // Semi-major axis
- proj4Params.rf = parseFloat(datumMatch[4]); // Inverse flattening
- }
- // 解析 PROJECTION 部分
- const projMatch = geoPart.match(/PROJECTION\["([^"]+)"\]/);
- if (projMatch) {
- proj4Params.proj = projMatch[1].toLowerCase().replace(/_/g, '');
- }
- // 解析 PARAMETER 部分
- const paramMatches = geoPart.matchAll(/PARAMETER\["([^"]+)",([-\d.]+)\]/g);
- for (const match of paramMatches) {
- const paramName = match[1].toLowerCase().replace(/ /g, '_');
- const paramValue = parseFloat(match[2]);
- switch (paramName) {
- case 'false_easting':
- proj4Params.x_0 = paramValue;
- break;
- case 'false_northing':
- proj4Params.y_0 = paramValue;
- break;
- case 'central_meridian':
- proj4Params.lon_0 = paramValue;
- break;
- case 'scale_factor':
- proj4Params.k = paramValue;
- break;
- case 'latitude_of_origin':
- proj4Params.lat_0 = paramValue;
- break;
- }
- }
- // 设置单位
- proj4Params.units = "m"; // 默认米
- proj4Params.no_defs = true;
- // 构建 Proj4 字符串
- let proj4Def = '+proj=' + proj4Params.proj;
- proj4Def += ' +lat_0=' + proj4Params.lat_0;
- proj4Def += ' +lon_0=' + proj4Params.lon_0;
- proj4Def += ' +k=' + proj4Params.k;
- proj4Def += ' +x_0=' + proj4Params.x_0;
- proj4Def += ' +y_0=' + proj4Params.y_0;
- proj4Def += ' +ellps=' + proj4Params.ellps;
- proj4Def += ' +units=' + proj4Params.units;
- proj4Def += ' +no_defs';
- return proj4Def;
- }
- */
-
- //shp必须,dbf目前没用,prj最好有
-
|