001/** 002 * 003 * Copyright 2015-2017 Ishan Khanna, Fernando Ramirez, 2019-2020 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.geoloc.packet; 018 019import java.io.Serializable; 020import java.net.URI; 021import java.util.Date; 022 023import javax.xml.namespace.QName; 024 025import org.jivesoftware.smack.packet.ExtensionElement; 026import org.jivesoftware.smack.packet.Message; 027import org.jivesoftware.smack.util.EqualsUtil; 028import org.jivesoftware.smack.util.HashCode; 029import org.jivesoftware.smack.util.XmlStringBuilder; 030 031import org.jivesoftware.smackx.xdata.FormField; 032import org.jivesoftware.smackx.xdata.FormFieldChildElement; 033 034/** 035 * A GeoLocation Extension packet, which is used by the XMPP clients to exchange their respective geographic locations. 036 * 037 * @see <a href="http://www.xmpp.org/extensions/xep-0080.html">XEP-0080</a> 038 * @author Ishan Khanna 039 */ 040public final class GeoLocation implements Serializable, ExtensionElement, FormFieldChildElement { 041 042 private static final long serialVersionUID = 1L; 043 044 public static final String NAMESPACE = "http://jabber.org/protocol/geoloc"; 045 046 public static final String ELEMENT = "geoloc"; 047 048 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 049 050 public static final GeoLocation EMPTY_GEO_LOCATION = GeoLocation.builder().build(); 051 052 private final Double accuracy; 053 private final Double alt; 054 private final Double altAccuracy; 055 private final String area; 056 private final Double bearing; 057 private final String building; 058 private final String country; 059 private final String countryCode; 060 private final String datum; 061 private final String description; 062 private final Double error; 063 private final String floor; 064 private final Double lat; 065 private final String locality; 066 private final Double lon; 067 private final String postalcode; 068 private final String region; 069 private final String room; 070 private final Double speed; 071 private final String street; 072 private final String text; 073 private final Date timestamp; 074 private final String tzo; 075 private final URI uri; 076 077 private GeoLocation(Builder builder) { 078 accuracy = builder.accuracy; 079 alt = builder.alt; 080 altAccuracy = builder.altAccuracy; 081 area = builder.area; 082 bearing = builder.bearing; 083 building = builder.building; 084 country = builder.country; 085 countryCode = builder.countryCode; 086 datum = builder.datum; 087 description = builder.description; 088 error = builder.error; 089 floor = builder.floor; 090 lat = builder.lat; 091 locality = builder.locality; 092 lon = builder.lon; 093 postalcode = builder.postalcode; 094 region = builder.region; 095 room = builder.room; 096 speed = builder.speed; 097 street = builder.street; 098 text = builder.text; 099 timestamp = builder.timestamp; 100 tzo = builder.tzo; 101 uri = builder.uri; 102 } 103 104 public Double getAccuracy() { 105 return accuracy; 106 } 107 108 public Double getAlt() { 109 return alt; 110 } 111 112 public Double getAltAccuracy() { 113 return altAccuracy; 114 } 115 116 public String getArea() { 117 return area; 118 } 119 120 public Double getBearing() { 121 return bearing; 122 } 123 124 public String getBuilding() { 125 return building; 126 } 127 128 public String getCountry() { 129 return country; 130 } 131 132 public String getCountryCode() { 133 return countryCode; 134 } 135 136 public String getDatum() { 137 return datum; 138 } 139 140 public String getDescription() { 141 return description; 142 } 143 144 /** 145 * Get the error. 146 * 147 * @return the error. 148 * @deprecated use {@link #getAccuracy()} instead. 149 */ 150 @Deprecated 151 public Double getError() { 152 return error; 153 } 154 155 public String getFloor() { 156 return floor; 157 } 158 159 public Double getLat() { 160 return lat; 161 } 162 163 public String getLocality() { 164 return locality; 165 } 166 167 public Double getLon() { 168 return lon; 169 } 170 171 public String getPostalcode() { 172 return postalcode; 173 } 174 175 public String getRegion() { 176 return region; 177 } 178 179 public String getRoom() { 180 return room; 181 } 182 183 public Double getSpeed() { 184 return speed; 185 } 186 187 public String getStreet() { 188 return street; 189 } 190 191 public String getText() { 192 return text; 193 } 194 195 public Date getTimestamp() { 196 return timestamp; 197 } 198 199 public String getTzo() { 200 return tzo; 201 } 202 203 public URI getUri() { 204 return uri; 205 } 206 207 @Override 208 public QName getQName() { 209 return QNAME; 210 } 211 212 @Override 213 public String getElementName() { 214 return ELEMENT; 215 } 216 217 @Override 218 public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 219 XmlStringBuilder xml = new XmlStringBuilder(this); 220 xml.rightAngleBracket(); 221 xml.optElement("accuracy", accuracy); 222 xml.optElement("alt", alt); 223 xml.optElement("altaccuracy", altAccuracy); 224 xml.optElement("area", area); 225 xml.optElement("bearing", bearing); 226 xml.optElement("building", building); 227 xml.optElement("country", country); 228 xml.optElement("countrycode", countryCode); 229 xml.optElement("datum", datum); 230 xml.optElement("description", description); 231 xml.optElement("error", error); 232 xml.optElement("floor", floor); 233 xml.optElement("lat", lat); 234 xml.optElement("locality", locality); 235 xml.optElement("lon", lon); 236 xml.optElement("postalcode", postalcode); 237 xml.optElement("region", region); 238 xml.optElement("room", room); 239 xml.optElement("speed", speed); 240 xml.optElement("street", street); 241 xml.optElement("text", text); 242 xml.optElement("timestamp", timestamp); 243 xml.optElement("tzo", tzo); 244 xml.optElement("uri", uri); 245 xml.closeElement(this); 246 return xml; 247 } 248 249 @Override 250 public String getNamespace() { 251 return NAMESPACE; 252 } 253 254 private final HashCode.Cache hashCodeCache = new HashCode.Cache(); 255 256 @Override 257 public int hashCode() { 258 return hashCodeCache.getHashCode(c -> 259 c 260 .append(accuracy) 261 .append(alt) 262 .append(altAccuracy) 263 .append(area) 264 .append(bearing) 265 .append(building) 266 .append(country) 267 .append(countryCode) 268 .append(datum) 269 .append(description) 270 .append(error) 271 .append(floor) 272 .append(lat) 273 .append(locality) 274 .append(lon) 275 .append(postalcode) 276 .append(region) 277 .append(room) 278 .append(speed) 279 .append(street) 280 .append(text) 281 .append(timestamp) 282 .append(tzo) 283 .append(uri) 284 ); 285 } 286 287 @Override 288 public boolean equals(Object obj) { 289 return EqualsUtil.equals(this, obj, (e, o) -> { 290 e 291 .append(accuracy, o.accuracy) 292 .append(altAccuracy, o.altAccuracy) 293 .append(area, o.area) 294 .append(bearing, o.bearing) 295 .append(building, o.building) 296 .append(country, o.country) 297 .append(countryCode, o.countryCode) 298 .append(datum, o.datum) 299 .append(description, o.description) 300 .append(error, o.error) 301 .append(floor, o.floor) 302 .append(lat, o.lat) 303 .append(locality, o.locality) 304 .append(lon, o.lon) 305 .append(postalcode, o.postalcode) 306 .append(region, o.region) 307 .append(room, o.room) 308 .append(speed, o.speed) 309 .append(street, o.street) 310 .append(text, o.text) 311 .append(timestamp, o.timestamp) 312 .append(tzo, o.tzo) 313 .append(uri, o.uri) 314 ; 315 }); 316 } 317 318 /** 319 * Returns a new instance of {@link Builder}. 320 * @return Builder 321 */ 322 public static Builder builder() { 323 return new GeoLocation.Builder(); 324 } 325 326 @Override 327 public boolean isExclusiveElement() { 328 return true; 329 } 330 331 /** 332 * Returns the first GeoLocation, or <code>null</code> if it doesn't exist in {@link Message}. 333 * <br> 334 * @param message The Message stanza containing GeoLocation 335 * @return GeoLocation 336 */ 337 public static GeoLocation from(Message message) { 338 return message.getExtension(GeoLocation.class); 339 } 340 341 /** 342 * Returns the first GeoLocation, or <code>null</code> if it doesn't exist in {@link FormField}. 343 * <br> 344 * @param formField the Formfield containing GeoLocation 345 * @return GeoLocation 346 */ 347 public static GeoLocation from(FormField formField) { 348 return (GeoLocation) formField.getFormFieldChildElement(QNAME); 349 } 350 351 /** 352 * This class defines a builder class for {@link GeoLocation}. 353 * <br> 354 * {@link GeoLocation} instance can be obtained using {@link #build()} method as follows.<br><br> 355 * <code>GeoLocation.Builder builder = GeoLocation.builder(); <br> 356 * GeoLocation geoLocation = builder.build();</code> 357 * <br><br> 358 * To set GeoLocation parameters, use their respective setters. 359 */ 360 public static class Builder { 361 362 private Double accuracy; 363 private Double alt; 364 private Double altAccuracy; 365 private String area; 366 private Double bearing; 367 private String building; 368 private String country; 369 private String countryCode; 370 371 // If datum is not included, receiver MUST assume WGS84; receivers MUST implement WGS84; senders MAY use another 372 // datum, but it is not recommended. 373 private String datum = "WGS84"; 374 375 private String description; 376 private Double error; 377 private String floor; 378 private Double lat; 379 private String locality; 380 private Double lon; 381 private String postalcode; 382 private String region; 383 private String room; 384 private Double speed; 385 private String street; 386 private String text; 387 private Date timestamp; 388 private String tzo; 389 private URI uri; 390 391 /** 392 * Sets accuracy of horizontal GPS error in meters. 393 * 394 * @param accuracy accuracy in meters 395 * @return Builder 396 */ 397 public Builder setAccuracy(Double accuracy) { 398 this.accuracy = accuracy; 399 return this; 400 } 401 402 /** 403 * Sets Altitude in meters above or below sea level. 404 * 405 * @param alt altitude in meters 406 * @return Builder 407 */ 408 public Builder setAlt(Double alt) { 409 this.alt = alt; 410 return this; 411 } 412 413 /** 414 * Sets Vertical GPS error in meters. 415 * 416 * @param altAccuracy altAccuracy in meters 417 * @return Builder 418 */ 419 public Builder setAltAccuracy(Double altAccuracy) { 420 this.altAccuracy = altAccuracy; 421 return this; 422 } 423 424 /** 425 * Sets a named area such as a campus or neighborhood. 426 * 427 * @param area the named area 428 * @return Builder 429 */ 430 public Builder setArea(String area) { 431 this.area = area; 432 return this; 433 } 434 435 /** 436 * Sets GPS bearing (direction in which the entity is heading<br> 437 * to reach its next waypoint), measured in decimal degrees,<br> 438 * relative to true north. 439 * 440 * @param bearing bearing in decimal degrees 441 * @return Builder 442 */ 443 public Builder setBearing(Double bearing) { 444 this.bearing = bearing; 445 return this; 446 } 447 448 /** 449 * Sets a specific building on a street or in an area. 450 * 451 * @param building name of the building 452 * @return Builder 453 */ 454 public Builder setBuilding(String building) { 455 this.building = building; 456 return this; 457 } 458 459 /** 460 * Sets the nation where the user is located. 461 * 462 * @param country user's country of location 463 * @return Builder 464 */ 465 public Builder setCountry(String country) { 466 this.country = country; 467 return this; 468 } 469 470 /** 471 * Sets The ISO 3166 two-letter country code. 472 * 473 * @param countryCode two-letter country code 474 * @return Builder 475 */ 476 public Builder setCountryCode(String countryCode) { 477 this.countryCode = countryCode; 478 return this; 479 } 480 481 /** 482 * Sets GPS Datum. 483 * 484 * @param datum GPS datum 485 * @return Builder 486 */ 487 public Builder setDatum(String datum) { 488 this.datum = datum; 489 return this; 490 } 491 492 /** 493 * Sets A natural-language name for or description of the location. 494 * 495 * @param description description of the location 496 * @return Builder 497 */ 498 public Builder setDescription(String description) { 499 this.description = description; 500 return this; 501 } 502 503 /** 504 * Sets Horizontal GPS error in arc minutes;<br> 505 * this element is deprecated in favor of accuracy. 506 * 507 * @param error error in arc minutes 508 * @return Builder 509 * @deprecated use {@link #setAccuracy(Double)} instead. 510 */ 511 @Deprecated 512 public Builder setError(Double error) { 513 this.error = error; 514 return this; 515 } 516 517 /** 518 * Sets a particular floor in a building. 519 * 520 * @param floor floor in a building 521 * @return Builder 522 */ 523 public Builder setFloor(String floor) { 524 this.floor = floor; 525 return this; 526 } 527 528 /** 529 * Sets Latitude in decimal degrees North. 530 * 531 * @param lat latitude in decimal degrees 532 * @return Builder 533 */ 534 public Builder setLat(Double lat) { 535 this.lat = lat; 536 return this; 537 } 538 539 /** 540 * Sets Locality within the administrative region,<br> 541 * such as a town or city. 542 * 543 * @param locality locality in a region 544 * @return Builder 545 */ 546 public Builder setLocality(String locality) { 547 this.locality = locality; 548 return this; 549 } 550 551 /** 552 * Sets Longitude in decimal degrees East. 553 * 554 * @param lon longitude in decimal degrees 555 * @return Builder 556 */ 557 public Builder setLon(Double lon) { 558 this.lon = lon; 559 return this; 560 } 561 562 /** 563 * Sets PostalCode used for postal delivery. 564 * 565 * @param postalcode code for postal delivery 566 * @return Builder 567 */ 568 public Builder setPostalcode(String postalcode) { 569 this.postalcode = postalcode; 570 return this; 571 } 572 573 /** 574 * Sets an administrative region of the nation,<br> 575 * such as a state or province. 576 * 577 * @param region an administrative region 578 * @return Builder 579 */ 580 public Builder setRegion(String region) { 581 this.region = region; 582 return this; 583 } 584 585 /** 586 * Sets a particular room in a building. 587 * 588 * @param room room inside a building 589 * @return Builder 590 */ 591 public Builder setRoom(String room) { 592 this.room = room; 593 return this; 594 } 595 596 /** 597 * Sets Speed at which the entity is moving, in meters per second. 598 * 599 * @param speed speed in meters per second 600 * @return Builder 601 */ 602 public Builder setSpeed(Double speed) { 603 this.speed = speed; 604 return this; 605 } 606 607 /** 608 * Sets a thoroughfare within the locality, or a crossing of two thoroughfares. 609 * 610 * @param street name of the street 611 * @return Builder 612 */ 613 public Builder setStreet(String street) { 614 this.street = street; 615 return this; 616 } 617 618 /** 619 * Sets a catch-all element that captures any other information about the location. 620 * 621 * @param text distinctive feature about the location 622 * @return Builder 623 */ 624 public Builder setText(String text) { 625 this.text = text; 626 return this; 627 } 628 629 /** 630 * Sets UTC timestamp specifying the moment when the reading was taken. 631 * 632 * @param timestamp timestamp of the reading 633 * @return Builder 634 */ 635 public Builder setTimestamp(Date timestamp) { 636 this.timestamp = timestamp; 637 return this; 638 } 639 640 /** 641 * Sets the time zone offset from UTC for the current location. 642 * 643 * @param tzo time zone offset 644 * @return Builder 645 */ 646 public Builder setTzo(String tzo) { 647 this.tzo = tzo; 648 return this; 649 } 650 651 /** 652 * Sets URI or URL pointing to information about the location. 653 * 654 * @param uri uri to the location 655 * @return Builder 656 */ 657 public Builder setUri(URI uri) { 658 this.uri = uri; 659 return this; 660 } 661 662 /** 663 * This method is called to build {@link GeoLocation} from the Builder. 664 * 665 * @return GeoLocation 666 */ 667 public GeoLocation build() { 668 return new GeoLocation(this); 669 } 670 } 671}