You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

math.py 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import math
  2. from django.db.models.expressions import Func
  3. from django.db.models.fields import FloatField, IntegerField
  4. from django.db.models.functions import Cast
  5. from django.db.models.functions.mixins import (
  6. FixDecimalInputMixin, NumericOutputFieldMixin,
  7. )
  8. from django.db.models.lookups import Transform
  9. class Abs(Transform):
  10. function = 'ABS'
  11. lookup_name = 'abs'
  12. class ACos(NumericOutputFieldMixin, Transform):
  13. function = 'ACOS'
  14. lookup_name = 'acos'
  15. class ASin(NumericOutputFieldMixin, Transform):
  16. function = 'ASIN'
  17. lookup_name = 'asin'
  18. class ATan(NumericOutputFieldMixin, Transform):
  19. function = 'ATAN'
  20. lookup_name = 'atan'
  21. class ATan2(NumericOutputFieldMixin, Func):
  22. function = 'ATAN2'
  23. arity = 2
  24. def as_sqlite(self, compiler, connection, **extra_context):
  25. if not getattr(connection.ops, 'spatialite', False) or not (
  26. (4, 3, 0) <= connection.ops.spatial_version < (5, 0, 0)
  27. ):
  28. return self.as_sql(compiler, connection)
  29. # This function is usually ATan2(y, x), returning the inverse tangent
  30. # of y / x, but it's ATan2(x, y) on SpatiaLite >= 4.3.0, < 5.0.0.
  31. # Cast integers to float to avoid inconsistent/buggy behavior if the
  32. # arguments are mixed between integer and float or decimal.
  33. # https://www.gaia-gis.it/fossil/libspatialite/tktview?name=0f72cca3a2
  34. clone = self.copy()
  35. clone.set_source_expressions([
  36. Cast(expression, FloatField()) if isinstance(expression.output_field, IntegerField)
  37. else expression for expression in self.get_source_expressions()[::-1]
  38. ])
  39. return clone.as_sql(compiler, connection, **extra_context)
  40. class Ceil(Transform):
  41. function = 'CEILING'
  42. lookup_name = 'ceil'
  43. def as_oracle(self, compiler, connection, **extra_context):
  44. return super().as_sql(compiler, connection, function='CEIL', **extra_context)
  45. class Cos(NumericOutputFieldMixin, Transform):
  46. function = 'COS'
  47. lookup_name = 'cos'
  48. class Cot(NumericOutputFieldMixin, Transform):
  49. function = 'COT'
  50. lookup_name = 'cot'
  51. def as_oracle(self, compiler, connection, **extra_context):
  52. return super().as_sql(compiler, connection, template='(1 / TAN(%(expressions)s))', **extra_context)
  53. class Degrees(NumericOutputFieldMixin, Transform):
  54. function = 'DEGREES'
  55. lookup_name = 'degrees'
  56. def as_oracle(self, compiler, connection, **extra_context):
  57. return super().as_sql(
  58. compiler, connection,
  59. template='((%%(expressions)s) * 180 / %s)' % math.pi,
  60. **extra_context
  61. )
  62. class Exp(NumericOutputFieldMixin, Transform):
  63. function = 'EXP'
  64. lookup_name = 'exp'
  65. class Floor(Transform):
  66. function = 'FLOOR'
  67. lookup_name = 'floor'
  68. class Ln(NumericOutputFieldMixin, Transform):
  69. function = 'LN'
  70. lookup_name = 'ln'
  71. class Log(FixDecimalInputMixin, NumericOutputFieldMixin, Func):
  72. function = 'LOG'
  73. arity = 2
  74. def as_sqlite(self, compiler, connection, **extra_context):
  75. if not getattr(connection.ops, 'spatialite', False):
  76. return self.as_sql(compiler, connection)
  77. # This function is usually Log(b, x) returning the logarithm of x to
  78. # the base b, but on SpatiaLite it's Log(x, b).
  79. clone = self.copy()
  80. clone.set_source_expressions(self.get_source_expressions()[::-1])
  81. return clone.as_sql(compiler, connection, **extra_context)
  82. class Mod(FixDecimalInputMixin, NumericOutputFieldMixin, Func):
  83. function = 'MOD'
  84. arity = 2
  85. class Pi(NumericOutputFieldMixin, Func):
  86. function = 'PI'
  87. arity = 0
  88. def as_oracle(self, compiler, connection, **extra_context):
  89. return super().as_sql(compiler, connection, template=str(math.pi), **extra_context)
  90. class Power(NumericOutputFieldMixin, Func):
  91. function = 'POWER'
  92. arity = 2
  93. class Radians(NumericOutputFieldMixin, Transform):
  94. function = 'RADIANS'
  95. lookup_name = 'radians'
  96. def as_oracle(self, compiler, connection, **extra_context):
  97. return super().as_sql(
  98. compiler, connection,
  99. template='((%%(expressions)s) * %s / 180)' % math.pi,
  100. **extra_context
  101. )
  102. class Round(Transform):
  103. function = 'ROUND'
  104. lookup_name = 'round'
  105. class Sin(NumericOutputFieldMixin, Transform):
  106. function = 'SIN'
  107. lookup_name = 'sin'
  108. class Sqrt(NumericOutputFieldMixin, Transform):
  109. function = 'SQRT'
  110. lookup_name = 'sqrt'
  111. class Tan(NumericOutputFieldMixin, Transform):
  112. function = 'TAN'
  113. lookup_name = 'tan'