[JAVA] Essayez de créer un modèle tridimensionnel (format PLY) à partir de la carte topographique numérique DSM de toute la préfecture de Hyogo

1. Vue d'ensemble

Le week-end dernier, la préfecture de Hyogo a publié un ensemble de données 3D maillées de 1 m pour toutes les préfectures. Les données publiques sont DSM, DEM, CS dessin stéréoscopique, la licence est C.C.4.0, "Ce sont des données qui peuvent être utilisées secondairement à n'importe quelle fin, veuillez donc les utiliser à diverses fins."

En tant qu'ensemble de données en trois dimensions qui a été publié jusqu'à présent, il existe un modèle numérique d'élévation (DEM de 5 m de maille) du National Land Research Institute: Basic Map Information Site. Cependant, la carte topographique numérique de toute la préfecture de Hyogo a 25 fois la densité de données, et en plus du DEM, le DSM comprenant les bâtiments et les forêts est ouvert au public. J'ai tout de suite voulu m'en servir pour créer un modèle tridimensionnel de villes et de forêts. snapshot00.png

2. Que faire cette fois

Le DSM publié dans la préfecture de Hyogo est un ensemble de données composé de coordonnées x, y et de valeurs d'élévation du système de coordonnées orthogonales du 5ème plan. Je veux des informations de couleur pour créer un modèle en trois dimensions. Pour cette raison, tout d'abord, Tuile de l'Institut de géographie: [Dernières photos nationales](https://maps.gsi.go.jp/development/ ichiran.html # seamlessphoto) Obtenez les informations de couleur de chaque maillage à partir de la mosaïque d'image et créez un outil pour convertir des données au format DSM en XYZ RVB. Ensuite, créez un outil qui génère une face à partir des données de sommet de XYZRGB et génère un modèle tridimensionnel au format PLY. Pour les spécifications du format PLY, je me suis référé au site suivant.

3. Implémentation de l'outil de conversion DSM-> XYZ RGB

(1) Flux de processus

Le flux de traitement est le suivant.

  1. Inspectez les coordonnées x et y du DSM (5e système de coordonnées orthogonales du plan) et acquérez l'aire du groupe de points DSM. Faire.
  2. Convertissez la zone du groupe de points DSM en une zone rectangulaire de latitude et de longitude.
  3. Convertissez la zone rectangulaire de latitude et de longitude en coordonnées de pixels et saisissez les coordonnées de tuile à acquérir.
  4. Récupérez la [Tuile d'image](https://maps.gsi.go.jp/development/ichiran.html#s Seamlessphoto) des coordonnées correspondant à DSM et concaténer.
  5. Convertissez les coordonnées du groupe de points DSM en coordonnées de pixels et obtenez les informations de couleur du pixel correspondant à partir de l '[Image de photographie aérienne] acquise (https://maps.gsi.go.jp/development/ichiran.html#s Seamlessphoto). Faire.
  6. Sortie dans un fichier texte au format "x y z r g b".

(2) Implémentation de la classe de conversion de coordonnées (système de coordonnées orthogonales plan <-> latitude / longitude)

Tout d'abord, implémentez une classe qui convertit les coordonnées xy du système de coordonnées orthogonales du plan en latitude et longitude. "Une méthode de calcul plus simple pour la conversion des coordonnées entre les coordonnées de latitude et de longitude et les coordonnées orthogonales du plan dans la projection de Gauss-Krüger », Et a créé la classe suivante.

LonlatXYT.java


import java.awt.geom.Point2D;

public class LonLatXY {
	private static final double a=6378137;
	private static final double rf=298.257222101;
	private static final double m0=0.9999;
	private static final double s2r=Math.PI/648000;
	private static final double n=0.5/(rf-0.5);
	private static final double n15=1.5*n;
	private static final double anh=0.5*a/(1+n);
	private static final double nsq=n*n;
	private static final double e2n=2*Math.sqrt(n)/(1+n);
	private static final double ra=2*anh*m0*(1+nsq/4+nsq*nsq/64);
	private static int jt=5;
	private static int jt2=2*jt;
	private static double ep=1.0;
	private static double[] e=getE();
	private static final double[] phi0=new double[]{0,33,33,36,33,36,36,36,36,36,40,44,44,44,26,26,26,26,20,26};
	private static final double[] lmbd0=new double[]{0,7770,7860,7930,8010,8060,8160,8230,8310,8390,8450,8415,8535,8655,8520,7650,7440,7860,8160,9240};
	private static double[] alp=getAlp();
	private static double[] beta=getBeta();
	private static double[] dlt=getDlt();


	private static double[] getAlp(){
		double[] alp=new double[6];
		alp[1]=(1.0/2.0+(-2.0/3.0+(5.0/16.0+(41.0/180.0-127.0/288.0*n)*n)*n)*n)*n;
		alp[2]=(13.0/48.0+(-3.0/5.0+(557.0/1440.0+281.0/630.0*n)*n)*n)*nsq;
		alp[3]=(61.0/240.0+(-103.0/140.0+15061.0/26880.0*n)*n)*n*nsq;
		alp[4]=(49561.0/161280.0-179.0/168.0*n)*nsq*nsq;
		alp[5]=34729.0/80640.0*n*nsq*nsq;
		return alp;
	}

	private static double[] getBeta(){
		double[] beta=new double[6];
		beta[1]=(1.0/2.0+(-2.0/3.0+(37.0/96.0+(-1.0/360.0-81.0/512.0*n)*n)*n)*n)*n;
		beta[2]=(1.0/48.0+(1.0/15.0+(-437.0/1440.0+46.0/105.0*n)*n)*n)*nsq;
		beta[3]=(17.0/480.0+(-37.0/840.0-209.0/4480.0*n)*n)*n*nsq;
		beta[4]=(4397.0/161280.0-11.0/504.0*n)*nsq*nsq;
		beta[5]=4583.0/161280.0*n*nsq*nsq;
		return beta;
	}

	private static double[] getDlt(){
		double[] dlt=new double[7];
		dlt[1]=(2.0+(-2.0/3.0+(-2.0+(116.0/45.0+(26.0/45.0-2854.0/675.0*n)*n)*n)*n)*n)*n;
		dlt[2]=(7.0/3.0+(-8.0/5.0+(-227.0/45.0+(2704.0/315.0+2323.0/945.0*n)*n)*n)*n)*nsq;
		dlt[3]=(56.0/15.0+(-136.0/35.0+(-1262.0/105.0+73814.0/2835.0*n)*n)*n)*n*nsq;
		dlt[4]=(4279.0/630.0+(-332.0/35.0-399572.0/14175.0*n)*n)*nsq*nsq;
		dlt[5]=(4174.0/315.0-144838.0/6237.0*n)*n*nsq*nsq;
		dlt[6]=601676.0/22275.0*nsq*nsq*nsq;
		return dlt;
	}

	private static double[] getE(){
		double[] e=new double[jt2+2];
		for(int k=1;k<=jt;k++){
			ep*=e[k]=n15/k-n;
			e[k+jt]=n15/(k+jt)-n;
		}
		return e;
	}

	public static Point2D xyToLonLat(int num,double xx,double yy){
		double x=yy;
		double y=xx;
		double xi=(x+m0*Merid(2*phi0[num]*3600*s2r))/ra;
		double xip=xi;
		double eta=y/ra;
		double etap=eta;
		double sgmp=1;
		double taup=0;
		for(int j=beta.length-1;j>0;j--){
			double besin=beta[j]*Math.sin(2*j*xi);
			double becos=beta[j]*Math.cos(2*j*xi);
			xip -=besin*Math.cosh(2*j*eta);
			etap -=becos*Math.sinh(2*j*eta);
			sgmp -=2*j*becos*Math.cosh(2*j*eta);
			taup +=2*j*besin*Math.sinh(2*j*eta);
		}
		double sxip=Math.sin(xip);
		double cxip=Math.cos(xip);
		double shetap=Math.sinh(etap);
		double chetap=Math.cosh(etap);
		double chi=Math.asin(sxip/chetap);
		double phi=chi;
		for(int j=dlt.length-1;j>=0;j--){
			phi +=dlt[j]*Math.sin(2*j*chi);
		}
		double nphi=(1-n)/(1+n)*Math.tan(phi);

		double lmbd=lmbd0[num]*60+Math.atan2(shetap, cxip)/s2r;
		double lat=phi/s2r/3600;
		double lon=lmbd/3600;
		return new Point2D.Double(lon,lat);
	}

	public static Point2D lonlatToXY(int num,double lon,double lat){
		double phirad=Math.toRadians(lat);
		double lmbddeg=Math.floor(lon);
		double lmbdmin=Math.floor(60.0*(lon-lmbddeg));
		double lmbdsec=lmbddeg*3600.0+lmbdmin*60.0+(lon-lmbddeg-lmbdmin/60)*3600.0;

		double sphi=Math.sin(phirad);
		double nphi=(1-n)/(1+n)*Math.tan(phirad);
		double dlmbd=(lmbdsec-lmbd0[num]*60.0)*s2r;
		double sdlmbd=Math.sin(dlmbd);
		double cdlmbd=Math.cos(dlmbd);
		double tchi=Math.sinh(atanh(sphi)-e2n*atanh(e2n*sphi));
		double cchi=Math.sqrt(1+tchi*tchi);
		double xip=Math.atan2(tchi, cdlmbd);
		double xi=xip;
		double etap=atanh(sdlmbd/cchi);
		double eta=etap;
		double sgm=1;
		double tau=0;
		for(int j=alp.length-1;j>0;j--){
			double alsin=alp[j]*Math.sin(2*j*xip);
			double alcos=alp[j]*Math.cos(2*j*xip);
			xi +=alsin*Math.cosh(2*j*etap);
			eta +=alcos*Math.sinh(2*j*etap);
			sgm +=2*j*alcos*Math.cosh(2*j*etap);
			tau +=2*j*alsin*Math.sinh(2*j*etap);
		}
		double x=ra*xi-m0*Merid(2*phi0[num]*3600*s2r);
		double y=ra*eta;
		return new Point2D.Double(x,y);
	}

	private static double Merid(double phi2) {
			double dc=2.0*Math.cos(phi2);
			double[] s=new double[jt2+2];
			double[] t=new double[jt2+2];
			s[1]=Math.sin(phi2);
			for(int i=1;i<=jt2;i++){
				s[i+1]=dc*s[i]-s[i-1];
				t[i]=(1.0/i-4.0*i)*s[i];
			}
			double sum=0.0;
			double c1=ep;
			int j=jt;
			while(j>0){
				double c2=phi2;
				double c3=2.0;
				int l=j;
				int m=0;
				while(l>0){
					c2 +=(c3/=e[l--])*t[++m]+(c3*=e[2*j-l])*t[++m];
				}
				sum +=c1*c1*c2 ; c1/=e[j--];
			}
			return anh*(sum+phi2);
	}

	private static double atanh(double v){
		return 0.5*Math.log((1.0+v)/(1.0-v));
	}
}

(3) Mise en œuvre de la classe d'acquisition d'image de tuile de photographie aérienne

Nous avons créé la classe suivante pour acquérir et connecter des images de tuiles de la carte du Geographical Survey Institute.

GSITileReader.java


import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.imageio.ImageIO;

public class GSITileReader {
	private static final String base_url="https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/";
	private static final double L=85.05112877980659;

	public static BufferedImage getGSIImage(Rectangle2D plane,int map_num,int zoom,double scale)throws IOException{
		Shape sp=getLonLatShapeAtPlane(map_num, plane.getX(), plane.getY(), plane.getWidth(), plane.getHeight());
		Rectangle2D rect=sp.getBounds2D();
		long[] topLeft=lonlatToPixel(zoom,rect.getX(),rect.getY());
		long[] bottomRight=lonlatToPixel(zoom,rect.getX()+rect.getWidth(),rect.getY()+rect.getHeight());
		Set<Long> x=new HashSet<Long>();
		Set<Long> y=new HashSet<Long>();
		for(long i=topLeft[1];i<=bottomRight[1];i++){
			x.add((long)Math.ceil(i/256));
		}
		for(long i=bottomRight[2];i<=topLeft[2];i++){
			y.add((long)Math.ceil(i/256));
		}
		Long[] xx=x.toArray(new Long[x.size()]);
		Long[] yy=y.toArray(new Long[y.size()]);
		Arrays.sort(xx);
		Arrays.sort(yy);
		BufferedImage im=new BufferedImage(xx.length*256,yy.length*256,BufferedImage.TYPE_INT_RGB);
		Graphics2D g=im.createGraphics();
		for(int i=0;i<xx.length;i++){
			for(int j=0;j<yy.length;j++){
				try{
					String url=base_url+Integer.toString(zoom)+"/"+Long.toString(xx[i])+"/"+Long.toString(yy[j])+".jpg ";
					BufferedImage tmp=ImageIO.read(new URL(url));
					g.drawImage(tmp, i*256, j*256, null);
				}catch(IOException e){
					e.printStackTrace();
				}
			}
		}
		g.dispose();
		im=subImage(im,plane,(long)xx[0],(long)yy[0],map_num,zoom,scale);
		return im;
	}

	private static BufferedImage subImage(BufferedImage src,Rectangle2D rect,long minX,long minY,int num,int zoom,double scale){
		double xx=rect.getX();
		double yy=rect.getY();
		double ww=Math.abs(rect.getWidth());
		double hh=Math.abs(rect.getHeight());
		minX=minX*256;
		minY=minY*256;
		BufferedImage dst=new BufferedImage((int)(ww/scale),(int)(hh/scale),BufferedImage.TYPE_INT_RGB);
		System.out.println(dst.getWidth()+"/"+dst.getHeight());
		System.out.println(minX+"/"+minY);
		for(int i=0;i<dst.getWidth();i++){
			for(int j=0;j<dst.getHeight();j++){
				double x=xx+scale*i;
				double y=yy-scale*j;
				Point2D p=LonLatXY.xyToLonLat(num, x, y);
				long[] pc=lonlatToPixel(zoom, p.getX(), p.getY());
				int px=(int)(pc[1]-minX);
				int py=(int)(pc[2]-minY);
				int color=src.getRGB(px, py);
				dst.setRGB(i, j, color);
			}
		}
		return dst;
	}

	public static long[] lonlatToPixel(int zoom,double lon,double lat){
		long x=(long)(Math.pow(2, zoom+7)*(lon/180.0+1.0));
		long y=(long)((Math.pow(2, zoom+7)/Math.PI)*(-atanh(Math.sin(Math.toRadians(lat)))+atanh(Math.sin(Math.toRadians(L)))));
		return new long[]{(long)zoom,x,y};
	}

	private static double atanh(double v){
		return 0.5*Math.log((1.0+v)/(1.0-v));
	}

	public static AffineTransform createTfwTransform(Rectangle2D rectXY,BufferedImage img){
		double sx=rectXY.getWidth()/img.getWidth();
		double sy=rectXY.getHeight()/img.getHeight();
		double x=rectXY.getX();
		double y=rectXY.getY()+rectXY.getHeight();
		AffineTransform af=new AffineTransform(new double[]{sx,0,0,-sy,x,y});
        	return af;
	}

	public static Shape getLonLatShapeAtPlane(int num,double x,double y,double w,double h){
		Point2D p1=LonLatXY.xyToLonLat(num, x, y);
		Point2D p2=LonLatXY.xyToLonLat(num, x+w, y);
		Point2D p3=LonLatXY.xyToLonLat(num, x+w, y+h);
		Point2D p4=LonLatXY.xyToLonLat(num, x, y+h);
		GeneralPath gp=new GeneralPath();
		gp.moveTo(p1.getX(),p1.getY());
		gp.lineTo(p2.getX(), p2.getY());
		gp.lineTo(p3.getX(), p3.getY());
		gp.lineTo(p4.getX(), p4.getY());
		gp.closePath();
		return gp;
	}

	public static void outTfw(AffineTransform af,File out)throws IOException{
		BufferedWriter bw=new BufferedWriter(new FileWriter(out));
		bw.write(af.getScaleX()+"\n");
		bw.write(af.getShearX()+"\n");
		bw.write(af.getShearY()+"\n");
		bw.write(af.getScaleY()+"\n");
		bw.write(af.getTranslateX()+"\n");
		bw.write(af.getTranslateY()+"\n");
		bw.close();
	}
}

(4) Classe de sortie XYZ RVB

Enfin, j'ai créé un outil pour convertir DSM en XYZRGB, tel que l'entrée / sortie de fichier. Dans cette classe, RVB des pixels correspondant aux coordonnées DSM est émis à partir de l'image de photographie aérienne acquise et émis au format XYZ RVB. Pour le moment, nous permettons de prendre en charge autre que le 5ème système de coordonnées orthogonales planes.

XYZRgbCreator.java


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.UIManager;

public class XYZRgbCreator {

	private JFrame frame;
	private JComboBox<String> zone;

	public XYZRgbCreator(){
		frame=new JFrame();
		frame.setTitle("XYZRGB");
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		try {
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
			SwingUtilities.updateComponentTreeUI(frame);
		}catch(Exception e){
			try {
				UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
				SwingUtilities.updateComponentTreeUI(frame);
			}catch(Exception ee){
				ee.printStackTrace();
			}
		}
		WindowAdapter wa=new WindowAdapter(){
			@Override
			public void windowClosing(WindowEvent e) {
				close();
			}
		};
		frame.addWindowListener(wa);
		frame.getContentPane().setLayout(new BorderLayout());
		zone=new JComboBox<String>(new String[]{
				null,"Système plan à angle droit 01","Système plan à angle droit 02","Système plan à angle droit 03","Avion à angle droit 04ème système","Plan à angle droit 05e système","Système plan 06 à angle droit","Avion à angle droit 07e système","Plan à angle droit 08e système",
				"09ème système à angle droit d'avion","10ème système à angle droit d'avion","11e système à angle droit d'avion","12ème système à angle droit d'avion","13e système à angle droit d'avion","Avion à angle droit 14e système","Système 15ème à angle droit d'avion","Système 16e à angle droit d'avion",
				"Système 17ème à angle droit d'avion","Système 18ème à angle droit d'avion","Avion à angle droit système 19ème"
			});
		zone.setSelectedIndex(5);
		frame.getContentPane().add(zone,BorderLayout.NORTH);
		JLabel label=new JLabel("DSM > XYZRGB");
		label.setFont(new Font(Font.SANS_SERIF,Font.BOLD,24));
		label.setVerticalAlignment(JLabel.CENTER);
		label.setHorizontalAlignment(JLabel.CENTER);
		JPanel jp=new JPanel(new BorderLayout());
		jp.add(label,BorderLayout.CENTER);
		frame.getContentPane().add(jp,BorderLayout.CENTER);
		frame.setSize(480,480);
		frame.setResizable(false);
		DropFileHandler handler=new DropFileHandler();
		label.setTransferHandler(handler);
		jp.setTransferHandler(handler);
	}

	private void close(){
		int id=JOptionPane.showConfirmDialog(frame, "Exit?", "Info", JOptionPane.YES_NO_OPTION,JOptionPane.INFORMATION_MESSAGE);
		if(id==JOptionPane.YES_OPTION){
			frame.setVisible(false);
			System.exit(0);
		}
	}

	private class DropFileHandler extends TransferHandler {
		private static final long serialVersionUID = 1L;
		@Override
		public boolean canImport(TransferSupport support) {
			if (!support.isDrop()) {
				return false;
			}
			if (!support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
				return false;
			}
			return true;
		}

		@SuppressWarnings("unchecked")
		@Override
		public boolean importData(TransferSupport support) {
			if (!canImport(support)) {
		        return false;
		    }
			Transferable t = support.getTransferable();
			try {
				List<File> files = (List<File>) t.getTransferData(DataFlavor.javaFileListFlavor);
				for (File file : files){
					if(file.isDirectory())continue;
					try{
						process(file);
					}catch(IOException e){
						e.printStackTrace();
					}
				}
			} catch (UnsupportedFlavorException | IOException e) {
				e.printStackTrace();
			}
			return true;
		}
	}

	private void process(File f) throws IOException{
		if(!(f.getName().endsWith(".txt")||f.getName().endsWith(".xyz")))return;
		frame.repaint();
		int bwz=zone.getSelectedIndex();
		Rectangle2D rectXY=getBounds(f);
		BufferedImage img=GSITileReader.getGSIImage(rectXY,bwz,18,0.5);
		String name=f.getName().substring(0,f.getName().lastIndexOf("."));
		File dir=f.getParentFile();
		ImageIO.write(img, "jpg", new File(dir.getAbsolutePath()+"/"+name+".jpg "));
		AffineTransform af=new AffineTransform(new double[]{
				0.5,0,0,-0.5,rectXY.getX(),rectXY.getY()});
		GSITileReader.outTfw(af, new File(dir.getAbsolutePath()+"/"+name+".jgw"));
		try{
			af=af.createInverse();
			BufferedReader br=new BufferedReader(new FileReader(f));
			File out=new File(dir.getAbsolutePath()+"/"+name+"_color.txt");
			BufferedWriter bw=new BufferedWriter(new FileWriter(out));
			String line=null;
			String str=null;
			Rectangle2D imgRect=new Rectangle2D.Double(0, 0, img.getWidth(),img.getHeight());
			while((line=br.readLine())!=null){
				line=line.replaceAll(" ",",");
				String[] sp=line.split(",");
				double x=Double.parseDouble(sp[0]);
				double y=Double.parseDouble(sp[1]);
				Point2D p=af.transform(new Point2D.Double(x,y), new Point2D.Double());
				int xx=(int)Math.floor(p.getX());
				int yy=(int)Math.floor(p.getY());
				if(imgRect.contains(xx, yy)){
					int col=img.getRGB(xx, yy);
					Color color=new Color(col);
					str=line+" "+Integer.toString(color.getRed())+" "+Integer.toString(color.getGreen())+" "+Integer.toString(color.getBlue())+"\n";
				}else{
					str=line+" 0 0 0\n";
				}
				bw.write(str);
				bw.flush();
			}
			br.close();
			bw.close();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	private Rectangle2D getBounds(File f)throws IOException{
		double xmin=Double.MAX_VALUE;
		double xmax=-Double.MAX_VALUE;
		double ymin=Double.MAX_VALUE;
		double ymax=-Double.MAX_VALUE;
		BufferedReader br=new BufferedReader(new FileReader(f));
		String line=null;
		while((line=br.readLine())!=null){
			line=line.replaceAll(" ",",");
			String[] sp=line.split(",");
			double x=Double.parseDouble(sp[0]);
			double y=Double.parseDouble(sp[1]);
			xmin=Math.min(x, xmin);
			xmax=Math.max(x, xmax);
			ymin=Math.min(y, ymin);
			ymax=Math.max(y, ymax);
		}
		br.close();
		Rectangle2D ret=new Rectangle2D.Double(xmin,ymax,xmax-xmin+1,-(ymax-ymin)-1);
		return ret;
	}

	public static void main(String[] args){
		XYZRgbCreator gp=new XYZRgbCreator();
		gp.frame.setLocationRelativeTo(null);
		gp.frame.setVisible(true);
	}
}

(5) Produits livrables

Lorsque vous exécutez XYZRgbCreator, la fenêtre suivante s'affiche. Si vous déposez le fichier DSM de topographie numérique de la zone entière de la préfecture de Hyogo dans la fenêtre, le processus démarre et le fichier xyzrgb est généré. À l'origine, les informations de progression du processus devraient être affichées, mais cette fois elles ont été omises.

999.jpg

4. Implémentation de l'outil de conversion RGBXYZ-> PLY

Ensuite, implémentez un outil pour convertir les fichiers XYZ RVB en fichiers PLY. Le modèle DSM est un modèle de structure tridimensionnel dans lequel seule la surface est définie, appelé le modèle de surface. Par conséquent, 2D [Division Delaunay](https://ja.wikipedia.org/wiki/%E3%83%89%E3%83%AD%E3%83%8D%E3%83%BC%E5%9B J'ai pensé qu'un modèle tridimensionnel pouvait être généré en exécutant% B3) et en définissant le polygone triangulaire sur Face. Delaunay split J'ai utilisé la bibliothèque de triangulation de Delaunay 2D "TINFOUR". J'ai vérifié les informations nécessaires dans mvn repositiry: tinfour et les ai ajoutées à dependancis dans le fichier pom.

(1) Flux de processus

Le flux de traitement est le suivant.

  1. Créez une liste de sommets à partir des informations XYZ du fichier XYZ RVB et une liste d'informations de couleur à partir des informations RVB.
  2. Transmettez la liste de sommets à l'instance IncrementalTin et effectuez une séparation Delaunay bidimensionnelle.
  3. Produisez les articles nécessaires conformément aux spécifications PLY.
  4. Sortie des informations sur les sommets et les couleurs.
  5. Informations sur la face de sortie.

(2) Implémentation de la classe PlyCreator

Implémentez la classe PlyCreator qui exécute le processus ci-dessus. C'est ennuyeux, donc je l'implémente en tant qu'outil de ligne de commande.

PlyCreator.java


import java.awt.Color;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.function.Consumer;

import org.tinfour.common.SimpleTriangle;
import org.tinfour.common.Vertex;
import org.tinfour.standard.IncrementalTin;
import org.tinfour.utils.TriangleCollector;

public class PlyCreator extends Observable{
	private List<Vertex> vertexs;
	private List<Color> colors;
	private IncrementalTin tin;

	public PlyCreator(){}

	private void outVertex(BufferedWriter bw)throws IOException{
		System.out.println("Sortie Vertex");
		for(Vertex v : vertexs){
			StringBuffer buf=new StringBuffer();
			buf.append(Float.toString((float)v.getX())+" ");
			buf.append(Float.toString((float)v.getY())+" ");
			buf.append(Float.toString((float)v.getZ())+" ");
			Color c=colors.get(v.getIndex());
			buf.append(Integer.toString(c.getRed())+" ");
			buf.append(Integer.toString(c.getGreen())+" ");
			buf.append(Integer.toString(c.getBlue())+"\n");
			writeBytes(bw,buf.toString());
		}
	}

	private void outFace(BufferedWriter bw)throws IOException{
		System.out.println("Sortie de visage");
		Consumer<SimpleTriangle> cons=new Consumer<SimpleTriangle>() {
		    @Override
		    public void accept(SimpleTriangle arg){
				StringBuffer buf=new StringBuffer();
				buf.append("3");
				buf.append(" "+Integer.toString(arg.getVertexA().getIndex()));
				buf.append(" "+Integer.toString(arg.getVertexB().getIndex()));
				buf.append(" "+Integer.toString(arg.getVertexC().getIndex()));
				buf.append("\n");
				try{
					writeBytes(bw,buf.toString());
				}catch(IOException e){
					e.printStackTrace();
				}
		    }
		};
		TriangleCollector.visitSimpleTriangles(tin, cons);
	}

	private void writeBytes(BufferedWriter bw,String str)throws IOException{
		bw.write(str,0,str.length());
	}

	public void writePLY(File f)throws IOException{
		System.out.println("Sortie PLY");
		Charset charset = Charset.forName("US-ASCII");
		BufferedWriter bw = Files.newBufferedWriter(f.toPath(),charset);
		writeBytes(bw,"ply\n");
		writeBytes(bw,"format ascii 1.0\n");
		writeBytes(bw,"element vertex "+Integer.toString(vertexs.size())+"\n");
		writeBytes(bw,"property float x\n");
		writeBytes(bw,"property float y\n");
		writeBytes(bw,"property float z\n");
		writeBytes(bw,"property uchar red\n");
		writeBytes(bw,"property uchar green\n");
		writeBytes(bw,"property uchar blue\n");
		writeBytes(bw,"element face "+Integer.toString(tin.countTriangles().getCount())+"\n");
		writeBytes(bw,"property list uchar int vertex_index\n");
		writeBytes(bw,"end_header\n");
		outVertex(bw);
		outFace(bw);;
		bw.close();
	}

	public void readXYZGRB(File f,String separator)throws IOException{
		System.out.println("Lecture de fichiers");
		BufferedReader br=new BufferedReader(new FileReader(f));
		String line=null;
		vertexs=new ArrayList<Vertex>();
		colors=new ArrayList<Color>();
		int id=0;
		while((line=br.readLine())!=null){
			String[] sp=line.split(separator);
			Vertex v=new Vertex(
				Double.parseDouble(sp[0]),
				Double.parseDouble(sp[1]),
				Double.parseDouble(sp[2]),
				id++
			);
			vertexs.add(v);
			Color c=new Color(
				Integer.parseInt(sp[3]),
				Integer.parseInt(sp[4]),
				Integer.parseInt(sp[5])
			);
			colors.add(c);
		}
		br.close();
		System.out.println("Traitement TIN");
		tin=new IncrementalTin();
		tin.add(vertexs, null);
	}

	public static void main(String[] args){
		 PlyCreator pc=new  PlyCreator();
		 File in=new File(args[0]);
		 File out=new File(args[1]);
		 try{
			 pc.readXYZGRB(in, ",");
			 pc.writePLY(out);
		 }catch(Exception e){
			 e.printStackTrace();
		 }
	}
}

(3) Produits livrables

Lorsque la classe PlyCreator est exécutée avec le fichier d'entrée (fichier XYZRGB) et le fichier de sortie (fichier PLY) comme arguments, un modèle tridimensionnel au format PLY est généré. 898.jpg

Si vous chargez le fichier PLY dans une visionneuse 3D telle que MeshLab, le modèle créé sera affiché. L'image ci-dessous est une image modèle générée à partir de DSM près de la gare JR Sannomiya. snapshot02.png

5. Résumé

J'ai obtenu les informations de couleur de la photographie aérienne du National Land Research Institute, j'ai converti la carte topographique numérique DSM de toute la préfecture de Hyogo en un fichier XYZ RGB et généré un modèle au format PLY. Cela peut paraître un peu grossier quand on regarde le modèle de la zone urbaine, mais l'image ci-dessous est un modèle du DSM de la zone forestière de Sayo-cho, mais s'il s'agit d'un maillage de 1 m, c'est un modèle topographique qui exprime la différence de couvert en fonction de la faune forestière Peut être obtenu. snapshot00.png

Sur le site Web de la préfecture de Hyogo, «En combinant ces données et d'autres données avec une technologie de pointe, nous proposons des idées et des propositions d'utilisation afin de promouvoir les efforts visant à résoudre les problèmes régionaux grâce à la collaboration entre le secteur privé, l'industrie, les universités et le gouvernement. Nous recrutons. "Cependant, comme le DSM et le DEM de toute la préfecture de Hyogo sont ouverts au public, la carte topographique numérique de toute la préfecture de Hyogo peut être utilisée à des fins diverses. En dehors de la préfecture de Hyogo, la préfecture de Shizuoka publie des données de groupe de points sur "la base de données des nuages de points de Shizuoka", mais d'autres préfectures publient également de telles données d'enquête publiques. Je pense que c'est intéressant s'il y a du mouvement.

Recommended Posts

Essayez de créer un modèle tridimensionnel (format PLY) à partir de la carte topographique numérique DSM de toute la préfecture de Hyogo
Créez une carte du métro de Tokyo à partir du fichier CSV de la station data.jp
Créer une carte multi-touches avec une bibliothèque standard