Packages
A configurable spherical geodesic grid data model designed for simulations GEOF Planet offers an ideal data model for simulations of 2½D spherical phenomena like oceanography and climate. It is ported from the JavaScript project g-e-o-f/peels.
Current section
Files
Jump to
Current section
Files
lib/geometry/interfield_centroids.ex
defmodule GEOF.Planet.Geometry.InterfieldCentroids do
@moduledoc """
Functions for computing the positions of the centroids between each
Field on a Sphere. This is used to determine the vertices of a
field's bounding polygon.
"""
alias GEOF.Planet.{
Sphere,
Field,
Geometry,
Geometry.FieldCentroids
}
###
#
# TYPES
#
###
@typedoc "`interfield_triangle`s are identified by the Fields whose centroids define their vertices."
@type interfield_triangle_index :: MapSet.t(Field.index())
@typedoc "Maps `interfield_triangle`s to the `position`s of their centroids."
@type interfield_centroid_sphere :: %{
required(interfield_triangle_index) => Geometry.position()
}
###
#
# FUNCTIONS
#
###
# Note: `divisions` is abbreviated as `d` in private functions.
##
# Utility functions
##
# Adds a new entry to an `interfield_centroid_sphere`.
@spec set_position(
interfield_centroid_sphere,
list(Field.index()),
Geometry.position()
) :: interfield_centroid_sphere
defp set_position(sphere, [index1, index2, index3], {:pos, lat, lon}) do
Map.put(sphere, MapSet.new([index1, index2, index3]), {:pos, lat, lon})
end
##
#
# Centroid sphere computation
#
##
# Convenience function without need for a `centroid_sphere`
@doc """
Computes a new `interfield_centroid_sphere`, computing its own `centroid_sphere`.
Deprecated outside of testing and examples.
"""
@spec interfield_centroids(Sphere.divisions()) :: interfield_centroid_sphere
def interfield_centroids(divisions) when is_integer(divisions) and divisions > 0 do
interfield_centroids(
FieldCentroids.field_centroids(divisions),
divisions
)
end
# Main function
@doc "Computes a new `interfield_centroid_sphere` with the provided `centroid_sphere`."
@spec interfield_centroids(FieldCentroids.centroid_sphere(), Sphere.divisions()) ::
interfield_centroid_sphere
def interfield_centroids(centroid_sphere, divisions)
when is_integer(divisions) and divisions > 0 do
Sphere.for_all_fields(
Map.new(),
divisions,
fn sphere, index ->
set_interfield_centroids_for_responsible_field(sphere, centroid_sphere, divisions, index)
end
)
end
# Computes centroids for the two triangles all non-polar fields are responsible for setting.
defp set_interfield_centroids_for_responsible_field(sphere, centroids, d, {:sxy, s, x, y}) do
index = {:sxy, s, x, y}
adj = Field.adjacents(index, d)
t1 = [adj.w, adj.nw, index]
t2 = [adj.sw, adj.w, index]
sphere
|> set_position(
t1,
Geometry.centroid(Enum.map(t1, fn index -> Map.get(centroids, index) end))
)
|> set_position(
t2,
Geometry.centroid(Enum.map(t2, fn index -> Map.get(centroids, index) end))
)
end
# Ignore polar fields, which are not responsible for any triangles.
defp set_interfield_centroids_for_responsible_field(sphere, _, _, _) do
sphere
end
end