Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lambert projection #214

Merged
merged 60 commits into from
Sep 15, 2020
Merged
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
2b3c1df
Added dummy comment
friedkitteh Aug 24, 2020
a83086a
Remove dummy comment
friedkitteh Aug 24, 2020
2c75882
Added dummy comment
friedkitteh Aug 24, 2020
3cda1dd
Remove dummy comment
friedkitteh Aug 24, 2020
755a4cf
Merge remote-tracking branch 'upstream/master'
friedkitteh Sep 7, 2020
c3ead09
Merge branch 'master' of https://github.com/friedkitteh/kikuchipy
friedkitteh Sep 7, 2020
866ec42
Merge branch 'master' of https://github.com/pyxem/kikuchipy
friedkitteh Sep 9, 2020
fbcb884
Created lambert_projection.py for lambert projection
friedkitteh Sep 9, 2020
9a30050
Updated __init__.py in projections
friedkitteh Sep 9, 2020
05172a2
Created LambertProjection class in the same style as other projection…
friedkitteh Sep 9, 2020
7aee13e
Created TODOs on the empty methods
friedkitteh Sep 9, 2020
fc26945
"to lambert" equations should now be in place. Brain needed to decide…
friedkitteh Sep 9, 2020
eb81ad4
Fixed output vector for iproject method in Lambert projection
friedkitteh Sep 9, 2020
bd76589
Created empty methods for the conversion between lambert and gnomonic…
friedkitteh Sep 10, 2020
9c92848
Implemented lambert_to_gnomonic method. Required changing GnomonicPro…
friedkitteh Sep 10, 2020
7f3415d
Implemented gnomonic_to_lambert method. Required changing LambertProj…
friedkitteh Sep 10, 2020
535363d
Fixed LambertProjection.project method to check the conditional and t…
friedkitteh Sep 10, 2020
66b3618
Fixed LambertProjection.iproject method to check the conditional and …
friedkitteh Sep 10, 2020
2235e9c
Fixed typos
friedkitteh Sep 10, 2020
d28699a
Added explanation to eq_c method
friedkitteh Sep 10, 2020
1bb3d9b
Made eq_c a private method
friedkitteh Sep 10, 2020
1f149a4
Added Edge case fix for iproject method and optimize TOOD. {skip CI}
friedkitteh Sep 10, 2020
770c4ec
Moved/Deleted some inline comments. Added some TODOs {skip CI}
friedkitteh Sep 10, 2020
3337537
Added docstrings to every method in lambert_projection. {skip CI}
friedkitteh Sep 10, 2020
6e2860a
Added docstrings to every method in gnomonic_projection. {skip CI}
friedkitteh Sep 10, 2020
1754ed1
Started working briefly on some tests for the lambert projection modu…
friedkitteh Sep 10, 2020
918305b
Added TODOs for critical methods missing (found while writing tests).…
friedkitteh Sep 10, 2020
dc85379
[skip ci] Fixed x, y, z assignments in LambertProjection.project as m…
friedkitteh Sep 10, 2020
be4961c
[skip ci] LambertProjection.project now raises an error if it gets a …
friedkitteh Sep 10, 2020
0bbefbb
[skip ci] LambertProjection.project now only uses numpy arrays. Prett…
friedkitteh Sep 10, 2020
bef1002
[skip ci] LambertProjection.iproject now only uses numpy arrays. Pret…
friedkitteh Sep 10, 2020
f1e6377
[skip ci] LambertProjection.iproject now only uses numpy arrays. Pret…
friedkitteh Sep 10, 2020
51dcd35
[skip ci] Added TODO to assert that input vector in GnomicProjection.…
friedkitteh Sep 10, 2020
69794fd
[skip ci] Made the "on-sphere" checker less strict
friedkitteh Sep 11, 2020
faabd4a
[skip ci] Moved index variable to where it belongs :)
friedkitteh Sep 11, 2020
be80a19
Made tests for every method. I expect them to fail initially, mostly …
friedkitteh Sep 11, 2020
b551c1c
[skip ci] Fixed a typo in gnomonic_projection. np.atan2 -> np.arctan2
friedkitteh Sep 11, 2020
443580f
[skip ci] Made SphericalProjection.project a classmethod
friedkitteh Sep 11, 2020
3fe869c
Made tests for every method, and fixed the code where it was needed.
friedkitteh Sep 11, 2020
7fafe9c
[skip ci] Changed LambertProjection to only take one vector instead o…
friedkitteh Sep 11, 2020
dffeb8f
[skip ci] Helper function skeletons made
friedkitteh Sep 12, 2020
09164ff
[skip ci] Finished helper functions, and removed duplicated code
friedkitteh Sep 12, 2020
5829183
[skip ci] Finished writing tests
friedkitteh Sep 12, 2020
e799e0e
[skip ci] Updated type hints
friedkitteh Sep 12, 2020
29bff45
Replaced for-loops with numpy.where()
friedkitteh Sep 14, 2020
45e3d74
Updated docstring to fit
friedkitteh Sep 14, 2020
3ae51bf
Deleted a comment, woke travis
friedkitteh Sep 14, 2020
346dff6
Add @friedkitteh to credits, update changelog [skip ci]
hakonanes Sep 15, 2020
e0df859
Updated docstrings [skip ci]
friedkitteh Sep 15, 2020
26fcc5c
Fixed import statement in test_lambert_projection.py [skip ci]
friedkitteh Sep 15, 2020
262c4bf
Re-use arrays in LambertProjection.iproject [skip ci]
friedkitteh Sep 15, 2020
992b1be
Merge branch 'lambert_projection' of https://github.com/friedkitteh/k…
friedkitteh Sep 15, 2020
a0ff37d
Updated docstring [skip ci]
friedkitteh Sep 15, 2020
5e21f02
Added comments [skip ci]
friedkitteh Sep 15, 2020
416df35
Updated docstrings [skip ci]
friedkitteh Sep 15, 2020
8c195f1
Updated docstrings [skip ci]
friedkitteh Sep 15, 2020
6ef5217
Optimized project and iproject methods [skip ci]
friedkitteh Sep 15, 2020
0b291af
Optimized iproject method [skip ci]
friedkitteh Sep 15, 2020
7ac5d89
Optimized iproject method [skip ci]
friedkitteh Sep 15, 2020
5a19271
Ran "black ." to create a change to create a commit to ping travis
hakonanes Sep 15, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ Unreleased
Contributors
------------
- Håkon Wiik Ånes
- Lars Andreas Hastad Lervik

