mirror of
https://github.com/meteoinfo/MeteoInfo.git
synced 2025-12-08 20:36:05 +00:00
add Rotation module
This commit is contained in:
parent
151211d7a3
commit
b4a5ea2275
@ -473,9 +473,13 @@ public class MapPlot extends Plot2D implements IWebMapPanel {
|
||||
|
||||
//Draw boundary line
|
||||
if (this.boundary != null) {
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
PolygonBreak pb = (PolygonBreak)this.boundary.getLegend().clone();
|
||||
pb.setDrawFill(false);
|
||||
this.drawGraphic(g, this.boundary, pb, area);
|
||||
if (!this.antiAlias) {
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
g.setTransform(oldMatrix);
|
||||
|
||||
@ -67,7 +67,7 @@ import java.util.zip.ZipInputStream;
|
||||
public static String getVersion(){
|
||||
String version = GlobalUtil.class.getPackage().getImplementationVersion();
|
||||
if (version == null || version.equals("")) {
|
||||
version = "3.8.6";
|
||||
version = "3.8.7";
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
@ -163,8 +163,12 @@ public class DateTimeIndex extends Index<LocalDateTime> {
|
||||
*/
|
||||
@Override
|
||||
public String getNameFormat() {
|
||||
String str = this.dtFormatter.format(this.data.get(0));
|
||||
return "%" + String.valueOf(str.length()) + "s";
|
||||
if (this.data.isEmpty()) {
|
||||
return this.format;
|
||||
} else {
|
||||
String str = this.dtFormatter.format(this.data.get(0));
|
||||
return "%" + String.valueOf(str.length()) + "s";
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
|
||||
@ -69,6 +69,14 @@ public class Index<V> implements Iterable<V>{
|
||||
public int size(){
|
||||
return data.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the index is empty
|
||||
* @return Empty or not
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.data.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string format
|
||||
|
||||
@ -1,34 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<MeteoInfo File="milconfig.xml" Type="configurefile">
|
||||
<Path OpenPath="D:\Working\MIScript\Jython\mis\chart\text">
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\array"/>
|
||||
<Path OpenPath="D:\Working\MIScript\Jython\mis\plot_types\funny">
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\netcdf"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\radar"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\volume"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\dataframe"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\scatter"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite\FY"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\chart"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\chart\text"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite\FY"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\dataframe"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
|
||||
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\funny"/>
|
||||
</Path>
|
||||
<File>
|
||||
<OpenedFiles>
|
||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\scatter\scatterm_grid.py"/>
|
||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\satellite\FY\FY4A_DSD_1.py"/>
|
||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\satellite\FY\FY4A_AGRI_CIX_1.py"/>
|
||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\chart\text\text_align_axesm.py"/>
|
||||
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\funny\rose_multi.py"/>
|
||||
</OpenedFiles>
|
||||
<RecentFiles>
|
||||
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\scatter\scatterm_grid.py"/>
|
||||
<RecentFile File="D:\Working\MIScript\Jython\mis\satellite\FY\FY4A_DSD_1.py"/>
|
||||
<RecentFile File="D:\Working\MIScript\Jython\mis\satellite\FY\FY4A_AGRI_CIX_1.py"/>
|
||||
<RecentFile File="D:\Working\MIScript\Jython\mis\chart\text\text_align_axesm.py"/>
|
||||
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\funny\rose_multi.py"/>
|
||||
</RecentFiles>
|
||||
</File>
|
||||
<Font>
|
||||
@ -36,5 +34,5 @@
|
||||
</Font>
|
||||
<LookFeel DockWindowDecorated="true" LafDecorated="true" Name="FlatDarkLaf"/>
|
||||
<Figure DoubleBuffering="true"/>
|
||||
<Startup MainFormLocation="35,0" MainFormSize="1334,786"/>
|
||||
<Startup MainFormLocation="-7,0" MainFormSize="1394,829"/>
|
||||
</MeteoInfo>
|
||||
|
||||
Binary file not shown.
@ -233,7 +233,11 @@ class DataFrame(object):
|
||||
step = 1 if k.step is None else k.step
|
||||
rowkey = Range(sidx, eidx, step)
|
||||
elif isinstance(k, (list,tuple,np.NDArray,series.Series)):
|
||||
if isinstance(k[0], (int, bool)):
|
||||
if isinstance(k, series.Series):
|
||||
k0 = k.iloc[0]
|
||||
else:
|
||||
k0 = k[0]
|
||||
if isinstance(k0, (int, bool)):
|
||||
if isinstance(k, (list, tuple)):
|
||||
rowkey = k
|
||||
else:
|
||||
|
||||
Binary file not shown.
@ -32,13 +32,16 @@ class Series(object):
|
||||
if series is None:
|
||||
if isinstance(data, (list, tuple)):
|
||||
data = np.array(data)
|
||||
|
||||
if index is None:
|
||||
index = range(0, len(data))
|
||||
else:
|
||||
if len(data) != len(index):
|
||||
raise ValueError('Wrong length of index!')
|
||||
|
||||
if isinstance(index, np.NDArray):
|
||||
index = index.tolist()
|
||||
|
||||
if isinstance(index, Index):
|
||||
self._index = index
|
||||
else:
|
||||
@ -154,6 +157,8 @@ class Series(object):
|
||||
rowkey = Range(sidx, eidx, step)
|
||||
r = self._series.getValues(rowkey)
|
||||
return Series(series=r)
|
||||
elif isinstance(key, int):
|
||||
return self._getitem_iloc(key)
|
||||
else:
|
||||
r = self._series.getValueByIndex(key)
|
||||
if isinstance(r, MISeries):
|
||||
|
||||
@ -1 +1,2 @@
|
||||
import distance
|
||||
import distance
|
||||
import transform
|
||||
@ -0,0 +1,3 @@
|
||||
from _rotation import Rotation
|
||||
|
||||
__all__ = ['Rotation']
|
||||
@ -0,0 +1,270 @@
|
||||
|
||||
from org.apache.commons.geometry.euclidean.threed.rotation import QuaternionRotation, AxisReferenceFrame, \
|
||||
AxisSequence, AxisAngleSequence
|
||||
from org.apache.commons.geometry.euclidean.threed import Vector3D
|
||||
from org.meteoinfo.math.spatial.transform import TransformUtil
|
||||
|
||||
import re
|
||||
from numbers import Number
|
||||
from mipylib import numeric as np
|
||||
|
||||
|
||||
def _cross3(a, b):
|
||||
result = np.empty(3)
|
||||
result[0] = a[1]*b[2] - a[2]*b[1]
|
||||
result[1] = a[2]*b[0] - a[0]*b[2]
|
||||
result[2] = a[0]*b[1] - a[1]*b[0]
|
||||
return result
|
||||
|
||||
def _compose_quat_single(p, q, r):
|
||||
# calculate p * q into r
|
||||
cross = _cross3(p[:3], q[:3])
|
||||
|
||||
r[0] = p[3]*q[0] + q[3]*p[0] + cross[0]
|
||||
r[1] = p[3]*q[1] + q[3]*p[1] + cross[1]
|
||||
r[2] = p[3]*q[2] + q[3]*p[2] + cross[2]
|
||||
r[3] = p[3]*q[3] - p[0]*q[0] - p[1]*q[1] - p[2]*q[2]
|
||||
|
||||
def _compose_quat(p, q):
|
||||
n = max(p.shape[0], q.shape[0])
|
||||
product = np.empty((n, 4))
|
||||
|
||||
# dealing with broadcasting
|
||||
if p.shape[0] == 1:
|
||||
for ind in range(n):
|
||||
_compose_quat_single(p[0], q[ind], product[ind])
|
||||
elif q.shape[0] == 1:
|
||||
for ind in range(n):
|
||||
_compose_quat_single(p[ind], q[0], product[ind])
|
||||
else:
|
||||
for ind in range(n):
|
||||
_compose_quat_single(p[ind], q[ind], product[ind])
|
||||
|
||||
return product
|
||||
|
||||
def _make_elementary_quat(axis, angles):
|
||||
n = angles.shape[0]
|
||||
quat = np.zeros((n, 4))
|
||||
|
||||
axis_ind = 0
|
||||
if axis == b'x': axis_ind = 0
|
||||
elif axis == b'y': axis_ind = 1
|
||||
elif axis == b'z': axis_ind = 2
|
||||
|
||||
for ind in range(n):
|
||||
quat[ind, 3] = cos(angles[ind] / 2.)
|
||||
quat[ind, axis_ind] = sin(angles[ind] / 2.)
|
||||
return quat
|
||||
|
||||
def _elementary_quat_compose(seq, angles, intrinsic=False):
|
||||
result = _make_elementary_quat(seq[0], angles[:, 0])
|
||||
seq_len = seq.shape[0]
|
||||
|
||||
for idx in range(1, seq_len):
|
||||
if intrinsic:
|
||||
result = _compose_quat(
|
||||
result,
|
||||
_make_elementary_quat(seq[idx], angles[:, idx]))
|
||||
else:
|
||||
result = _compose_quat(
|
||||
_make_elementary_quat(seq[idx], angles[:, idx]),
|
||||
result)
|
||||
return result
|
||||
|
||||
def _format_angles(angles, degrees, num_axes):
|
||||
angles = np.asarray(angles, dtype='float')
|
||||
if degrees:
|
||||
angles = np.deg2rad(angles)
|
||||
|
||||
is_single = False
|
||||
# Prepare angles to have shape (num_rot, num_axes)
|
||||
if num_axes == 1:
|
||||
if angles.ndim == 0:
|
||||
# (1, 1)
|
||||
angles = angles.reshape((1, 1))
|
||||
is_single = True
|
||||
elif angles.ndim == 1:
|
||||
# (N, 1)
|
||||
angles = angles[:, None]
|
||||
elif angles.ndim == 2 and angles.shape[-1] != 1:
|
||||
raise ValueError("Expected `angles` parameter to have shape "
|
||||
"(N, 1), got {}.".format(angles.shape))
|
||||
elif angles.ndim > 2:
|
||||
raise ValueError("Expected float, 1D array, or 2D array for "
|
||||
"parameter `angles` corresponding to `seq`, "
|
||||
"got shape {}.".format(angles.shape))
|
||||
else: # 2 or 3 axes
|
||||
if angles.ndim not in [1, 2] or angles.shape[-1] != num_axes:
|
||||
raise ValueError("Expected `angles` to be at most "
|
||||
"2-dimensional with width equal to number "
|
||||
"of axes specified, got "
|
||||
"{} for shape".format(angles.shape))
|
||||
|
||||
if angles.ndim == 1:
|
||||
# (1, num_axes)
|
||||
angles = angles[None, :]
|
||||
is_single = True
|
||||
|
||||
# By now angles should have shape (num_rot, num_axes)
|
||||
# sanity check
|
||||
if angles.ndim != 2 or angles.shape[-1] != num_axes:
|
||||
raise ValueError("Expected angles to have shape (num_rotations, "
|
||||
"num_axes), got {}.".format(angles.shape))
|
||||
|
||||
return angles, is_single
|
||||
|
||||
def _get_axis_vector(axis):
|
||||
if axis == b'x': return Vector3D.of(1, 0, 0)
|
||||
elif axis == b'y': return Vector3D.of(0, 1, 0)
|
||||
elif axis == b'z': return Vector3D.of(0, 0, 1)
|
||||
else: return Vector3D.of(1, 0, 0)
|
||||
|
||||
def _get_axis_sequence(seq):
|
||||
if seq == 'xyz': return AxisSequence.XYZ
|
||||
elif seq == 'xzy': return AxisSequence.XZY
|
||||
elif seq == 'yxz': return AxisSequence.YXZ
|
||||
elif seq == 'yzx': return AxisSequence.YZX
|
||||
elif seq == 'zxy': return AxisSequence.ZXY
|
||||
elif seq == 'zyx': return AxisSequence.ZYX
|
||||
elif seq == 'xyx': return AxisSequence.XYX
|
||||
elif seq == 'xzx': return AxisSequence.XZX
|
||||
elif seq == 'yxy': return AxisSequence.YXY
|
||||
elif seq == 'yzy': return AxisSequence.YZY
|
||||
elif seq == 'zxz': return AxisSequence.ZXZ
|
||||
elif seq == 'zyz': return AxisSequence.ZYZ
|
||||
else: return AxisSequence.XYZ
|
||||
|
||||
class Rotation(object):
|
||||
|
||||
def __init__(self, rotation=None):
|
||||
if rotation is None:
|
||||
self._rotation = QuaternionRotation()
|
||||
else:
|
||||
self._rotation = rotation
|
||||
|
||||
@classmethod
|
||||
def from_euler(cls, seq, angles, degrees=False):
|
||||
"""Initialize from Euler angles.
|
||||
|
||||
Rotations in 3-D can be represented by a sequence of 3
|
||||
rotations around a sequence of axes. In theory, any three axes spanning
|
||||
the 3-D Euclidean space are enough. In practice, the axes of rotation are
|
||||
chosen to be the basis vectors.
|
||||
|
||||
The three rotations can either be in a global frame of reference
|
||||
(extrinsic) or in a body centred frame of reference (intrinsic), which
|
||||
is attached to, and moves with, the object under rotation [1]_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
seq : string
|
||||
Specifies sequence of axes for rotations. Up to 3 characters
|
||||
belonging to the set {'X', 'Y', 'Z'} for intrinsic rotations, or
|
||||
{'x', 'y', 'z'} for extrinsic rotations. Extrinsic and intrinsic
|
||||
rotations cannot be mixed in one function call.
|
||||
angles : float or array_like, shape (N,) or (N, [1 or 2 or 3])
|
||||
Euler angles specified in radians (`degrees` is False) or degrees
|
||||
(`degrees` is True).
|
||||
For a single character `seq`, `angles` can be:
|
||||
|
||||
- a single value
|
||||
- array_like with shape (N,), where each `angle[i]`
|
||||
corresponds to a single rotation
|
||||
- array_like with shape (N, 1), where each `angle[i, 0]`
|
||||
corresponds to a single rotation
|
||||
|
||||
For 2- and 3-character wide `seq`, `angles` can be:
|
||||
|
||||
- array_like with shape (W,) where `W` is the width of
|
||||
`seq`, which corresponds to a single rotation with `W` axes
|
||||
- array_like with shape (N, W) where each `angle[i]`
|
||||
corresponds to a sequence of Euler angles describing a single
|
||||
rotation
|
||||
|
||||
degrees : bool, optional
|
||||
If True, then the given angles are assumed to be in degrees.
|
||||
Default is False.
|
||||
|
||||
Returns
|
||||
-------
|
||||
rotation : `Rotation` instance
|
||||
Object containing the rotation represented by the sequence of
|
||||
rotations around given axes with given angles.
|
||||
"""
|
||||
num_axes = len(seq)
|
||||
if num_axes < 1 or num_axes > 3:
|
||||
raise ValueError("Expected axis specification to be a non-empty "
|
||||
"string of upto 3 characters, got {}".format(seq))
|
||||
|
||||
intrinsic = (re.match(r'^[XYZ]{1,3}$', seq) is not None)
|
||||
extrinsic = (re.match(r'^[xyz]{1,3}$', seq) is not None)
|
||||
if not (intrinsic or extrinsic):
|
||||
raise ValueError("Expected axes from `seq` to be from ['x', 'y', "
|
||||
"'z'] or ['X', 'Y', 'Z'], got {}".format(seq))
|
||||
|
||||
if any(seq[i] == seq[i+1] for i in range(num_axes - 1)):
|
||||
raise ValueError("Expected consecutive axes to be different, "
|
||||
"got {}".format(seq))
|
||||
|
||||
seq = seq.lower()
|
||||
|
||||
angles = np.asarray(angles, dtype='float')
|
||||
if degrees:
|
||||
angles = np.deg2rad(angles)
|
||||
|
||||
if angles.ndim == 0:
|
||||
axis_vector = _get_axis_vector(seq)
|
||||
rotation = QuaternionRotation.fromAxisAngle(axis_vector, angles.item())
|
||||
else:
|
||||
arf = AxisReferenceFrame.RELATIVE if intrinsic else AxisReferenceFrame.ABSOLUTE
|
||||
axis_sequence = _get_axis_sequence(seq)
|
||||
aas = AxisAngleSequence(arf, axis_sequence, angles[0], angles[1], angles[2])
|
||||
rotation = QuaternionRotation.fromAxisAngleSequence(aas)
|
||||
return cls(rotation)
|
||||
|
||||
def apply(self, vectors, inverse=False):
|
||||
"""Apply this rotation to a set of vectors.
|
||||
|
||||
If the original frame rotates to the final frame by this rotation, then
|
||||
its application to a vector can be seen in two ways:
|
||||
|
||||
- As a projection of vector components expressed in the final frame
|
||||
to the original frame.
|
||||
- As the physical rotation of a vector being glued to the original
|
||||
frame as it rotates. In this case the vector components are
|
||||
expressed in the original frame before and after the rotation.
|
||||
|
||||
In terms of rotation matrices, this application is the same as
|
||||
``self.as_matrix() @ vectors``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vectors : array_like, shape (3,) or (N, 3)
|
||||
Each `vectors[i]` represents a vector in 3D space. A single vector
|
||||
can either be specified with shape `(3, )` or `(1, 3)`. The number
|
||||
of rotations and number of vectors given must follow standard numpy
|
||||
broadcasting rules: either one of them equals unity or they both
|
||||
equal each other.
|
||||
inverse : boolean, optional
|
||||
If True then the inverse of the rotation(s) is applied to the input
|
||||
vectors. Default is False.
|
||||
|
||||
Returns
|
||||
-------
|
||||
rotated_vectors : ndarray, shape (3,) or (N, 3)
|
||||
Result of applying rotation on input vectors.
|
||||
Shape depends on the following cases:
|
||||
|
||||
- If object contains a single rotation (as opposed to a stack
|
||||
with a single rotation) and a single vector is specified with
|
||||
shape ``(3,)``, then `rotated_vectors` has shape ``(3,)``.
|
||||
- In all other cases, `rotated_vectors` has shape ``(N, 3)``,
|
||||
where ``N`` is either the number of rotations or vectors.
|
||||
"""
|
||||
vectors = np.asarray(vectors)
|
||||
if vectors.ndim > 2 or vectors.shape[-1] != 3:
|
||||
raise ValueError("Expected input of shape (3,) or (P, 3), "
|
||||
"got {}.".format(vectors.shape))
|
||||
|
||||
r = TransformUtil.rotation(self._rotation, vectors._array)
|
||||
return np.NDArray(r)
|
||||
@ -15,6 +15,7 @@
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<ejml.version>0.41.1</ejml.version>
|
||||
<commons-geometry.version>1.0</commons-geometry.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@ -43,6 +44,16 @@
|
||||
<artifactId>ojalgo</artifactId>
|
||||
<version>52.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-geometry-core</artifactId>
|
||||
<version>${commons-geometry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-geometry-euclidean</artifactId>
|
||||
<version>${commons-geometry.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacpp-platform</artifactId>
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
package org.meteoinfo.math.spatial.transform;
|
||||
|
||||
import org.apache.commons.geometry.core.Transform;
|
||||
import org.apache.commons.geometry.euclidean.threed.Vector3D;
|
||||
import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
|
||||
import org.meteoinfo.ndarray.Array;
|
||||
import org.meteoinfo.ndarray.DataType;
|
||||
|
||||
public class TransformUtil {
|
||||
/**
|
||||
* Quaternion rotation of 3D axis
|
||||
* @param quaternionRotation The rotation
|
||||
* @param array Input array
|
||||
* @return Rotated array
|
||||
*/
|
||||
public static Array rotation(QuaternionRotation quaternionRotation, Array array) {
|
||||
array = array.copyIfView();
|
||||
if (array.getRank() == 1) {
|
||||
Vector3D v1 = Vector3D.of(array.getDouble(0), array.getDouble(1), array.getDouble(2));
|
||||
Vector3D v2 = quaternionRotation.apply(v1);
|
||||
return Array.factory(DataType.DOUBLE, array.getShape(), v2.toArray());
|
||||
}
|
||||
int[] shape = array.getShape();
|
||||
int ni = shape[0];
|
||||
int nj = shape[1];
|
||||
Array r = Array.factory(DataType.DOUBLE, array.getShape());
|
||||
for (int i = 0; i < array.getSize(); i += 3) {
|
||||
Vector3D v1 = Vector3D.of(array.getDouble(i), array.getDouble(i + 1), array.getDouble(i + 2));
|
||||
Vector3D v2 = quaternionRotation.apply(v1);
|
||||
r.setDouble(i, v2.getX());
|
||||
r.setDouble(i + 1, v2.getY());
|
||||
r.setDouble(i + 2, v2.getZ());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
2
pom.xml
2
pom.xml
@ -35,7 +35,7 @@
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<revision>3.8.6</revision>
|
||||
<revision>3.8.7</revision>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<maven.compiler.release>8</maven.compiler.release>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user