Announcement

Collapse
No announcement yet.

Surface shapes rendering performance

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Surface shapes rendering performance

    Hello,

    I am developing a plug in to display sensors for aircraft, using the surface shapes in the source code. The shapes are being displayed. To explain it simply I have a large circle 100km radius, and a small square shape being displayed. When I zoom in on the larger circle world wind bogs down alot. To a point to where the program is basically going in slide show mode. I have the shapes being updated with a position every .1 seconds or so. I was wondering if this slow down is from bad coding, or if it is more along the lines of my system performance. I'm running your typical office computer, intel core duo, with internal graphics and 2gb of memory.

    So my question is, is my computer too slow (which would be fine with me), are the shapes I am rendering not designed too well for this, or is my code not up to par to handle such large shapes efficiently?

    I attached the example.txt csv file below, which is used by my program to run some simulated lat long coords to represent an aircraft. That needs to be placed in the top directory of world wind directory. Rename it to example.csv.

    Code is below

    Code:
    package gov.nasa.worldwind.examples;
    
    
    import com.sun.opengl.util.j2d.TextRenderer;
    import gov.nasa.worldwind.Configuration;
    import gov.nasa.worldwind.util.BasicDragger;
    import gov.nasa.worldwind.event.*;
    import gov.nasa.worldwind.geom.*;
    import gov.nasa.worldwind.layers.RenderableLayer;
    import gov.nasa.worldwind.pick.PickedObjectList;
    import gov.nasa.worldwind.render.*;
    
    
    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.event.*;
    import java.awt.*;
    import java.awt.event.*;
    import java.util.ArrayList;
    
    import java.io.*;
    import java.lang.*;
    import java.awt.*;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class Sensor
    {
    protected static class AppFrame extends JFrame
    {
    		private Dimension canvasSize = new Dimension(1000, 800);
    		private Dimension selectPanelSize = new Dimension(200, 100);
    		private ApplicationTemplate.AppPanel wwjPanel;
    		private RenderableLayer layer = new RenderableLayer();
    		private TextRenderer textRenderer = new TextRenderer(java.awt.Font.decode("Arial-Plain-13"), true, false);
    
    		double angle;
    		double radarHole, minorRadius, majorRadius, radarRim;
    
    
    
    		//Initialize the radar footprint (circle)
    		LatLon position = new LatLon(Angle.fromDegrees(0), Angle.fromDegrees(0));
    		//SurfaceCircle(position,minorradius,majorradius,heading,intervals,sensorHoleradius,arc angle)
    		Renderable radar = new SurfaceCircle(position, 100e3, 100e3, Angle.fromDegrees(0),100,25e3,Angle.fromDegrees(350));
    		Renderable EO = new SurfaceCircle(position, 25e3, 25e3, Angle.fromDegrees(270),100,0,Angle.fromDegrees(360));
    
    		Color color, Bcolor;
    		float alpha;
    		AbstractSurfaceShape rdrShape;
    		AbstractSurfaceShape EOShape;
    
    		public AppFrame()
    		{
    			// Create the WorldWindow.
    			this.wwjPanel = new ApplicationTemplate.AppPanel(this.canvasSize, true);
    			this.wwjPanel.setPreferredSize(canvasSize);
    			ApplicationTemplate.insertBeforePlacenames(this.wwjPanel.getWwd(), layer);
    
    			setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    			setVisible(true);
    
    			//Call function to create JCheckBox on side window
    			//JPanel sensorPanel = sensePanel();
    			//JPanel larpanel = larPanel();
    
    			JPanel selectPanel = new JPanel(new GridLayout(2,1));
    			selectPanel.add(sensePanel());
    			selectPanel.add(larPanel());
    
    			JPanel selectPanel2 = new JPanel(new BorderLayout());
    			selectPanel2.add(selectPanel, BorderLayout.NORTH);
    
    
    			//selectPanel.setPreferredSize(this.selectPanelSize);
    
    			this.getContentPane().add(selectPanel2, BorderLayout.WEST);
    			this.getContentPane().add(wwjPanel, BorderLayout.CENTER);
    			//this.getContentPane().add(controlPanel, BorderLayout.WEST);
    			this.pack();
    
    			// Center the application on the screen.
    			Dimension prefSize = this.getPreferredSize();
    			Dimension parentSize;
    			java.awt.Point parentLocation = new java.awt.Point(0, 0);
    			parentSize = Toolkit.getDefaultToolkit().getScreenSize();
    			int x = parentLocation.x + (parentSize.width - prefSize.width) / 2;
    			int y = parentLocation.y + (parentSize.height - prefSize.height) / 2;
    			this.setLocation(x, y);
    			this.setResizable(true);
    
    			color = new Color(1f, 1f, 0f);
    			Bcolor = new Color(1f, 0f, 0f);
    			alpha = 2/10f;
    
    			rdrShape = (AbstractSurfaceShape) radar;
    			rdrShape.getAttributes().setDrawOutline(true);
    			rdrShape.getAttributes().setOutlineMaterial(new Material(Bcolor));
    			rdrShape.getAttributes().setOutlineOpacity(alpha);
    
    			rdrShape.getAttributes().setInteriorMaterial(new Material(color));
    			rdrShape.getAttributes().setInteriorOpacity(alpha);
    			rdrShape.getAttributes().setDrawInterior(true);
    
    			color = new Color(.196f,.99f,.196f);
    			alpha = 5/10f;
    
    			EOShape = (AbstractSurfaceShape) EO;
    			EOShape.getAttributes().setDrawOutline(false);
    
    			EOShape.getAttributes().setInteriorMaterial(new Material(color));
    			EOShape.getAttributes().setInteriorOpacity(alpha);
    			EOShape.getAttributes().setDrawInterior(true);
    
    			this.layer.addRenderable(this.radar);
    			this.layer.addRenderable(this.EO);
    
    		}
    		public void update(double lat, double lon, double heading, double EOHead, double EOfov, double eoSensorHole, double eoSensorRim, double radarRim, double radarHole)
    		{
    
    			//Update the radar sensor footprints location, heaing, and altitude
    			SurfaceEllipse radarUpdate = (SurfaceCircle) radar;
    			radarUpdate.setCenter(LatLon.fromDegrees(lat, lon));
    			radarUpdate.setHeading(Angle.fromDegrees(heading+270));
    			radarUpdate.setMajorRadius(radarRim);
    			radarUpdate.setMinorRadius(radarRim);
    			radarUpdate.setHoleRadius(radarHole);
    			//radarUpdate.setCircleAngle(Angle.fromDegrees(350));
    
    			//Update the radar sensor footprints location, heaing, and altitude
    			SurfaceEllipse EOUpdate = (SurfaceCircle) EO;
    			EOUpdate.setCenter(LatLon.fromDegrees(lat, lon));
    			EOUpdate.setHeading(Angle.fromRadians(EOHead));
    			EOUpdate.setCircleAngle(Angle.fromRadians(EOfov));
    
    			EOUpdate.setHoleRadius(eoSensorHole);
    
    			EOUpdate.setMinorRadius(eoSensorRim);
    			EOUpdate.setMajorRadius(eoSensorRim);
    
    			this.wwjPanel.getWwd().redraw();
    
    		}
    
    		//Syncs with frame
    		protected void updateFrame(final double lat, final double lon, final double heading, final double eoHead, final double eofov)
    		{
    			System.out.println("Lat " + lat);
    
    			// Ensure this is executed on the EDT.
    			if (!SwingUtilities.isEventDispatchThread())
    			{
    				SwingUtilities.invokeLater(new Runnable()
    				{
    					public void run()
    					{
    						updateFrame(lat, lon, heading, eoHead, eofov);
    					}
    				});
    			}
    			else
    			{
    				update(lat, lon, heading, eoHead, eofov, this.minorRadius, this.majorRadius, this.radarRim,this.radarHole);
    			}
            }
    
            //Calculates the neccesary inputs for each sensor
            public void doMath(double lat, double lon, double heading, double altitude, double EOHead, double depression, double EOfov)
            {
    
    			//Convert to radians
    			EOHead = EOHead*Math.PI/180;
    			depression = depression*Math.PI/180;
    			EOfov = EOfov*Math.PI/180;
    
    			//Area of radar coverage
    			this.radarRim = 1.23*Math.sqrt(altitude); //Radar horizon in nm
    			this.radarRim = this.radarRim * 1852; // convert natical miles to meters
    
    			//radarRim = 30000;
    			//d = this.R*Math.acos(this.R/(this.R+altitude));
    
    			this.radarHole=5000;
    
    			//Check to make sure depression isnt too small
    			depression = (depression <= .5*EOfov) ? .5*(EOfov) : depression;
    			//Check to make sure depression isnt too large
    			depression = (depression > Math.PI) ? Math.PI : depression;
    
    			if (depression <= Math.PI/2 -.5*EOfov)
    			{
    				//Sensor hole
    				this.angle = Math.PI/2 - (depression+.5*EOfov);
    				this.minorRadius = Math.tan(this.angle)*altitude;
    
    				//MajorRadius
    				this.angle = Math.PI/2 - (depression-.5*EOfov);
    				this.majorRadius = Math.tan(this.angle)*altitude;
    				//System.out.println("angle " + angle + "majorRadius"+majorRadius);
    			}
    			else if (depression == Math.PI)
    			{
    				this.minorRadius = 1;
    				this.majorRadius = Math.tan(.5*EOfov)*altitude;
    			}
    			else
    			{
    				this.minorRadius = 1;
    				this.majorRadius = 5000;
    			}
    
    			updateFrame(lat, lon, heading, EOHead, EOfov);
    		}
    
    		public void deleteRenderable(Renderable shape)
    		{
    			this.layer.removeRenderable(shape);
    		}
    
    		public void plusRenderable(Renderable shape)
    		{
    			this.layer.addRenderable(shape);
    		}
    
    		//Create sensor Panel button
    		public JPanel sensePanel()
    		{
    			final Renderable radar = this.radar;
    			final Renderable EO = this.EO;
    
    			//rows, columns, hgap, vgap
    			GridLayout layout = new GridLayout(1,2,20,0);
    			JPanel ssPanel = new JPanel(layout);
    
    			ssPanel.setBorder(BorderFactory.createTitledBorder("Sensor selection"));
    
    			final JCheckBox b = new JCheckBox("Radar",true);
    
    			ssPanel.add(b);
    			final JCheckBox c = new JCheckBox("EO",true);
    
    			ssPanel.add(c);
    
    			b.addActionListener(new ActionListener()
    			{
    				boolean selected;
    
    				public void actionPerformed(ActionEvent actionEvent)
    				{
    
    					if (b.isSelected()==false)
    					{
    						deleteRenderable(radar);
    					}
    					else
    					{
    						plusRenderable(radar);
    					}
    
    				}
    			});
    
    			c.addActionListener(new ActionListener()
    			{
    				public void actionPerformed(ActionEvent actionEvent)
    				{
    					if (c.isSelected()==false)
    					{
    						deleteRenderable(EO);
    					}
    					else
    					{
    						plusRenderable(EO);
    					}
    
    				}
    			});
    			//ssPanel.setBorder(this.createTitleBorder("Sensors"));
    
    			return ssPanel;
    		}
    
    
    
    
    	public JPanel larPanel()
    	{
    
    		GridLayout layout = new GridLayout(1,1,20,0);
    
    		JPanel LARpanel = new JPanel(layout);
    
    		LARpanel.setBorder(BorderFactory.createTitledBorder("LAR overlay"));
    
    		final JCheckBox a = new JCheckBox("LAR",true);
    
    		LARpanel.add(a);
    
    		a.addActionListener(new ActionListener()
    		{
    			boolean selected;
    
    			public void actionPerformed(ActionEvent actionEvent)
    			{
    
    				if (a.isSelected()==false)
    					{
    						//deleteRenderable(radar);
    					}
    					else
    					{
    						//plusRenderable(radar);
    					}
    
    			}
    		});
    
    		return LARpanel;
    	}
    
    }
    
    	public static void main(String[] args)
    	{
    		int end = 0;
    		double lat;
    		double lon;
    		try
    		{
    
    		readfile start = new readfile();
    		start.Read();
    		}
    		catch (Exception e)
    		{
    		e.printStackTrace();
    		}
    
    
    	}
    
    }
    Code:
    package gov.nasa.worldwind.examples;
    
    import com.sun.opengl.util.j2d.TextRenderer;
    import gov.nasa.worldwind.Configuration;
    import gov.nasa.worldwind.util.BasicDragger;
    import gov.nasa.worldwind.event.*;
    import gov.nasa.worldwind.geom.*;
    import gov.nasa.worldwind.layers.RenderableLayer;
    import gov.nasa.worldwind.pick.PickedObjectList;
    import gov.nasa.worldwind.render.*;
    
    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.event.*;
    import java.awt.*;
    import java.awt.event.*;
    import java.util.ArrayList;
    
    import java.io.*;
    import java.lang.*;
    import java.awt.*;
    import java.util.Timer;
    import java.util.TimerTask;
    
    //Dans addition - reads the latitude longitude from a file and ouputs to a console while WW is running
    public class readfile
    {
    
    	//Sensor frame = new Sensor();
    	Sensor.AppFrame frame = new Sensor.AppFrame();
    
    
    	int ending;
    	private Timer timer;
    	BufferedReader in;
    	private int row=0;
    	private String str;
    	long interval;
    	String Str;
    
    	double lat=0;
    	double lon=0;
    	double heading=0;
    	double EOHead, beamAngle, EOfov, altitude;
    
    	//Constructor
    	public readfile() {
    
    		try {
    			this.interval = 100;
    
    			BufferedReader in = new BufferedReader(new FileReader("Example.csv"));
    			this.in = in;
    
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    
    	}
    	public void Read() {
    
    		timer = new Timer();
    		timer.scheduleAtFixedRate(new Printlatlong(), 0, interval);
    
    
    	}
    
    	// Timer method that runs every given interval
    class Printlatlong extends TimerTask {
    
    		//Arithmetic mean radius of earth, I will use to find horizon
    	private final static double R = 6371008.7714;
    
    	Printlatlong() {
    
    
    
    	}
    
    	public void run()
    	{
    
    	try{
    	if((str = in.readLine()) != null) {
    
    		if(row != 0) {
    
    		String[] Colvalues = str.split(",");
    		lat = Double.parseDouble(Colvalues[0]);
    		lon = Double.parseDouble(Colvalues[1]);
    		heading = Double.parseDouble(Colvalues[2]);
    		altitude = Double.parseDouble(Colvalues[3]);
    		EOHead = Double.parseDouble(Colvalues[4]);
    		beamAngle = Double.parseDouble(Colvalues[5]);
    		EOfov = Double.parseDouble(Colvalues[6]);
    
    		frame.doMath(lat, lon, heading, altitude, EOHead, beamAngle, EOfov);
    
    
    
    		//System.out.println("Beam Angle " + beamAngle +  "Major Radius " + majorRadius + "Minor Radius " + minorRadius);
    
    		//frame.update1(40.1, 128);
    
    		//Create object for top level Shapes class
    		//Sensor.AppFrame outObj = new Sensor.AppFrame();
    		//outObj.update(lat,lon);
    		//outObj.updateLatLong(lat, lon);
    		//Creates obj for nested class
    		//Shapes.AppFrame obj = new Shapes.AppFrame();
    
    		}
    
    		row++;
    		ending=0;
    
    		} else {
    		timer.cancel();
    		in.close();
    		ending=1;
    		}
    
    		} catch(IOException e) {
    		}
    
    	}
    
    
    
    	}
    }
    Attached Files

  • #2
    Instructions to get this to work.

    Replace the existing surfacecircle.java and surfaceellipse.java in the HOME\src\gov\nasa\worldwind\render directory for this to work.

    Add sensor.java and readfile.java to HOME\src\gov\nasa\worldwind\examples

    run sensor.
    Attached Files
    Last edited by DanPngc12; 10-14-2009, 01:19 AM. Reason: Instructiosn to run.

    Comment


    • #3
      Two surface shapes should not be an issue.

      There are two things to know about surface shapes:

      1. For optimal performance, all shapes should be handled by the same TiledSurfaceObjectRenderer instance. Use the SurfaceShapeLayer rather then the RenderableLayer - it will automatically pipe all surface shapes to one renderer and render all other renderables as a renderable layer would do. If you use a RenderableLayer, each surface shape will use it's own renderer rather then share the same.

      2. With the actual implementation, performance degrades when getting very close to the ground surface. This is a known issue that will be fixed at some point. How close to your shape are you zooming until WW noticably slows down?
      My World Wind Java Blog & WW.net Plugins page

      Comment


      • #4
        Thanks for the info, I will be adding a lot of new shapes so using the new SurfaceShapeLayer will help.

        World Wind does slow down the most when I zoom in. When the shape itself is covering most of the viewing area it turns into slide show mode.

        Dan

        Comment


        • #5
          How close to the ground are you when performance degrades? Are you looking at the horizon or straight down?
          My World Wind Java Blog & WW.net Plugins page

          Comment


          • #6
            Which WWJ build are you using this with?

            I downloaded and tried your app with the latest build (0.6.267.12725) and got it to run without errors but nothing happens except the Lat updates are in the console.

            I have had problems with "addRenderable" when using the worldwind.layers.xml. Apparently I am not getting your layers to render.
            this.layer.addRenderable(this.radar);
            this.layer.addRenderable(this.EO);

            When I run my app that lists the layers (legacy style) rather than the new worldwind.layers.xml it works OK. I will search the forum for this issue and maybe start a thread. When I get that resolved I will try your Sensor app again.

            For your 5 files you could leave the .java and .csv extensions and just zip them into one .zip file.

            edit: Running on a single core even though the radar and EO layers are not rendered it was about 70% CPU. Commenting out the render line it was about 2%. So apparently no problem with the rest of the code. I also have 2 with dual core I could try when I get the layer.addRenderable() issue resolved.

            NetBeans (which I seldom use) has an included profiler. You could import it and get a profile of your code. I did with my WWJ app and CPU usage was not an issue and the most time used was by a socket to interact with apps in Python, C++ or whatever, the time was minimal. Eclipse may have a plugin to profile with but usually just trial and error works OK.

            If you want the FAA Sectionals for your app they are available, just do a search in this forum.
            Last edited by nlneilson; 10-17-2009, 05:31 PM. Reason: add build # and additional comment.
            Neil
            http://www.nlneilson.com

            Comment


            • #7
              I started a thread on the rendering issue.
              http://forum.worldwindcentral.com/sh...ad.php?t=22499

              On your performance issue look at this thread, it may be an option you could try in your app.
              http://forum.worldwindcentral.com/sh...t=22403&page=2
              Neil
              http://www.nlneilson.com

              Comment


              • #8
                Looking at the code in post 1, you set each shape attributes using shape.getAttributes().setxxx(). That does not work with the actual implementation.

                When getting at a shape attributes, you get a copy of the attributes. Mofifying that copy will not modify the shape attributes. The proper way is to get the attributes, change them and set the attributes of the shape again - shape.setAttributes(newAttr). When a shape attributes are set, the shape makes a copy of the attributes. So changing the attributes after having set them will not affect the shape either.
                My World Wind Java Blog & WW.net Plugins page

                Comment


                • #9
                  Originally posted by patmurris View Post
                  How close to the ground are you when performance degrades? Are you looking at the horizon or straight down?
                  The performance slows down when I am looking towards the horizon, or only at the surface. As long as the shape itself is big enough to cover most of the viewing area.

                  Comment


                  • #10
                    Originally posted by nlneilson View Post
                    Which WWJ build are you using this with?

                    I downloaded and tried your app with the latest build (0.6.267.12725) and got it to run without errors but nothing happens except the Lat updates are in the console.

                    I have had problems with "addRenderable" when using the worldwind.layers.xml. Apparently I am not getting your layers to render.
                    this.layer.addRenderable(this.radar);
                    this.layer.addRenderable(this.EO);

                    When I run my app that lists the layers (legacy style) rather than the new worldwind.layers.xml it works OK. I will search the forum for this issue and maybe start a thread. When I get that resolved I will try your Sensor app again.

                    For your 5 files you could leave the .java and .csv extensions and just zip them into one .zip file.

                    edit: Running on a single core even though the radar and EO layers are not rendered it was about 70% CPU. Commenting out the render line it was about 2%. So apparently no problem with the rest of the code. I also have 2 with dual core I could try when I get the layer.addRenderable() issue resolved.

                    NetBeans (which I seldom use) has an included profiler. You could import it and get a profile of your code. I did with my WWJ app and CPU usage was not an issue and the most time used was by a socket to interact with apps in Python, C++ or whatever, the time was minimal. Eclipse may have a plugin to profile with but usually just trial and error works OK.

                    If you want the FAA Sectionals for your app they are available, just do a search in this forum.

                    I'm currently running worldwind-0.6.215.12519. Sometimes the shape doesnt show up the first run so try it atleast 2 times. Also make sure you are looking over at China. The color of the shape kind of blends in with the ground. I will try with the latest app at some point. PatMurris told me using the SurfaceShapeLayer() is ideal for this so I have also changed my code to do that. I also created my own SurfaceSensor.java file that removes the need of using a modified version of SurfaceEllipse.java and SurfaceCircle.java. You just need to put SurfaceSensor.java in the same directory as Circle and Ellipse. Make sure Ellipse and Circle are the unmodified files straight from the latest build.

                    I attached a zip with an updated Sensor.java and SurfaceSensor.java.
                    Attached Files

                    Comment


                    • #11
                      I downloaded your .zip and tried it, nothing displayed.
                      In readfile.java at line 96 I put:
                      System.out.println("Lat " + lat + " Lon " + lon);

                      Going to the location showed by the Lat and Lon still did not show your images.
                      Since the pick and drag of the globe is disabled for a surface image I could determine your surface ellipse when the .csv file ends is approximately:

                      122.1 to 129.9 major axis
                      36.5 to 39.5 minor axis

                      Did you make any changes to a config file or something else?
                      You could download the latest build into a new workspace and try your code and let us know if it works or if something needs to be changed.

                      How is your performance using the surface images?

                      edit: I tried this about 5 times, no image is displayed.
                      The SurfaceCircle and SurfaceEllipse are the original except the visibility of "int intervals" in SurfaceEllipse needed to be changed to "protected".
                      Last edited by nlneilson; 10-20-2009, 12:38 AM.
                      Neil
                      http://www.nlneilson.com

                      Comment


                      • #12
                        Hey,

                        I'll try out the newest build tomorrow, right now I'm messing around with adding some map messages to represent each new renderable.

                        Dan

                        Comment


                        • #13
                          My client is keen to improve the performance of very large shapes when zoomed in, so I'm considering working on that myself. We notice a problem when a very large circle (the size of Australia) is drawn. We first thought it might be the number of points in that circle becomes very large when zoomed in, but I noticed the number of points appears to be capped at 100 (by default). I'm running 0.6.299.

                          Can you give me some pointers as to what the problem is?

                          Comment


                          • #14
                            We noticed that a circle can render with a maximum of 3200 points (with the default settings). I noticed an improvement by clamping that at 100 points.

                            Comment


                            • #15
                              If you have something like a large circle a regular polyline might be an option.
                              You can zoom in as much as you want without loosing quality.

                              What I have found after using surface shapes and regular polylines is that the number of shapes or polylines is the limiting factor not the number of positions.

                              Culling or limiting the data as done with the grid layers works best in some situations.
                              Neil
                              http://www.nlneilson.com

                              Comment

                              Working...
                              X