Added
-----
- Reader for EMsoft's simulated EBSD patterns returned by their ``EMEBSD.f90``
program. (`#202 <https://github.com/pyxem/kikuchipy/pull/202>`_)
- Modified Lambert mapping, and its inverse, from points on the unit sphere to a
2D square grid, as implemented in Callahan and De Graef (2013).
(`#214 <https://github.com/pyxem/kikuchipy/pull/214>`_)

Changed
-------
Expand Down
6 changes: 4 additions & 2 deletions kikuchipy/projections/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
ebsd_projections,
gnomonic_projection,
hesse_normal_form,
lambert_projection,
spherical_projection,
)

__all__ = [
"ebsd_projections",
"gnomonic_projection.py",
"gnomonic_projection",
"hesse_normal_form",
"spherical_projection.py",
"lambert_projection",
"spherical_projection",
]
5 changes: 4 additions & 1 deletion kikuchipy/projections/gnomonic_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
class GnomonicProjection(SphericalProjection):
"""Gnomonic projection of a vector as implemented in MTEX."""

@classmethod
def project(self, v: Union[Vector3d, np.ndarray]) -> np.ndarray:
"""Convert from Cartesian to the Gnomonic projection."""
polar = super().project(v)
theta, phi = polar[..., 0], polar[..., 1]

Expand All @@ -53,6 +55,7 @@ def project(self, v: Union[Vector3d, np.ndarray]) -> np.ndarray:

@staticmethod
def iproject(x: np.ndarray, y: np.ndarray) -> Vector3d:
"""Convert from the Gnomonic projection to Cartesian coordinates."""
theta = np.arctan(np.sqrt(x ** 2 + y ** 2))
phi = np.atan2(y, x)
phi = np.arctan2(y, x)
return Vector3d.from_polar(theta=theta, phi=phi)
127 changes: 127 additions & 0 deletions kikuchipy/projections/lambert_projection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
# Copyright 2019-2020 The kikuchipy developers
#
# This file is part of kikuchipy.
#
# kikuchipy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# kikuchipy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see <http://www.gnu.org/licenses/>.

from typing import Union

import numpy as np
from orix.vector import Vector3d

from kikuchipy.projections.gnomonic_projection import GnomonicProjection


class LambertProjection:
"""Lambert projection of a vector [Callahan2013]_."""

@classmethod
def project(cls, v: Union[Vector3d, np.ndarray]) -> np.ndarray:
"""Convert (n, 3) vector from Cartesian to the Lambert projection."""
if isinstance(v, Vector3d):
w = v.unit.data
x = w[..., 0]
y = w[..., 1]
z = w[..., 2]
else:
norm = np.sqrt(
np.sum(np.square([v[..., 0], v[..., 1], v[..., 2]]), axis=0)
)
x = v[..., 0] / norm
y = v[..., 1] / norm
z = v[..., 2] / norm

# Arrays used in both setting X and Y
sqrt_z = np.sqrt(2 * (1 - z))
sign_x = np.sign(x)
sign_y = np.sign(y)
abs_yx = abs(y) <= abs(x)

# Reusable constants
sqrt_pi = np.sqrt(np.pi)
sqrt_pi_half = sqrt_pi / 2
two_over_sqrt_pi = 2 / sqrt_pi

# Equations 10a and 10b from Callahan and De Graef (2013)
X = np.where(
hakonanes marked this conversation as resolved.
Show resolved Hide resolved
abs_yx,
sign_x * sqrt_z * sqrt_pi_half,
sign_y * sqrt_z * (two_over_sqrt_pi * np.arctan2(x, y)),
)
Y = np.where(
abs_yx,
sign_x * sqrt_z * (two_over_sqrt_pi * np.arctan2(y, x)),
sign_y * sqrt_z * sqrt_pi_half,
)

return np.column_stack((X, Y))

@staticmethod
def iproject(xy: np.ndarray) -> Vector3d:
"""Convert (n, 2) array from Lambert to Cartesian coordinates."""
X = xy[..., 0]
Y = xy[..., 1]

# Arrays used in setting x and y
true_term = Y * np.pi / (4 * X)
false_term = X * np.pi / (4 * Y)
abs_yx = abs(Y) <= abs(X)
c_x = _eq_c(X)
c_y = _eq_c(Y)

# Equations 8a and 8b from Callahan and De Graef (2013)
x = np.where(
abs_yx,
c_x * np.cos(true_term),
c_y * np.sin(false_term),
)
y = np.where(
abs_yx,
c_x * np.sin(true_term),
c_y * np.cos(false_term),
)
z = np.where(
abs_yx,
1 - (2 * (X ** 2)) / np.pi,
1 - (2 * (Y ** 2)) / np.pi,
)

return Vector3d(np.column_stack((x, y, z)))

@staticmethod
def lambert_to_gnomonic(xy: np.ndarray) -> np.ndarray:
"""Convert (n,2) array from Lambert via Cartesian coordinates to
Gnomonic."""
# These two functions could probably be combined into 1 to decrease
# runtime
vec = LambertProjection.iproject(xy)
xy = GnomonicProjection.project(vec)
return xy

@staticmethod
def gnomonic_to_lambert(xy: np.ndarray) -> np.ndarray:
"""Convert (n,2) array from Gnomonic via Cartesian coordinates to
Lambert."""
# These two functions could probably be combined into 1 to decrease
# runtime
vec = GnomonicProjection.iproject(xy[..., 0], xy[..., 1])
xy = LambertProjection.project(vec)
return xy


def _eq_c(p: Union[np.ndarray, float, int]) -> Union[np.ndarray, float, int]:
"""Private function used inside LambertProjection.iproject to increase
readability."""
return (2 * p / np.pi) * np.sqrt(np.pi - p ** 2)
1 change: 1 addition & 0 deletions kikuchipy/projections/spherical_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class SphericalProjection:

spherical_region = SphericalRegion([0, 0, 1])

@classmethod
def project(self, v: Union[Vector3d, np.ndarray]) -> np.ndarray:
"""Convert from cartesian to spherical coordinates."""
polar = get_polar(v)
Expand Down
17 changes: 17 additions & 0 deletions kikuchipy/projections/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright 2019-2020 The kikuchipy developers
#
# This file is part of kikuchipy.
#
# kikuchipy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# kikuchipy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see <http://www.gnu.org/licenses/>.
92 changes: 92 additions & 0 deletions kikuchipy/projections/tests/test_lambert_projection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
# Copyright 2019-2020 The kikuchipy developers
#
# This file is part of kikuchipy.
#
# kikuchipy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# kikuchipy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kikuchipy. If not, see <http://www.gnu.org/licenses/>.

import numpy as np
from orix.vector import Vector3d
import pytest

from kikuchipy.projections.lambert_projection import LambertProjection, _eq_c


class TestLambertProjection:
def test_project_vector3d(self):
"""Works for Vector3d objects with single and multiple vectors"""

vector_one = Vector3d((0.578, 0.578, 0.578))
output_a = LambertProjection.project(vector_one)
expected_a = np.array((0.81417, 0.81417))

assert output_a[..., 0][0] == pytest.approx(expected_a[..., 0], rel=1e4)
assert output_a[..., 1][0] == pytest.approx(expected_a[..., 1], rel=1e4)

vector_two = Vector3d(
np.array(
[[0.578, 0.578, 0.578], [0, 0.707, 0.707], [0.707, 0, 0.707]]
)
)
output_b = LambertProjection.project(vector_two)

expected_b = np.array(
(
(0.814172, 0.814172, 0.814172),
(0, 0, 0),
(0.6784123, 0, 0.6784123),
)
)

assert output_a[..., 0][0] == pytest.approx(expected_a[..., 0], rel=1e4)
assert output_a[..., 1][0] == pytest.approx(expected_a[..., 1], rel=1e4)

def test_project_ndarray(self):
"Works for numpy ndarrays"
ipt = np.array((0.578, 0.578, 0.578))
output = LambertProjection.project(ipt)
expected = np.array((0.81417, 0.81417))
assert output[..., 0][0] == pytest.approx(expected[..., 0], rel=1e4)

def test_iproject(self):
"""Conversion from Lambert to Cartesian coordinates works"""
vec = np.array((0.81417, 0.81417))
expected = Vector3d((0.57702409, 0.577, 0.578))
output = LambertProjection.iproject(vec)
assert np.allclose(output.x.data[0], expected.x.data[0], rtol=1e-05)

def test_eq_c(self):
"""Helper function works"""
arr_1 = np.array((0.578, 0.578, 0.578))
value = arr_1[..., 0]
output_arr = np.array((0.61655, 0.61655, 0.61655))
expected = output_arr[..., 0]
output = _eq_c(value)
assert output == pytest.approx(expected, rel=1e-5)

def test_lambert_to_gnomonic(self):
"""Conversion from Lambert to Gnomonic works"""
vec = np.array(
(0.81417, 0.81417)
) # Should give x,y,z = 1/sqrt(3) (1, 1, 1)
output = LambertProjection.lambert_to_gnomonic(vec)
expected = np.array((1, 1))
assert output[..., 0][0] == pytest.approx(expected[..., 0], abs=1e-2)

def test_gnomonic_to_lambert(self):
"""Conversion from Gnomonic to Lambert works"""
vec = np.array((1, 1)) # Similar to the case above
output = LambertProjection.gnomonic_to_lambert(vec)
expected = np.array((0.81417, 0.81417))
assert output[..., 0][0] == pytest.approx(expected[..., 0], rel=1e-3)
6 changes: 5 additions & 1 deletion kikuchipy/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@

author = "kikuchipy developers"
copyright = "Copyright 2019-2020, kikuchipy"
credits = ["Håkon Wiik Ånes", "Tina Bergh"]
credits = [
"Håkon Wiik Ånes",
"Tina Bergh",
"Lars Andreas Hastad Lervik",
]
license = "GPLv3+"
maintainer = "Håkon Wiik Ånes"
maintainer_email = "hakon.w.anes@ntnu.no"
Expand Down
1 change: 1 addition & 0 deletions kikuchipy/signals/ebsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1531,3 +1531,4 @@ def get_decomposition_model_write(
# Delete temporary files
os.remove(file_learn)
gc.collect() # Don't sink