/** Simple GPS destination. No db query required */ public Destination(StorageService service, double lon, double lat) { GpsParams params = service.getGpsParams(); mPref = new Preferences(service.getApplicationContext()); if (null != params) { mLonInit = params.getLongitude(); mLatInit = params.getLatitude(); } else { mLonInit = lon; mLatInit = lat; } mInited = true; mService = service; mDbType = GPS; mFound = true; mLooking = false; mRunways = new LinkedList<Runway>(); mTrackShape = new TrackShape(); mEte = new String("--:--"); mEta = new String("--:--"); mFuel = new String("-.-"); mLond = lon; mLatd = lat; mParams = new LinkedHashMap<String, String>(); mFreq = new LinkedHashMap<String, String>(); mAwos = new LinkedList<Awos>(); mParams.put(DataBaseHelper.LONGITUDE, "" + mLond); mParams.put(DataBaseHelper.LATITUDE, "" + mLatd); mParams.put(DataBaseHelper.FACILITY_NAME, GPS); addTime(); mTrackShape.updateShape(new GpsParams(getLocationInit()), Destination.this); mAfdFound = null; mName = Helper.truncGeo(lat) + "&" + Helper.truncGeo(lon); mDestType = GPS; }
/** * Find if a GPS dst is valid * * @return */ public static boolean isGPSValid(String dst) { if (dst.contains("&")) { String tokens[] = dst.split("&"); try { double lon = Double.parseDouble(tokens[1]); double lat = Double.parseDouble(tokens[0]); if ((Helper.isLatitudeSane(lat)) && (Helper.isLongitudeSane(lon))) { return true; } } catch (Exception e) { } } return false; }
private void startClosestAirportTask(double x, double y) { // We won't be doing the airport long press under certain circumstances if (mDraw) { return; } if (null != mClosestTask) { mClosestTask.cancel(true); } mLongTouchDestination = null; mClosestTask = new ClosestAirportTask(); double lon2, lat2; if (mPref.isTrackUp()) { double c_x = mOrigin.getOffsetX(mGpsParams.getLongitude()); double c_y = mOrigin.getOffsetY(mGpsParams.getLatitude()); double thetab = mGpsParams.getBearing(); double p[]; p = Helper.rotateCoord(c_x, c_y, thetab, x, y); lon2 = mOrigin.getLongitudeOf(p[0]); lat2 = mOrigin.getLatitudeOf(p[1]); } else { lon2 = mOrigin.getLongitudeOf(x); lat2 = mOrigin.getLatitudeOf(y); } mClosestTask.execute(lon2, lat2); }
/** * @param name * @param type */ private void parseGps(String name, String type) { /* * GPS * GPS coordinates are either x&y (user), or addr@x&y (google maps) * get the x&y part, then parse them to lon=y lat=x */ if (name.contains("&")) { String token[] = new String[2]; token[1] = token[0] = name; if (name.contains("@")) { /* * This could be the geo point from maps */ token = name.split("@"); } /* * This is lon/lat destination */ String tokens[] = token[1].split("&"); try { mLond = Double.parseDouble(tokens[1]); mLatd = Double.parseDouble(tokens[0]); } catch (Exception e) { /* * Bad input from user on GPS */ mName = ""; mDestType = ""; return; } /* * Sane input */ if ((!Helper.isLatitudeSane(mLatd)) || (!Helper.isLongitudeSane(mLond))) { mName = ""; mDestType = ""; return; } mName = token[0]; mDestType = type; } }
/* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { /* * For display purpose */ if (!mFound) { return (mName + "? "); } else { return Helper.makeLine( mDistance, Preferences.distanceConversionUnit, mEte, mBearing, mDeclination); } }
/** @return */ private String getPriorityMessage() { if (mPointProjection != null) { String priorityMessage = Helper.makeLine2( mPointProjection.getDistance(), Preferences.distanceConversionUnit, mPointProjection.getGeneralDirectionFrom(mGpsParams.getDeclinition()), mPointProjection.getBearing(), mGpsParams.getDeclinition()); return priorityMessage; } return null; }
/** @param params */ public void initParams(GpsParams params, StorageService service) { /* * Comes from storage service. This will do nothing for fresh start, * but it will load previous combo on re-activation */ mService = service; mMovement = mService.getMovement(); mImageDataSource = mService.getDBResource(); if (null == mMovement) { mMovement = new Movement(); } mPan = mService.getPan(); if (null == mPan) { mPan = new Pan(); mService.setPan(mPan); } if (null != params) { mGpsParams = params; } else if (null != mService.getDestination()) { mGpsParams = new GpsParams(mService.getDestination().getLocation()); } else { mGpsParams = new GpsParams(null); } loadTiles(); postInvalidate(); // Tell the CDI the paint that we use for display tfr mService.getCDI().setSize(mPaint, Math.min(getWidth(), getHeight())); mService.getVNAV().setSize(mPaint, Math.min(getWidth(), getHeight())); // Tell the odometer how to access preferences mService.getOdometer().setPref(mPref); mService.getEdgeTape().setPaint(mPaint); // Resize our runway icon based upon the size of the display. // We want the icon no more than 1/3 the size of the screen. Since we show 2 images // of this icon, that means the total size is no more than 2/3 of the available space. // This leaves room to print the runway numbers with some real estate left over. Bitmap newRunway = Helper.getResizedBitmap(mRunwayBitmap.getBitmap(), getWidth(), getHeight(), (double) 1 / 3); // If a new bitmap was generated, then load it in. if (newRunway != mRunwayBitmap.getBitmap()) { mRunwayBitmap = new BitmapHolder(newRunway); } }
/* (non-Javadoc) * @see android.os.AsyncTask#doInBackground(Params[]) */ @Override protected Boolean doInBackground(Object... vals) { Thread.currentThread().setName("Destination"); Boolean guess = (Boolean) vals[0]; String dbType = (String) vals[1]; /* * If we dont know type, find with a guess. */ if (guess) { StringPreference s = mService.getDBResource().searchOne(mName); if (null == s) { return false; } mDestType = s.getType(); mName = s.getId(); } /* * If GPS/Maps, parse */ if (mName.contains("&")) { parseGps(mName, mDestType); } mWinds = mService.getDBResource().getWindsAloft(mLond, mLatd); if (mDestType.equals(UDW)) { Waypoint p = mService.getUDWMgr().get(mName); if (null != p) { mLatd = p.getLat(); mLond = p.getLon(); mCmt = p.getCmt(); mParams.put(DataBaseHelper.LONGITUDE, "" + mLond); mParams.put(DataBaseHelper.LATITUDE, "" + mLatd); mParams.put(DataBaseHelper.FACILITY_NAME, UDWMgr.UDWDESCRIPTION); addTime(); mAfdFound = null; mFound = true; mLooking = false; mDbType = UDW; mTrackShape.updateShape(new GpsParams(getLocationInit()), Destination.this); return true; } return false; } if (mDestType.equals(GPS)) { /* * For GPS coordinates, simply put parsed lon/lat in params * No need to query database */ mParams = new LinkedHashMap<String, String>(); mFreq = new LinkedHashMap<String, String>(); mAwos = new LinkedList<Awos>(); mParams.put(DataBaseHelper.LONGITUDE, "" + mLond); mParams.put(DataBaseHelper.LATITUDE, "" + mLatd); mParams.put(DataBaseHelper.FACILITY_NAME, GPS); addTime(); mAfdFound = null; mFound = true; mLooking = false; mDbType = GPS; mTrackShape.updateShape(new GpsParams(getLocationInit()), Destination.this); if (!isGPSValid(mName)) { mFound = false; } if (!mName.contains("&")) { /* * This comes from MAPS to GPS for user edited */ mName += "@" + mLatd + "&" + mLond; } return true; } if (null == mDataSource) { return false; } /* * For Google maps address, if we have already geo decoded it using internet, * then no need to do again because internet may not be available on flight. * It could be coming from storage and not google maps. */ if (mDestType.equals(MAPS)) { if (mLond == 0 && mLatd == 0) { /* * We have already decomposed it? * No. */ String strAddress = mName; Geocoder coder = new Geocoder(mService); Address location = null; /* * Decompose */ try { List<Address> address = coder.getFromLocationName(strAddress, 1); if (address != null) { location = address.get(0); } } catch (Exception e) { return false; } if (null == location) { return false; } /* * Decomposed it * */ try { mLond = Helper.truncGeo(location.getLongitude()); mLatd = Helper.truncGeo(location.getLatitude()); } catch (Exception e) { } if ((!Helper.isLatitudeSane(mLatd)) || (!Helper.isLongitudeSane(mLond))) { return false; } } /* * Common stuff */ mParams = new LinkedHashMap<String, String>(); mFreq = new LinkedHashMap<String, String>(); mAwos = new LinkedList<Awos>(); mAfdFound = null; mDbType = mDestType; mParams.put(DataBaseHelper.TYPE, mDestType); mParams.put(DataBaseHelper.FACILITY_NAME, mName); mParams.put(DataBaseHelper.LONGITUDE, "" + mLond); mParams.put(DataBaseHelper.LATITUDE, "" + mLatd); addTime(); mName += "@" + mLatd + "&" + mLond; return true; } /* * For all others, find in DB */ mDataSource.findDestination(mName, mDestType, dbType, mParams, mRunways, mFreq, mAwos); if (mDestType.equals(BASE)) { /* * Find A/FD */ mAfdFound = null; final LinkedList<String> afdName = mDataSource.findAFD(mName); if (afdName.size() > 0) { FilenameFilter filter = new FilenameFilter() { public boolean accept(File directory, String fileName) { boolean match = false; for (final String name : afdName) { match |= fileName.matches(name + Preferences.IMAGE_EXTENSION) || fileName.matches(name + "-[0-9]+" + Preferences.IMAGE_EXTENSION); } return match; } }; String afd[] = null; afd = new File(mPref.mapsFolder() + "/afd/").list(filter); if (null != afd) { java.util.Arrays.sort(afd); int len1 = afd.length; String tmp1[] = new String[len1]; for (int count = 0; count < len1; count++) { /* * Add A/FD */ String tokens[] = afd[count].split(Preferences.IMAGE_EXTENSION); tmp1[count] = mPref.mapsFolder() + "/afd/" + tokens[0]; } if (len1 > 0) { mAfdFound = tmp1; } } } } return (!mParams.isEmpty()); }
/** * Update the current speed, lat, lon, that will update ETA, distance and bearing to the * destination * * @param params */ public void updateTo(GpsParams params) { /* */ double mLon = params.getLongitude(); double mLat = params.getLatitude(); double speed = params.getSpeed(); mDeclination = params.getDeclinition(); if (!mFound) { return; } if (!mInited) { mLonInit = mLon; mLatInit = mLat; mInited = true; } /* * Project and find distance */ Projection p = new Projection(mLon, mLat, mLond, mLatd); mDistance = p.getDistance(); mBearing = p.getBearing(); // in flying mode, calculate time based on ground speed from GPS mGroundSpeed = speed; mWca = 0; mCrs = mBearing; if (mPref.isSimulationMode()) { double ws = 0; double wd = 0; if (mWinds != null) { double winds[] = mWinds.getWindAtAltitude(params.getAltitude()); ws = winds[0]; wd = winds[1]; } // in sim mode, do planning with winds speed = mPref.getAircraftTAS(); // in sim mode, use preferred TAS // from aviation formulary double hd = mBearing; mGroundSpeed = Math.sqrt( ws * ws + speed * speed - 2 * ws * speed * Math.cos((hd - wd) * Math.PI / 180.0)); mWca = -Math.toDegrees( Math.atan2( ws * Math.sin((hd - wd) * Math.PI / 180.0), speed - ws * Math.cos((hd - wd) * Math.PI / 180.0))); mCrs = (hd + mWca + 360) % 360; } else if (mPref.useBearingForETEA() && (!mService.getPlan().isActive())) { // This is just when we have a destination set and no plan is active // We can't assume that we are heading DIRECTLY for the destination, so // we need to figure out the multiply factor by taking the COS of the difference // between the bearing and the heading. double angDif = Helper.angularDifference(params.getBearing(), mBearing); double xFactor = 1; // If the difference is 90 or greater, then ETE means nothing as we are not // closing on the target if (angDif < 90) { // Calculate the actual relative speed closing on the target xFactor = Math.cos(angDif * Math.PI / 180); } mGroundSpeed *= xFactor; } /* * ETA when speed != 0 */ mEte = Helper.calculateEte(mDistance, mGroundSpeed, 0, true); if (mGroundSpeed == 0) { mEteSec = Long.MAX_VALUE; mFuelGallons = Float.MAX_VALUE; mFuel = "-.-"; } else { mEteSec = (long) (mDistance / mGroundSpeed * 3600); mFuelGallons = (float) mEteSec / 3600 * mPref.getFuelBurn(); mFuel = String.valueOf((float) Math.round(mFuelGallons * 10.f) / 10.f); } // Calculate the time of arrival at our destination. We SHOULD be taking in to account // the timezone at that location mEta = Helper.calculateEta(Calendar.getInstance().getTimeZone(), mDistance, mGroundSpeed); }
/* (non-Javadoc) * @see com.ds.avare.MultiTouchController.MultiTouchObjectCanvas#setPositionAndScale(java.lang.Object, com.ds.avare.MultiTouchController.PositionAndScale, com.ds.avare.MultiTouchController.PointInfo) */ public boolean setPositionAndScale( Object obj, PositionAndScale newObjPosAndScale, PointInfo touchPoint) { touchPointChanged(touchPoint); if (false == mCurrTouchPoint.isMultiTouch()) { /* * Do not move on drag */ if (mDragPlanPoint >= 0) { return true; } /* * Do not move on multitouch */ if (mDraw && mService != null) { float x = mCurrTouchPoint.getX() * mScale.getScaleFactor(); float y = mCurrTouchPoint.getY() * mScale.getScaleFactor(); /* * Threshold the drawing so we do not generate too many points */ if (mPref.isTrackUp()) { double thetab = mGpsParams.getBearing(); double p[] = new double[2]; double c_x = mOrigin.getOffsetX(mGpsParams.getLongitude()); double c_y = mOrigin.getOffsetY(mGpsParams.getLatitude()); p = Helper.rotateCoord(c_x, c_y, thetab, x, y); mService.getDraw().addPoint((float) p[0], (float) p[1], mOrigin); } else { mService.getDraw().addPoint(x, y, mOrigin); } return true; } // Pan if (mPan.setMove(newObjPosAndScale.getXOff(), newObjPosAndScale.getYOff())) { /* * Query when we have moved one tile. This will happen in background. */ loadTiles(); } } else { // Zooming does not change drag mDragPlanPoint = -1; /* * on double touch find distance and bearing between two points. */ if (mPointProjection == null) { double x0 = mCurrTouchPoint.getXs()[0]; double y0 = mCurrTouchPoint.getYs()[0]; double x1 = mCurrTouchPoint.getXs()[1]; double y1 = mCurrTouchPoint.getYs()[1]; double lon0, lat0, lon1, lat1; // convert to origin coord if Trackup if (mPref.isTrackUp()) { double c_x = mOrigin.getOffsetX(mGpsParams.getLongitude()); double c_y = mOrigin.getOffsetY(mGpsParams.getLatitude()); double thetab = mGpsParams.getBearing(); double p0[], p1[]; p0 = Helper.rotateCoord(c_x, c_y, thetab, x0, y0); p1 = Helper.rotateCoord(c_x, c_y, thetab, x1, y1); lon0 = mOrigin.getLongitudeOf(p0[0]); lat0 = mOrigin.getLatitudeOf(p0[1]); lon1 = mOrigin.getLongitudeOf(p1[0]); lat1 = mOrigin.getLatitudeOf(p1[1]); } else { lon0 = mOrigin.getLongitudeOf(x0); lat0 = mOrigin.getLatitudeOf(y0); lon1 = mOrigin.getLongitudeOf(x1); lat1 = mOrigin.getLatitudeOf(y1); } mPointProjection = new Projection(lon0, lat0, lon1, lat1); } /* * Clamp scaling. */ mScale.setScaleFactor(newObjPosAndScale.getScale()); } updateCoordinates(); invalidate(); return true; }
/** @param context */ private void setup(Context context) { /* * Set up all graphics. */ mContext = context; mPan = new Pan(); mScale = new Scale(MAX_SCALE); mOrigin = new Origin(); mMovement = new Movement(); mErrorStatus = null; mMacro = 1; mDragPlanPoint = -1; mImageDataSource = null; mGpsParams = new GpsParams(null); mPaint = new Paint(); mPaint.setAntiAlias(true); mPointProjection = null; mDraw = false; mPref = new Preferences(context); mFace = Typeface.createFromAsset(mContext.getAssets(), "LiberationMono-Bold.ttf"); mPaint.setTypeface(mFace); mPaint.setTextSize(getResources().getDimension(R.dimen.TextSize)); mTextPaint = new TextPaint(); mTextPaint.setAntiAlias(true); mTextPaint.setColor(Color.WHITE); mTextPaint.setTypeface(mFace); mTextPaint.setTextSize(R.dimen.TextSize); /* * Set up the paint for misc messages to display */ mMsgPaint = new Paint(); mMsgPaint.setAntiAlias(true); mMsgPaint.setTextSize(getResources().getDimension(R.dimen.distanceRingNumberTextSize)); /* * Set up the paint for the runways as much as possible here */ mRunwayPaint = new Paint(mPaint); mRunwayPaint.setTextSize(getResources().getDimension(R.dimen.runwayNumberTextSize)); setOnTouchListener(this); mAirplaneBitmap = DisplayIcon.getDisplayIcon(context, mPref); mLineBitmap = new BitmapHolder(context, R.drawable.line); mLineHeadingBitmap = new BitmapHolder(context, R.drawable.line_heading); mRunwayBitmap = new BitmapHolder(context, R.drawable.runway_extension); mMultiTouchC = new MultiTouchController<Object>(this); mCurrTouchPoint = new PointInfo(); mGestureDetector = new GestureDetector(context, new GestureListener()); // We're going to give the user twice the slop as normal final ViewConfiguration configuration = ViewConfiguration.get(context); int touchSlop = configuration.getScaledTouchSlop() * 2; mTouchSlopSquare = touchSlop * touchSlop; mDoCallbackWhenDone = false; mDipToPix = Helper.getDpiToPix(context); }
/* (non-Javadoc) * @see android.os.AsyncTask#onPostExecute(java.lang.Object) */ @Override protected void onPostExecute(String airport) { if (null != mGestureCallBack && null != mPointProjection && null != airport) { mLongTouchDestination = new LongTouchDestination(); mLongTouchDestination.airport = airport; mLongTouchDestination.info = Math.round(mPointProjection.getDistance()) + Preferences.distanceConversionUnit + "(" + mPointProjection.getGeneralDirectionFrom(mGpsParams.getDeclinition()) + ") " + Helper.correctConvertHeading( Math.round( Helper.getMagneticHeading( mPointProjection.getBearing(), mGpsParams.getDeclinition()))) + '\u00B0'; /* * Clear old weather */ mService.getAdsbWeather().sweep(); /* * Do not background ADSB weather as its a RAM opertation and quick, * also avoids concurrent mod exception. */ if (mPref.useAdsbWeather()) { taf = mService.getAdsbWeather().getTaf(airport); metar = mService.getAdsbWeather().getMETAR(airport); aireps = mService.getAdsbWeather().getAireps(lon, lat); wa = mService.getAdsbWeather().getWindsAloft(lon, lat); layer = mService.getAdsbWeather().getNexrad().getDate(); } else { boolean inWeatherOld = mService.getInternetWeatherCache().isOld(mPref.getExpiryTime()); if (inWeatherOld) { // expired weather and TFR text do not show taf = null; metar = null; aireps = null; textMets = null; tfr = null; wa = null; } } if (null != aireps) { for (Airep a : aireps) { a.updateTextWithLocation(lon, lat, mGpsParams.getDeclinition()); } } if (null != wa) { wa.updateStationWithLocation(lon, lat, mGpsParams.getDeclinition()); } mLongTouchDestination.tfr = tfr; mLongTouchDestination.taf = taf; mLongTouchDestination.metar = metar; mLongTouchDestination.airep = aireps; mLongTouchDestination.mets = textMets; mLongTouchDestination.wa = wa; mLongTouchDestination.freq = freq; mLongTouchDestination.sua = sua; mLongTouchDestination.layer = layer; mLongTouchDestination.fuel = fuel; mLongTouchDestination.ratings = ratings; if (metar != null) { mLongTouchDestination.performance = WeatherHelper.getMetarTime(metar.rawText) + "\n" + mContext.getString(R.string.DensityAltitude) + " " + WeatherHelper.getDensityAltitude(metar.rawText, elev) + "\n" + mContext.getString(R.string.BestRunway) + " " + WeatherHelper.getBestRunway(metar.rawText, runways); } // If the long press event has already occurred, we need to do the gesture callback here if (mDoCallbackWhenDone) { mGestureCallBack.gestureCallBack(GestureInterface.LONG_PRESS, mLongTouchDestination); } } invalidate(); }
/* (non-Javadoc) * @see android.os.AsyncTask#doInBackground(Params[]) */ @Override protected String doInBackground(Object... vals) { Thread.currentThread().setName("Closest"); if (null == mService) { return null; } String airport = null; lon = (Double) vals[0]; lat = (Double) vals[1]; // if the user is moving instead of doing a long press, give them a chance // to cancel us before we start doing anything try { Thread.sleep(200); } catch (Exception e) { } if (isCancelled()) return ""; /* * Get TFR tfr if touched on its top */ LinkedList<TFRShape> shapes = null; List<AirSigMet> mets = null; if (null != mService) { shapes = mService.getTFRShapes(); if (!mPref.useAdsbWeather()) { mets = mService.getInternetWeatherCache().getAirSigMet(); } } if (null != shapes) { for (int shape = 0; shape < shapes.size(); shape++) { TFRShape cshape = shapes.get(shape); /* * Set TFR tfr */ String txt = cshape.getTextIfTouched(lon, lat); if (null != txt) { tfr += txt + "\n--\n"; } } } /* * Air/sigmets */ if (null != mets) { for (int i = 0; i < mets.size(); i++) { MetShape cshape = mets.get(i).shape; if (null != cshape) { /* * Set MET tfr */ String txt = cshape.getTextIfTouched(lon, lat); if (null != txt) { textMets += txt + "\n--\n"; } } } } airport = mService.getDBResource().findClosestAirportID(lon, lat); if (isCancelled()) { return ""; } if (null == airport) { airport = "" + Helper.truncGeo(lat) + "&" + Helper.truncGeo(lon); } else { freq = mService.getDBResource().findFrequencies(airport); if (isCancelled()) { return ""; } taf = mService.getDBResource().getTAF(airport); if (isCancelled()) { return ""; } metar = mService.getDBResource().getMETAR(airport); if (isCancelled()) { return ""; } runways = mService.getDBResource().findRunways(airport); if (isCancelled()) { return ""; } elev = mService.getDBResource().findElev(airport); if (isCancelled()) { return ""; } LinkedList<String> fl = mService.getDBResource().findFuelCost(airport); if (fl.size() == 0) { // If fuel not available, show its not fuel = mContext.getString(R.string.NotAvailable); } else { fuel = ""; } // Concat all fuel reports for (String s : fl) { fuel += s + "\n\n"; } if (isCancelled()) return ""; LinkedList<String> cm = mService.getDBResource().findRatings(airport); if (cm.size() == 0) { // If ratings not available, show its not ratings = mContext.getString(R.string.NotAvailable); } else { ratings = ""; } // Concat all fuel reports for (String s : cm) { ratings += s + "\n\n"; } if (isCancelled()) return ""; } /* * ADSB gets this info from weather cache */ if (!mPref.useAdsbWeather()) { aireps = mService.getDBResource().getAireps(lon, lat); if (isCancelled()) { return ""; } wa = mService.getDBResource().getWindsAloft(lon, lat); if (isCancelled()) { return ""; } sua = mService.getDBResource().getSua(lon, lat); if (isCancelled()) { return ""; } if (mLayer != null) { layer = mLayer.getDate(); } if (isCancelled()) { return ""; } } mPointProjection = new Projection(mGpsParams.getLongitude(), mGpsParams.getLatitude(), lon, lat); return airport; }