models.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import subprocess
  4. import os
  5. from math import radians, degrees, sin, cos, atan2, sqrt
  6. from django.db import models
  7. from django.conf import settings
  8. from django.core.validators import MinValueValidator, MaxValueValidator
  9. from django.utils.encoding import python_2_unicode_compatible
  10. class Point(models.Model):
  11. latitude = models.FloatField(verbose_name="latitude", help_text="In degrees",
  12. validators=[MinValueValidator(-90),
  13. MaxValueValidator(90)])
  14. longitude = models.FloatField(verbose_name="longitude", help_text="In degrees",
  15. validators=[MinValueValidator(-180),
  16. MaxValueValidator(180)])
  17. altitude = models.FloatField(verbose_name="altitude", help_text="In meters",
  18. validators=[MinValueValidator(0.)])
  19. def line_distance(self, other):
  20. """Distance of the straight line between two points on Earth.
  21. Note that this is only useful because we are considering
  22. line-of-sight links, where straight-line distance is the relevant
  23. distance. For arbitrary points on Earth, great-circle distance
  24. would most likely be preferred.
  25. """
  26. earth_radius = 6371009
  27. lat, lon = radians(self.latitude), radians(self.longitude)
  28. alt = earth_radius + self.altitude
  29. lat2, lon2 = radians(other.latitude), radians(other.longitude)
  30. alt2 = earth_radius + other.altitude
  31. # Cosine of the angle between the two points on their great circle.
  32. cos_angle = sin(lat) * sin(lat2) + cos(lat) * cos(lat2) * cos(lon2 - lon)
  33. # Al-Kashi formula
  34. return sqrt(alt ** 2 + alt2 ** 2 - 2 * alt * alt2 * cos_angle)
  35. def bearing(self, other):
  36. """Bearing, in degrees, between this point and another point."""
  37. lat, lon = radians(self.latitude), radians(self.longitude)
  38. lat2, lon2 = radians(other.latitude), radians(other.longitude)
  39. y = sin(lon2 - lon) * cos(lat2)
  40. x = cos(lat) * sin(lat2) - sin(lat) * cos(lat2) * cos(lon2 - lon)
  41. return degrees(atan2(y, x))
  42. def elevation(self, other):
  43. """Elevation, in degrees, between this point and another point."""
  44. class Meta:
  45. abstract = True
  46. @python_2_unicode_compatible
  47. class Panorama(Point):
  48. name = models.CharField(verbose_name="name", max_length=255,
  49. help_text="Name of the panorama")
  50. loop = models.BooleanField(default=False, verbose_name="360° panorama",
  51. help_text="Whether the panorama loops around the edges")
  52. image = models.ImageField(verbose_name="image", upload_to="pano")
  53. def tiles_dir(self):
  54. return os.path.join(settings.MEDIA_ROOT, settings.PANORAMA_TILES_DIR,
  55. str(self.pk))
  56. def tiles_url(self):
  57. return os.path.join(settings.MEDIA_URL, settings.PANORAMA_TILES_DIR,
  58. str(self.pk))
  59. def to_dict(self):
  60. """Useful to pass information to the javascript code as JSON"""
  61. return {"id": self.id,
  62. "name": self.name,
  63. "loop": self.loop,
  64. "latitude": self.latitude,
  65. "longitude": self.longitude,
  66. "altitude": self.altitude,
  67. "tiles_url": self.tiles_url()}
  68. def generate_tiles(self):
  69. # The trailing slash is necessary for the shell script.
  70. tiles_dir = self.tiles_dir() + "/"
  71. try:
  72. os.makedirs(tiles_dir)
  73. except OSError:
  74. pass
  75. script = os.path.join(settings.BASE_DIR, "panorama", "gen_tiles.sh")
  76. ret = subprocess.call([script, "-p", tiles_dir, self.image.path])
  77. return ret
  78. def __str__(self):
  79. return self.name
  80. @python_2_unicode_compatible
  81. class ReferencePoint(Point):
  82. name = models.CharField(verbose_name="name", max_length=255,
  83. help_text="Name of the reference point")
  84. def to_dict(self):
  85. """Useful to pass information to the javascript code as JSON"""
  86. return {"id": self.id,
  87. "name": self.name,
  88. "latitude": self.latitude,
  89. "longitude": self.longitude,
  90. "altitude": self.altitude}
  91. def to_dict_extended(self, point):
  92. """Same as above, but also includes information relative
  93. to the given point: bearing, azimuth, distance."""
  94. d = self.to_dict()
  95. d['distance'] = self.line_distance(point)
  96. return d
  97. def __str__(self):
  98. return self.name