Calculate sunset and dawn times by the coordinates of an Android device using earthtools

Hi Habr!

image

Once, the task was set to develop an engine for calculating the start time of sunset and sunrise, based on the location of the phone.

In addition, in order to facilitate the work, I was loaded with abnormal mathematical formulas, from which nothing fit in my head. So, I decided to look for an alternative to these hellish formulas.

Rummaging through Google, I found an interesting site: http://www.earthtools.org/ . It provides several web services related to working with maps. One of them was a service to which you input latitude, longitude and date, and as a response you get an xml of the following content:



Based on this, the task is much simpler:

1. Get the coordinates of the device;
2. Generate a GET request;
3. Send a request;
4. Get response (our XML);
5. Parse XML to get the necessary data;
6. Display the result.

I’ll immediately warn you that there will be no hardcore code, but only a simple engine.

Let's get started to start the resolution in AndroidManifest.xml



Next, we get the coordinates of the device. I note right away that in practice exact coordinates are not needed for tasks of this kind, so you can even use the coordinates that the cellular network gives us. Since there is no super UI here, but an exceptionally small engine, therefore the Activity has only the Update button, and below it a TextView to display the results. I also removed the names of package and import.

public class MainActivity extends ActionBarActivity implements View.OnClickListener {
    private TextView helloWorld;
    private Button update;
    private LocationListener locationListener;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initUI();
        initLocationListener();
    }
    private void requestLocationUpdate() {
        LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        List allProviders = locationManager.getAllProviders();
        for (String provider : allProviders) {
            helloWorld.append(provider + "\n");
        }
        String bestProvider = locationManager.getBestProvider(createGPSCriteria(), true);
        helloWorld.append("Best: " + bestProvider + "\n");
        locationManager.requestSingleUpdate(bestProvider, locationListener, null);
    }
    private void initLocationListener() {
        locationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                Calendar calendar = Calendar.getInstance();
                int day = calendar.get(Calendar.DATE);
                int month = calendar.get(Calendar.MONTH) + 1;
                SunAsyncTask sunAsyncTask = new SunAsyncTask(location.getLatitude(), location.getLongitude(), day, month);
                try {
                    HttpResponse response = sunAsyncTask.execute().get();
                    String stringEntity = EntityUtils.toString(response.getEntity());
                    SunXmlParser sunXmlParser = new SunXmlParser(stringEntity);
                    helloWorld.append("Sunset: " + sunXmlParser.parse(SunXmlParser.SUNSET) + "\n");
                    helloWorld.append("Sunrise: " + sunXmlParser.parse(SunXmlParser.SUNRISE) + "\n");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {
            }
            @Override
            public void onProviderEnabled(String provider) {
            }
            @Override
            public void onProviderDisabled(String provider) {
            }
        };
    }
    private void initUI() {
        helloWorld = (TextView) findViewById(R.id.hello_world);
        update = (Button) findViewById(R.id.update);
        update.setOnClickListener(this);
    }
    private Criteria createGPSCriteria() {
        Criteria criteria = new Criteria();
        criteria.setAccuracy(Criteria.ACCURACY_FINE);
        criteria.setPowerRequirement(Criteria.POWER_LOW);
        criteria.setCostAllowed(false);
        return criteria;
    }
    @Override
    public void onClick(View clickedView) {
        int id = clickedView.getId();
        if (id == R.id.update) {
            requestLocationUpdate();
        }
    }
}

Here we first initialize the UI, then initialize the LocationListener, which will have to respond to a change in coordinates, namely, when the position changes, it will launch AsyncTask, which will form a request, send it and receive a response.

After initialization, we need to choose a provider from which we will take the latitude and longitude of our location.

Nothing complicated here, there is a ready-made getBestProvider method that does this according to the criteria that we pass to it in the first argument. Criteria is the class in which we put the parameters of the provider that we want to get. In this example, I set a criterion for selecting a free provider that would work even with a bad signal, and with "normal" location accuracy.

Well, and accordingly, by pressing the Update key from the provider, we request an update of the location to which the LocationListener responds, calling AsynkTask about which I will tell you now.

public class SunAsyncTask extends AsyncTask {
    private double latitude;
    private double longitude;
    private int day;
    private int month;
    public SunAsyncTask(double latitude, double longitude, int day, int month) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.day = day;
        this.month = month;
    }
    private String createUrl() {
        // http://www.earthtools.org/sun//////
        StringBuilder stringBuilder = new StringBuilder("http://www.earthtools.org/sun/");
        stringBuilder.append(String.valueOf(latitude)).append("/");
        stringBuilder.append(String.valueOf(longitude)).append("/");
        stringBuilder.append(String.valueOf(day)).append("/");
        stringBuilder.append(String.valueOf(month)).append("/");
        stringBuilder.append("99").append("/");
        stringBuilder.append("0");
        Log.d("URL", stringBuilder.toString());
        return stringBuilder.toString();
    }
    @Override
    protected HttpResponse doInBackground(Void... params) {
        HttpResponse response = null;
        HttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(createUrl());
        try {
            response = client.execute(get);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response;
    }
}

Everything here is also simple. Let me remind you, as I already said, to get the time you need the coordinates and date of the day of sunset and dawn, which we want to get.

The createUrl method generates a URL for a GET request, but there are slightly more parameters there than I wrote before. Namely: timezone and dst. So, with timezone everything is clear, you can specify the time zone in which you want to calculate, but if you substitute “99” as a time zone, it will automatically be determined based on the coordinates that you transmit. Accordingly, I set 99. Well, dst is the account of summer time (0 - no, 1 - yes).

So, it is assumed that we will get XML with the result. Now you need to parse it. We will use as usual XmlPullParser, who are not familiar, I advise you to familiarize yourself with it, since it has a number of "features". Well, actually, a class that parses sunset and sunrise.

public class SunXmlParser {
    private String xmlString;
    private XmlPullParser parser;
    public static final String SUNRISE = "sunrise";
    public static final String SUNSET = "sunset";
    public SunXmlParser(String xmlString) throws XmlPullParserException {
        this.xmlString = xmlString;
        parser = Xml.newPullParser();
        parser.setInput(new StringReader(xmlString));
    }
    public void setXmlString(String xmlString) throws XmlPullParserException {
        this.xmlString = xmlString;
    }
    public String parse(String tag) throws XmlPullParserException, IOException {
        parser.setInput(new StringReader(xmlString));
        int eventType = parser.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
            if(eventType == XmlPullParser.START_TAG && parser.getName().equalsIgnoreCase(tag)) {
                parser.next();
                return parser.getText();
            }
            eventType = parser.next();
        }
        return "Not found";
    }
}

That, in fact, is all. The problem is solved, the result on the screen:


Also popular now: