1
1
# -*- coding: utf-8 -*-
2
2
3
- __author__ = "Mateus Stano Junqueira, Sofia Lopes Suesdek Rocha"
3
+ __author__ = "Mateus Stano Junqueira, Sofia Lopes Suesdek Rocha, Abdulklech Sorban "
4
4
__copyright__ = "Copyright 20XX, Projeto Jupiter"
5
5
__license__ = "MIT"
6
6
7
7
8
+ import math
8
9
import traceback
9
- from time import process_time , time
10
10
import warnings
11
+ from time import process_time , time
11
12
12
13
import matplotlib .pyplot as plt
13
14
import numpy as np
15
+ import simplekml
14
16
from imageio import imread
15
17
from IPython .display import display
16
18
from matplotlib .patches import Ellipse
17
19
from numpy .random import *
18
- from datetime import datetime
19
20
20
21
from .Environment import Environment
21
22
from .Flight import Flight
22
23
from .Function import Function
23
24
from .Motor import HybridMotor , SolidMotor
24
25
from .Rocket import Rocket
26
+ from .utilities import invertedHaversine
25
27
26
28
27
29
class Dispersion :
@@ -1203,10 +1205,15 @@ def plotDrogueFullyVelocity(self, dispersion_results):
1203
1205
1204
1206
return None
1205
1207
1206
- def plotEllipses (self , dispersion_results , image , realLandingPoint ):
1208
+ def createEllipses (self , dispersion_results ):
1209
+ """A function to create apogee and impact ellipses from the dispersion
1210
+ results.
1207
1211
1208
- # Import background map
1209
- img = imread (image )
1212
+ Parameters
1213
+ ----------
1214
+ dispersion_results : dict
1215
+ A dictionary containing the results of the dispersion analysis.
1216
+ """
1210
1217
1211
1218
# Retrieve dispersion data por apogee and impact XY position
1212
1219
apogeeX = np .array (dispersion_results ["apogeeX" ])
@@ -1220,10 +1227,6 @@ def eigsorted(cov):
1220
1227
order = vals .argsort ()[::- 1 ]
1221
1228
return vals [order ], vecs [:, order ]
1222
1229
1223
- # Create plot figure
1224
- plt .figure (num = None , figsize = (8 , 6 ), dpi = 150 , facecolor = "w" , edgecolor = "k" )
1225
- ax = plt .subplot (111 )
1226
-
1227
1230
# Calculate error ellipses for impact
1228
1231
impactCov = np .cov (impactX , impactY )
1229
1232
impactVals , impactVecs = eigsorted (impactCov )
@@ -1242,14 +1245,14 @@ def eigsorted(cov):
1242
1245
)
1243
1246
impactEll .set_facecolor ((0 , 0 , 1 , 0.2 ))
1244
1247
impact_ellipses .append (impactEll )
1245
- ax .add_artist (impactEll )
1246
1248
1247
1249
# Calculate error ellipses for apogee
1248
1250
apogeeCov = np .cov (apogeeX , apogeeY )
1249
1251
apogeeVals , apogeeVecs = eigsorted (apogeeCov )
1250
1252
apogeeTheta = np .degrees (np .arctan2 (* apogeeVecs [:, 0 ][::- 1 ]))
1251
1253
apogeeW , apogeeH = 2 * np .sqrt (apogeeVals )
1252
1254
1255
+ apogee_ellipses = []
1253
1256
# Draw error ellipses for apogee
1254
1257
for j in [1 , 2 , 3 ]:
1255
1258
apogeeEll = Ellipse (
@@ -1260,7 +1263,51 @@ def eigsorted(cov):
1260
1263
color = "black" ,
1261
1264
)
1262
1265
apogeeEll .set_facecolor ((0 , 1 , 0 , 0.2 ))
1263
- ax .add_artist (apogeeEll )
1266
+ apogee_ellipses .append (apogeeEll )
1267
+ return impact_ellipses , apogee_ellipses
1268
+
1269
+ def plotEllipses (
1270
+ self ,
1271
+ dispersion_results ,
1272
+ image = None ,
1273
+ realLandingPoint = None ,
1274
+ perimeterSize = 3000 ,
1275
+ xlim = (- 3000 , 3000 ),
1276
+ ylim = (- 3000 , 3000 ),
1277
+ ):
1278
+ """A function to plot the error ellipses for the apogee and impact
1279
+ points of the rocket. The function also plots the real landing point, if
1280
+ given
1281
+
1282
+ Parameters
1283
+ ----------
1284
+ dispersion_results : dict
1285
+ A dictionary containing the results of the dispersion analysis
1286
+ image : str
1287
+ The path to the image to be used as the background
1288
+ realLandingPoint : tuple, optional
1289
+ A tuple containing the real landing point of the rocket, by default None
1290
+ """
1291
+ # Import background map
1292
+ if image is not None :
1293
+ img = imread (image )
1294
+
1295
+ # Retrieve dispersion data por apogee and impact XY position
1296
+ apogeeX = np .array (dispersion_results ["apogeeX" ])
1297
+ apogeeY = np .array (dispersion_results ["apogeeY" ])
1298
+ impactX = np .array (dispersion_results ["impactX" ])
1299
+ impactY = np .array (dispersion_results ["impactY" ])
1300
+
1301
+ impact_ellipses , apogee_ellipses = self .createEllipses (dispersion_results )
1302
+
1303
+ # Create plot figure
1304
+ plt .figure (num = None , figsize = (8 , 6 ), dpi = 150 , facecolor = "w" , edgecolor = "k" )
1305
+ ax = plt .subplot (111 )
1306
+
1307
+ for ell in impact_ellipses :
1308
+ ax .add_artist (ell )
1309
+ for ell in apogee_ellipses :
1310
+ ax .add_artist (ell )
1264
1311
1265
1312
# Draw launch point
1266
1313
plt .scatter (0 , 0 , s = 30 , marker = "*" , color = "black" , label = "Launch Point" )
@@ -1301,16 +1348,164 @@ def eigsorted(cov):
1301
1348
# You can translate the basemap by changing dx and dy (in meters)
1302
1349
dx = 0
1303
1350
dy = 0
1304
- plt .imshow (img , zorder = 0 , extent = [- 3000 - dx , 3000 - dx , - 3000 - dy , 3000 - dy ])
1351
+ if image is not None :
1352
+ plt .imshow (
1353
+ img ,
1354
+ zorder = 0 ,
1355
+ extent = [
1356
+ - perimeterSize - dx ,
1357
+ perimeterSize - dx ,
1358
+ - perimeterSize - dy ,
1359
+ perimeterSize - dy ,
1360
+ ],
1361
+ )
1305
1362
plt .axhline (0 , color = "black" , linewidth = 0.5 )
1306
1363
plt .axvline (0 , color = "black" , linewidth = 0.5 )
1307
- plt .xlim (- 3000 , 3000 )
1308
- plt .ylim (- 3000 , 3000 )
1364
+ plt .xlim (* xlim )
1365
+ plt .ylim (* ylim )
1309
1366
1310
1367
# Save plot and show result
1311
1368
plt .savefig (str (self .filename ) + ".pdf" , bbox_inches = "tight" , pad_inches = 0 )
1312
1369
plt .savefig (str (self .filename ) + ".svg" , bbox_inches = "tight" , pad_inches = 0 )
1313
1370
plt .show ()
1371
+ return None
1372
+
1373
+ def prepareEllipses (self , ellipses , origin_lat , origin_lon , resolution = 100 ):
1374
+ """Generate a list of latitude and longitude points for each ellipse in
1375
+ ellipses.
1376
+
1377
+ Parameters
1378
+ ----------
1379
+ ellipses : list
1380
+ List of matplotlib.patches.Ellipse objects.
1381
+ origin_lat : float
1382
+ Latitude of the origin of the coordinate system.
1383
+ origin_lon : float
1384
+ Longitude of the origin of the coordinate system.
1385
+ resolution : int, optional
1386
+ Number of points to generate for each ellipse, by default 100
1387
+ """
1388
+ outputs = []
1389
+
1390
+ for ell in ellipses :
1391
+ # Get ellipse path points
1392
+ center = ell .get_center ()
1393
+ width = ell .get_width ()
1394
+ height = ell .get_height ()
1395
+ angle = np .deg2rad (ell .get_angle ())
1396
+ points = []
1397
+
1398
+ for i in range (resolution ):
1399
+ x = width / 2 * math .cos (2 * np .pi * i / resolution )
1400
+ y = height / 2 * math .sin (2 * np .pi * i / resolution )
1401
+ x_rot = center [0 ] + x * math .cos (angle ) - y * math .sin (angle )
1402
+ y_rot = center [1 ] + x * math .sin (angle ) + y * math .cos (angle )
1403
+ points .append ((x_rot , y_rot ))
1404
+ points = np .array (points )
1405
+
1406
+ # Convert path points to lat/lon
1407
+ lat_lon_points = []
1408
+ for point in points :
1409
+ x = point [0 ]
1410
+ y = point [1 ]
1411
+
1412
+ # Convert to distance and bearing
1413
+ d = math .sqrt ((x ** 2 + y ** 2 ))
1414
+ bearing = math .atan2 (
1415
+ x , y
1416
+ ) # math.atan2 returns the angle in the range [-pi, pi]
1417
+
1418
+ lat_lon_points .append (
1419
+ invertedHaversine (
1420
+ origin_lat , origin_lon , d , bearing , eRadius = 6.3781e6
1421
+ )
1422
+ )
1423
+
1424
+ # Export string
1425
+ outputs .append (lat_lon_points )
1426
+ return outputs
1427
+
1428
+ def exportEllipsesToKML (
1429
+ self ,
1430
+ dispersion_results ,
1431
+ filename ,
1432
+ origin_lat ,
1433
+ origin_lon ,
1434
+ type = "all" ,
1435
+ resolution = 100 ,
1436
+ color = "ff0000ff" ,
1437
+ ):
1438
+ """Generates a KML file with the ellipses on the impact point.
1439
+ Parameters
1440
+ ----------
1441
+ dispersion_results : dict
1442
+ Contains dispersion results from the Monte Carlo simulation.
1443
+ filename : String
1444
+ Name to the KML exported file.
1445
+ origin_lat : float
1446
+ Latitude coordinate of Ellipses' geometric center, in degrees.
1447
+ origin_lon : float
1448
+ Latitude coordinate of Ellipses' geometric center, in degrees.
1449
+ type : String
1450
+ Type of ellipses to be exported. Options are: 'all', 'impact' and
1451
+ 'apogee'. Default is 'all', it exports both apogee and impact
1452
+ ellipses.
1453
+ resolution : int
1454
+ Number of points to be used to draw the ellipse. Default is 100.
1455
+ color : String
1456
+ Color of the ellipse. Default is 'ff0000ff', which is red.
1457
+ """
1458
+
1459
+ impact_ellipses , apogee_ellipses = self .createEllipses (dispersion_results )
1460
+ outputs = []
1461
+
1462
+ if type == "all" or type == "impact" :
1463
+ outputs = outputs + self .prepareEllipses (
1464
+ impact_ellipses , origin_lat , origin_lon , resolution = resolution
1465
+ )
1466
+
1467
+ if type == "all" or type == "apogee" :
1468
+ outputs = outputs + self .prepareEllipses (
1469
+ apogee_ellipses , origin_lat , origin_lon , resolution = resolution
1470
+ )
1471
+
1472
+ # Prepare data to KML file
1473
+ kml_data = []
1474
+ for i in range (len (outputs )):
1475
+ temp = []
1476
+ for j in range (len (outputs [i ])):
1477
+ temp .append ((outputs [i ][j ][1 ], outputs [i ][j ][0 ])) # log, lat
1478
+ kml_data .append (temp )
1479
+
1480
+ # Export to KML
1481
+ kml = simplekml .Kml ()
1482
+
1483
+ for i in range (len (outputs )):
1484
+ if (type == "all" and i < 3 ) or (type == "impact" ):
1485
+ ellName = "Impact σ" + str (i + 1 )
1486
+ elif type == "all" and i >= 3 :
1487
+ ellName = "Apogee σ" + str (i - 2 )
1488
+ else :
1489
+ ellName = "Apogee σ" + str (i + 1 )
1490
+
1491
+ mult_ell = kml .newmultigeometry (name = ellName )
1492
+ mult_ell .newpolygon (
1493
+ outerboundaryis = kml_data [i ],
1494
+ name = "Ellipse " + str (i ),
1495
+ )
1496
+ # Setting ellipse style
1497
+ mult_ell .tessellate = 1
1498
+ mult_ell .visibility = 1
1499
+ # mult_ell.innerboundaryis = kml_data
1500
+ # mult_ell.outerboundaryis = kml_data
1501
+ mult_ell .style .linestyle .color = color
1502
+ mult_ell .style .linestyle .width = 3
1503
+ mult_ell .style .polystyle .color = simplekml .Color .changealphaint (
1504
+ 100 , simplekml .Color .blue
1505
+ )
1506
+
1507
+ kml .save (filename )
1508
+ return None
1314
1509
1315
1510
def meanLateralWindSpeed (self , dispersion_results ):
1316
1511
print (
0 commit comments