add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / contrib / spatial / src / java / org / apache / lucene / spatial / geometry / shape / LLRect.java
1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package org.apache.lucene.spatial.geometry.shape;
19
20 import org.apache.lucene.spatial.geometry.FloatLatLng;
21 import org.apache.lucene.spatial.geometry.LatLng;
22
23
24
25 /**
26  * Lat-long rect.  Instances are mutable.
27  *
28  * <p><font color="red"><b>NOTE:</b> This API is still in
29  * flux and might change in incompatible ways in the next
30  * release.</font>
31  */
32 public class LLRect {
33   private LatLng ll, ur;
34   
35   public LLRect(LatLng ll, LatLng ur) {
36     this.ll=ll;
37     this.ur=ur;
38   }
39   
40   public LLRect(LLRect other) {
41     this.ll=other.ll;
42     this.ur=other.ur;
43   }
44   
45   /**
46    * Return the area in units of lat-lng squared.  This is a contrived unit
47    * that only has value when comparing to something else.
48    */
49   public double area() {
50     return Math.abs((ll.getLat()-ur.getLat()) * (ll.getLng()-ur.getLng()));
51   }
52
53   public LatLng getLowerLeft() {
54     return ll;
55   }
56   
57   public LatLng getUpperRight() {
58     return ur;
59   }
60   
61   @Override
62   public String toString() {
63     return "{" + ll + ", " + ur + "}";
64   }
65
66   public LatLng getMidpoint() {
67     return ll.calculateMidpoint(ur);
68   }
69
70   /**
71    * Approximates a box centered at the given point with the given width and height in miles.
72    * @param center
73    * @param widthMi
74    * @param heightMi
75    */
76   public static LLRect createBox(LatLng center, double widthMi, double heightMi) {
77     double d = widthMi;
78     LatLng ur = boxCorners(center, d, 45.0); // assume right angles
79     LatLng ll = boxCorners(center, d, 225.0);
80
81     //System.err.println("boxCorners: ur " + ur.getLat() + ',' + ur.getLng());
82     //System.err.println("boxCorners: cnt " + center.getLat() + ',' + center.getLng());
83     //System.err.println("boxCorners: ll " + ll.getLat() + ',' + ll.getLng());
84     return new LLRect(ll, ur);
85   }
86   
87   /**
88    * Returns a rectangle shape for the bounding box
89    */
90   public Rectangle toRectangle() {
91     return new Rectangle(ll.getLng(), ll.getLat(), ur.getLng(), ur.getLat());
92   }
93
94   private static LatLng boxCorners(LatLng center, double d, double brngdeg) {
95     double a = center.getLat();
96     double b = center.getLng();
97     double R = 3963.0; // radius of earth in miles
98     double brng = (Math.PI*brngdeg/180);
99     double lat1 = (Math.PI*a/180);
100     double lon1 = (Math.PI*b/180);
101
102     // Haversine formula
103     double lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
104                              Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );
105     double lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
106                                     Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));
107
108     lat2 = (lat2*180)/Math.PI;
109     lon2 = (lon2*180)/Math.PI;
110
111     // normalize long first
112     LatLng ll = normLng(lat2,lon2);
113
114     // normalize lat - could flip poles
115     ll = normLat(ll.getLat(),ll.getLng());
116
117     return ll;
118 }
119
120   /**
121    * Returns a normalized Lat rectangle shape for the bounding box
122    * If you go over the poles, you need to flip the lng value too
123    */
124   private static LatLng normLat(double lat, double lng) {
125     if (lat > 90.0) {
126         lat = 90.0 - (lat - 90.0);
127         if (lng < 0) {
128                 lng = lng+180;
129         } else {
130                 lng=lng-180;
131         }
132     }
133     else if (lat < -90.0) {
134         lat = -90.0 - (lat + 90.0);
135         if (lng < 0) {
136                 lng = lng+180;
137         } else {
138                 lng=lng-180;
139         }
140     }
141     LatLng ll=new FloatLatLng(lat, lng);
142     return ll;
143   }
144
145   /**
146    * Returns a normalized Lng rectangle shape for the bounding box
147    */
148   private static LatLng normLng(double lat,double lng) {
149     if (lng > 180.0) {
150         lng = -1.0*(180.0 - (lng - 180.0));
151     }
152     else if (lng < -180.0) {
153         lng = (lng + 180.0)+180.0;
154     }
155     LatLng ll=new FloatLatLng(lat, lng);
156     return ll;
157   }
158
159   @Override
160   public int hashCode() {
161     final int prime = 31;
162     int result = 1;
163     result = prime * result + ((ll == null) ? 0 : ll.hashCode());
164     result = prime * result + ((ur == null) ? 0 : ur.hashCode());
165     return result;
166   }
167
168   @Override
169   public boolean equals(Object obj) {
170     if (this == obj)
171       return true;
172     if (obj == null)
173       return false;
174     if (getClass() != obj.getClass())
175       return false;
176     LLRect other = (LLRect) obj;
177     if (ll == null) {
178       if (other.ll != null)
179         return false;
180     } else if (!ll.equals(other.ll))
181       return false;
182     if (ur == null) {
183       if (other.ur != null)
184         return false;
185     } else if (!ur.equals(other.ur))
186       return false;
187     return true;
188   }
189   
190   
191 }