org.nuiton.jaxx.widgets.gis.DmsCoordinate Maven / Gradle / Ivy
package org.nuiton.jaxx.widgets.gis;
/*
* #%L
* JAXX :: Widgets
* %%
* Copyright (C) 2008 - 2024 Code Lutin, Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import io.ultreia.java4all.i18n.I18n;
import org.jdesktop.beans.AbstractSerializableBean;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.regex.Pattern;
/**
* Geo coordinate in degree, minute, second format.
*
* Created on 10/23/13.
*
* @author Tony Chemit - [email protected]
* @since 2.12
*/
public class DmsCoordinate extends AbstractSerializableBean implements Coordinate {
public static final String COORDINATE_STRING_PATTERN = "%s%s°%s'%s''";
public static final Pattern COORDINATE_PATTERN =
Pattern.compile("(.*)°(.*)'(.*)''");
public static final String PROPERTY_SIGN = "sign";
public static final String PROPERTY_DEGREE = "degree";
public static final String PROPERTY_MINUTE = "minute";
public static final String PROPERTY_SECOND = "second";
private static final long serialVersionUID = 1L;
protected boolean sign;
protected Integer degree;
protected Integer minute;
protected Integer second;
public static DmsCoordinate empty() {
return new DmsCoordinate();
}
/**
* Methode statique de fabrique de position a partir d'un autre {@link DmsCoordinate}.
*
* Note : Si la valeur vaut null
, alors on
* reinitialise les composants de la position a null
et la
* methode {@link #isNull()} vaudra alors {@code true}.
*
* @param decimal la valeur au format decimal
* @return une nouvelle instance de position convertie
*/
public static DmsCoordinate valueOf(DmsCoordinate decimal) {
DmsCoordinate r = new DmsCoordinate();
if (decimal != null) {
r.setSign(decimal.isSign());
r.setDegree(decimal.getDegree());
r.setMinute(decimal.getMinute());
r.setSecond(decimal.getSecond());
}
return r;
}
/**
* Methode statique de fabrique de position a partir d'une valeur du format
* decimal.
*
* Note : Si la valeur (au format decimal) vaut null
, alors on
* reinitialise les composants de la position a null
et la
* methode {@link #isNull()} vaudra alors {@code true}.
*
* @param decimal la valeur au format decimal
* @return une nouvelle instance de position convertie
*/
public static DmsCoordinate valueOf(Float decimal) {
DmsCoordinate r = new DmsCoordinate();
r.fromDecimal(decimal);
return r;
}
/**
* Methode statique de fabrique de position a partir d'une valeur du format
* degre-minute-seconde.
*
* @param sign le signe
* @param d la valeur des degres
* @param m la valeur des minutes
* @param s la valeur des secondes
* @return une nouvelle instance de position convertie
*/
public static DmsCoordinate valueOf(boolean sign, Integer d, Integer m, Integer s) {
DmsCoordinate r = new DmsCoordinate();
r.setSign(sign);
r.setDegree(d);
r.setMinute(m);
r.setSecond(s);
return r;
}
@Override
public CoordinateFormat format() {
return CoordinateFormat.dms;
}
public boolean isSign() {
return sign;
}
public void setSign(boolean sign) {
Object oldValue = isSign();
this.sign = sign;
firePropertyChange(PROPERTY_SIGN, oldValue, sign);
}
public Integer getDegree() {
return degree;
}
public void setDegree(Integer degree) {
Object oldValue = getDegree();
this.degree = degree;
firePropertyChange(PROPERTY_DEGREE, oldValue, degree);
}
public Integer getMinute() {
return minute;
}
public void setMinute(Integer minute) {
Object oldValue = getMinute();
this.minute = minute;
firePropertyChange(PROPERTY_MINUTE, oldValue, minute);
}
public Integer getSecond() {
return second;
}
public void setSecond(Integer second) {
Object oldValue = getSecond();
this.second = second;
firePropertyChange(PROPERTY_SECOND, oldValue, second);
}
public boolean isDegreeNull() {
return degree == null || degree == 0;
}
public boolean isMinuteNull() {
return minute == null || minute == 0;
}
public boolean isSecondNull() {
return second == null || second == 0;
}
/**
* @return {@code true} si aucune composante n'est renseignée,
* {@code false} autrement.
*/
@Override
public boolean isNull() {
return degree == null && minute == null && second == null;
}
/**
* Mets a jour les composants de la position a partir d'une valeur decimal.
*
* Note : Si la valeur (au format decimal) vaut null
, alors on
* reinitialise les composants de la position a null
et la
* methode {@link #isNull()} vaudra alors {@code true}.
*
* @param decimal la valeur decimale a convertir (qui peut etre nulle).
*/
@Override
public void fromDecimal(Float decimal) {
Integer d = null;
Integer m = null;
Integer s = null;
boolean si = false;
if (decimal != null) {
si = decimal < 0;
decimal = Math.abs(decimal);
int remain = 0;
d = (int) (Math.round(decimal + 0.5) - 1);
m = 0;
s = 0;
decimal = 60.0f * (decimal - d);
if (decimal > 0) {
m = (int) (Math.round(decimal + 0.5) - 1);
decimal = 60 * (decimal - m);
if (decimal > 0) {
s = (int) (Math.round(decimal + 0.5) - 1);
remain = (int) (10 * (decimal - s));
}
}
if (remain > 9) {
s++;
}
if (s == 60) {
m++;
s = 0;
}
if (m == 60) {
d++;
m = 0;
}
}
degree = d;
minute = m;
second = s;
sign = si;
if (decimal != null) {
removeTrailingZero();
}
}
@Override
public Float toDecimal() {
if (isNull()) {
return null;
}
int d = getNotNullDegree();
int m = getNotNullMinute();
int s = getNotNullSecond();
Float result = (float) d;
if (m > 0) {
result += (float) m / 60;
if (s == 0) {
result += 0.5f / 3600;
}
}
if (s > 0) {
result += ((float) s + 0.5f) / 3600;
}
if (sign) {
result *= -1;
}
result = CoordinateHelper.roundToFourDecimals(result);
return result;
}
@Override
public void addTrailingZero() {
if (degree == null) {
degree = 0;
}
if (minute == null) {
minute = 0;
}
if (second == null) {
second = 0;
}
}
@Override
public void removeTrailingZero() {
if (degree != null && degree == 0) {
degree = null;
}
if (minute != null && minute == 0) {
minute = null;
}
if (second != null && second == 0) {
second = null;
}
}
@Override
public void validateLatitude(BiConsumer messageConsumer) {
Integer degree = getDegree();
if (degree != null) {
if (degree > 90) {
messageConsumer.accept(I18n.n("jaxx.validation.coordinate.degree.latitude.outOfBound"), degree);
}
}
Integer minute = getMinute();
if (minute != null) {
if (minute > 59) {
messageConsumer.accept(I18n.n("jaxx.validation.coordinate.minute.latitude.outOfBound"), minute);
}
}
Integer second = getSecond();
if (second != null) {
if (second > 59) {
messageConsumer.accept(I18n.n("jaxx.validation.coordinate.second.latitude.outOfBound"), second);
}
}
}
@Override
public void validateLongitude(BiConsumer messageConsumer) {
Integer degree = getDegree();
if (degree != null) {
if (degree > 180) {
messageConsumer.accept(I18n.n("jaxx.validation.coordinate.degree.longitude.outOfBound"), degree);
}
}
Integer minute = getMinute();
if (minute != null) {
if (minute > 59) {
messageConsumer.accept(I18n.n("jaxx.validation.coordinate.minute.longitude.outOfBound"), minute);
}
Integer second = getSecond();
if (second != null) {
if (second > 59) {
messageConsumer.accept(I18n.n("jaxx.validation.coordinate.second.longitude.outOfBound"), second);
}
}
}
}
public Integer getSignedDegree() {
Integer result = null;
if (!isDegreeNull()) {
result = degree;
if (isSign()) {
result *= -1;
}
}
return result;
}
public int getNotNullDegree() {
return isDegreeNull() ? 0 : degree;
}
public int getNotNullMinute() {
return isMinuteNull() ? 0 : minute;
}
public int getNotNullSecond() {
return isSecondNull() ? 0 : second;
}
public boolean isLatitudeDegreeValid() {
return isDegreeValid(false);
}
public boolean isLongitudeDegreeValid() {
return isDegreeValid(true);
}
public boolean isMinuteValid() {
boolean result = true;
if (!isMinuteNull()) {
if (60 == minute) {
// check minute and second are null
result = isSecondNull();
} else {
result = 0 <= minute && minute < 60;
}
}
return result;
}
public boolean isSecondValid() {
return isSecondNull() || (0 <= second && second < 60);
}
@Override
public String toString() {
return "DmsCoordinateComponent{" +
"sign=" + sign +
", degree=" + degree +
", minute=" + minute +
", second=" + second +
'}';
}
protected boolean isDegreeValid(boolean longitude) {
boolean result = true;
if (!isDegreeNull()) {
int bound = longitude ? 180 : 90;
if (bound == degree) {
// check minute and second are null
result = isMinuteNull() && isSecondNull();
} else {
result = degree < bound;
}
}
return result;
}
public void reset() {
degree = null;
minute = null;
second = null;
}
}