eu.trentorise.smartcampus.trentinofamiglia.map.MapManager.java Source code

Java tutorial

Introduction

Here is the source code for eu.trentorise.smartcampus.trentinofamiglia.map.MapManager.java

Source

/*******************************************************************************
 * Copyright 2012-2013 Trento RISE
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *        http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either   express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package eu.trentorise.smartcampus.trentinofamiglia.map;

import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.util.SparseArray;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;

import eu.trentorise.smartcampus.territoryservice.model.BaseDTObject;
import eu.trentorise.smartcampus.territoryservice.model.POIObject;
import eu.trentorise.smartcampus.trentinofamiglia.R;
import eu.trentorise.smartcampus.trentinofamiglia.custom.CategoryHelper;
import eu.trentorise.smartcampus.trentinofamiglia.custom.DTParamsHelper;
import eu.trentorise.smartcampus.trentinofamiglia.custom.data.DTHelper;
import eu.trentorise.smartcampus.trentinofamiglia.custom.data.model.ExplorerObject;
import eu.trentorise.smartcampus.trentinofamiglia.custom.data.model.TrackObject;

public class MapManager {

    private static MapView mapView;

    public static int ZOOM_DEFAULT = 15;
    private static final int MAX_ZOOM = 18;
    public static final int MAX_VISIBLE_DISTANCE = 20;
    public static LatLng DEFAULT_POINT = new LatLng(46.0696727540531, 11.1212700605392); // Trento

    public static void initWithParam() {
        int zoom = DTParamsHelper.getZoomLevelMap();
        if (zoom != 0) {
            ZOOM_DEFAULT = zoom;
        }

        List<Double> centerMap = DTParamsHelper.getCenterMap();
        if (centerMap != null) {
            Double latitute = centerMap.get(0);
            Double longitude = centerMap.get(1);
            DEFAULT_POINT = new LatLng(latitute, longitude);
        }
    }

    public static MapView getMapView() {
        return mapView;
    }

    public static void setMapView(MapView mapView) {
        MapManager.mapView = mapView;
        MapManager.mapView.setClickable(true);
        MapManager.mapView.setBuiltInZoomControls(true);
    }

    public static GeoPoint requestMyLocation(Context ctx) {
        return DTHelper.getLocationHelper().getLocation();
    }

    public static void fitMapWithOverlays(Collection<? extends BaseDTObject> objects, GoogleMap map) {
        double[] llrr = null;
        if (objects != null && !objects.isEmpty()) {
            for (BaseDTObject o : objects) {
                if (o instanceof TrackObject && objects.size() == 1) {
                    double[] point = new double[2];
                    for (LatLng ll : ((TrackObject) o).decodedLine()) {
                        point[0] = ll.latitude;
                        point[1] = ll.longitude;
                        llrr = fit(llrr, point);
                    }
                } else {
                    llrr = fit(llrr, o.getLocation());
                }
            }
        }
        if (llrr != null) {
            fit(map, new double[] { llrr[0], llrr[1] }, new double[] { llrr[2], llrr[3] },
                    objects != null && objects.size() > 1);
        } else {
            fit(map, null, null, objects != null && objects.size() > 1);
        }
    }

    /**
     * @param llrr
     * @param location
     * @return
     */
    private static double[] fit(double[] llrr, double[] location) {
        if (llrr == null) {
            llrr = new double[4];
            llrr[0] = location[0];
            llrr[1] = location[1];
            llrr[2] = location[0];
            llrr[3] = location[1];
        } else {
            llrr[0] = Math.min(llrr[0], location[0]);
            llrr[1] = Math.max(llrr[1], location[1]);

            llrr[2] = Math.max(llrr[2], location[0]);
            llrr[3] = Math.min(llrr[3], location[1]);
        }
        return llrr;
    }

    private static void fit(GoogleMap map, double[] ll, double[] rr, boolean zoomIn) {
        //      if (ll != null && rr != null) {
        //         LatLngBounds bounds = LatLngBounds.builder().include(new LatLng(rr[0], rr[1]))
        //               .include(new LatLng(ll[0], ll[1])).build();
        //         map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 12));
        //      }
        if (ll != null && rr != null) {
            float[] dist = new float[3];
            Location.distanceBetween(ll[0], ll[1], rr[0], rr[1], dist);
            if (dist[0] > MAX_VISIBLE_DISTANCE) {
                LatLngBounds bounds = LatLngBounds.builder().include(new LatLng(rr[0], rr[1]))
                        .include(new LatLng(ll[0], ll[1])).build();
                map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 64));
            } else {
                map.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(ll[0], ll[1]), MAX_ZOOM));
            }
        }
    }

    //   public static MarkerOptions createStoryStepMarker(Context ctx, BaseDTObject obj, int pos, boolean selected) {
    //      LatLng latLng = getLatLngFromBasicObject(obj);
    //
    //      int markerIcon = selected ? R.drawable.selected_step : R.drawable.step;
    //
    //      BitmapDescriptor bd = BitmapDescriptorFactory.fromBitmap(writeOnStoryMarker(ctx, markerIcon,
    //            Integer.toString(pos)));
    //      MarkerOptions marker = new MarkerOptions().anchor(0.5f, 0.5f).position(latLng).icon(bd).title("" + pos);
    //      return marker;
    //   }
    //
    //   public static PolylineOptions createStoryStepLine(Context ctx, BaseDTObject from, BaseDTObject to) {
    //      LatLng latLngFrom = getLatLngFromBasicObject(from);
    //      LatLng latLngTo = getLatLngFromBasicObject(to);
    //      PolylineOptions line = new PolylineOptions().add(latLngFrom, latLngTo)
    //            .color(Color.parseColor(ctx.getString(R.color.dtappcolor))).width(6);
    //      return line;
    //   }

    /*
     * CLUSTERING
     */
    public static class ClusteringHelper {
        private static final String TAG = "MapManager.ClusteringHelper";

        private static final int DENSITY_X = 5;
        private static final int DENSITY_Y = 5;

        private static List<List<List<BaseDTObject>>> grid = new ArrayList<List<List<BaseDTObject>>>();
        private static SparseArray<int[]> item2group = new SparseArray<int[]>();

        public synchronized static <T extends BaseDTObject> List<MarkerOptions> cluster(Context mContext,
                GoogleMap map, Collection<T> objects) {
            item2group.clear();
            // 2D array with some configurable, fixed density
            grid.clear();

            for (int i = 0; i <= DENSITY_X; i++) {
                ArrayList<List<BaseDTObject>> column = new ArrayList<List<BaseDTObject>>(DENSITY_Y + 1);
                for (int j = 0; j <= DENSITY_Y; j++) {
                    column.add(new ArrayList<BaseDTObject>());
                }
                grid.add(column);
            }

            LatLng lu = map.getProjection().getVisibleRegion().farLeft;
            LatLng rd = map.getProjection().getVisibleRegion().nearRight;
            int step = (int) (Math.abs((lu.longitude * 1E6) - (rd.longitude * 1E6)) / DENSITY_X);

            // compute leftmost bound of the affected grid:
            // this is the bound of the leftmost grid cell that intersects
            // with the visible part
            int startX = (int) ((lu.longitude * 1E6) - ((lu.longitude * 1E6) % step));
            if (lu.longitude < 0) {
                startX -= step;
            }
            // compute bottom bound of the affected grid
            int startY = (int) ((rd.latitude * 1E6) - ((rd.latitude * 1E6) % step));
            if (lu.latitude < 0) {
                startY -= step;
            }
            int endX = startX + (DENSITY_X + 1) * step;
            int endY = startY + (DENSITY_Y + 1) * step;

            int idx = 0;
            try {
                for (BaseDTObject basicObject : objects) {
                    LatLng objLatLng = getLatLngFromBasicObject(basicObject);

                    if (objLatLng != null && (objLatLng.longitude * 1E6) >= startX
                            && (objLatLng.longitude * 1E6) <= endX && (objLatLng.latitude * 1E6) >= startY
                            && (objLatLng.latitude * 1E6) <= endY) {
                        int binX = (int) (Math.abs((objLatLng.longitude * 1E6) - startX) / step);
                        int binY = (int) (Math.abs((objLatLng.latitude * 1E6) - startY) / step);

                        item2group.put(idx, new int[] { binX, binY });
                        // just push the reference
                        grid.get(binX).get(binY).add(basicObject);
                    }
                    idx++;
                }
            } catch (ConcurrentModificationException ex) {
                Log.e(TAG, ex.toString());
            }

            // if (mapView.getZoomLevel() == mapView.getMaxZoomLevel()) {
            if (map.getCameraPosition().zoom == map.getMaxZoomLevel()) {
                for (int i = 0; i < grid.size(); i++) {
                    for (int j = 0; j < grid.get(0).size(); j++) {
                        List<BaseDTObject> curr = grid.get(i).get(j);
                        if (curr.size() == 0)
                            continue;

                        if (i > 0) {
                            if (checkDistanceAndMerge(i - 1, j, curr))
                                continue;
                        }
                        if (j > 0) {
                            if (checkDistanceAndMerge(i, j - 1, curr))
                                continue;
                        }
                        if (i > 0 && j > 0) {
                            if (checkDistanceAndMerge(i - 1, j - 1, curr))
                                continue;
                        }
                    }
                }
            }

            // generate markers
            List<MarkerOptions> markers = new ArrayList<MarkerOptions>();

            for (int i = 0; i < grid.size(); i++) {
                for (int j = 0; j < grid.get(i).size(); j++) {
                    List<BaseDTObject> markerList = grid.get(i).get(j);
                    if (markerList.size() > 1) {
                        markers.add(createGroupMarker(mContext, map, markerList, i, j));
                    } else if (markerList.size() == 1) {
                        // draw single marker
                        markers.add(createSingleMarker(markerList.get(0), i, j));
                    }
                }
            }

            return markers;
        }

        /**
         * Render markers on the map
         * @param map
         * @param markers
         */
        public static void render(GoogleMap map, List<MarkerOptions> markers) {
            for (MarkerOptions mo : markers) {
                map.addMarker(mo);
            }
        }

        /**
         * Render clustered object markers and eventual lines of the {@link TrackObject}
         * @param map
         * @param markers
         * @param objects
         */
        public static void render(Context ctx, GoogleMap map, List<MarkerOptions> markers,
                Collection<? extends BaseDTObject> objects) {
            for (MarkerOptions mo : markers) {
                map.addMarker(mo);
            }
            List<List<LatLng>> paths = new ArrayList<List<LatLng>>();
            for (BaseDTObject o : objects) {
                if (o instanceof TrackObject) {
                    paths.add(((TrackObject) o).decodedLine());
                }
            }
            if (paths.size() == 1) {
                draw(map, paths, ctx);
            }
        }

        private static MarkerOptions createTrackMarker(TrackObject item) {
            LatLng latLng = item.startingPoint();
            int markerIcon = CategoryHelper.getMapIconByType(item.getType());

            MarkerOptions marker = new MarkerOptions().position(latLng)
                    .icon(BitmapDescriptorFactory.fromResource(markerIcon)).title(item.getId());
            return marker;
        }

        private static MarkerOptions createSingleMarker(BaseDTObject item, int x, int y) {
            LatLng latLng = getLatLngFromBasicObject(item);

            int markerIcon = CategoryHelper.getMapIconByType(item.getType());
            if (CategoryHelper.FAMILY_CATEGORY_POI.equals(item.getType())
                    || (CategoryHelper.FAMILY_CATEGORY_EVENT.equals(item.getType())))
                markerIcon = objectCertified(item);

            MarkerOptions marker = new MarkerOptions().position(latLng)
                    .icon(BitmapDescriptorFactory.fromResource(markerIcon)).title(x + ":" + y);
            return marker;
        }

        private static MarkerOptions createGroupMarker(Context mContext, GoogleMap map,
                List<BaseDTObject> markerList, int x, int y) {
            BaseDTObject item = markerList.get(0);
            LatLng latLng = getLatLngFromBasicObject(item);

            int markerIcon = R.drawable.ic_marker_p_generic;

            BitmapDescriptor bd = BitmapDescriptorFactory
                    .fromBitmap(writeOnMarker(mContext, markerIcon, Integer.toString(markerList.size())));
            MarkerOptions marker = new MarkerOptions().position(latLng).icon(bd).title(x + ":" + y);
            return marker;
        }

        public static List<BaseDTObject> getFromGridId(String id) {
            try {
                String[] parsed = id.split(":");
                int x = Integer.parseInt(parsed[0]);
                int y = Integer.parseInt(parsed[1]);

                return grid.get(x).get(y);
            } catch (Exception e) {
                return null;
            }
        }

        private static boolean checkDistanceAndMerge(int i, int j, List<BaseDTObject> curr) {
            List<BaseDTObject> src = grid.get(i).get(j);
            if (src.size() == 0) {
                return false;
            }

            LatLng srcLatLng = getLatLngFromBasicObject(src.get(0));
            LatLng currLatLng = getLatLngFromBasicObject(curr.get(0));

            if (srcLatLng != null && currLatLng != null) {
                float[] dist = new float[3];

                Location.distanceBetween(srcLatLng.latitude, srcLatLng.longitude, currLatLng.latitude,
                        currLatLng.longitude, dist);

                if (dist[0] < 20) {
                    src.addAll(curr);
                    curr.clear();
                    return true;
                }
            }
            return false;
        }

    }

    public static void switchToMapView(ArrayList<BaseDTObject> list, Fragment src) {
        FragmentTransaction fragmentTransaction = src.getActivity().getSupportFragmentManager().beginTransaction();
        MapFragment fragment = new MapFragment();
        Bundle args = new Bundle();
        args.putSerializable(MapFragment.ARG_OBJECTS, list);
        fragment.setArguments(args);
        fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        // fragmentTransaction.detach(src);
        fragmentTransaction.replace(R.id.frame_content, fragment, src.getTag());
        fragmentTransaction.addToBackStack(fragment.getTag());
        fragmentTransaction.commit();

    }

    public static void switchToMapView(String category, String argType, Fragment src) {
        FragmentTransaction fragmentTransaction = src.getActivity().getSupportFragmentManager().beginTransaction();
        MapFragment fragment = new MapFragment();
        Bundle args = new Bundle();
        args.putString(argType, category);
        fragment.setArguments(args);
        fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        // fragmentTransaction.detach(src);
        fragmentTransaction.replace(R.id.frame_content, fragment, src.getTag());
        fragmentTransaction.addToBackStack(fragment.getTag());
        fragmentTransaction.commit();

    }

    private static int objectCertified(BaseDTObject o) {
        if (o.getCustomData() != null) {
            if ((o instanceof ExplorerObject) && ((Boolean) o.getCustomData().get("certified"))) {
                /* se ceretificato e evento */
                return R.drawable.ic_marker_e_family_certified;
            }

            /* se certificato e poi */
            String status = (String) o.getCustomData().get("status");
            if ((o instanceof POIObject)
                    && (("Certificato finale").equals(status) || ("Certificato base").equals(status))) {
                return R.drawable.ic_marker_p_family_certified;
            }
        }

        return CategoryHelper.getMapIconByType(o.getType());
    }

    private static Bitmap writeOnMarker(Context mContext, int drawableId, String text) {
        float scale = mContext.getResources().getDisplayMetrics().density;

        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), drawableId)
                .copy(Bitmap.Config.ARGB_8888, true);

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextAlign(Align.CENTER);
        paint.setTextSize(scale * 14);
        paint.setAntiAlias(true);
        paint.setARGB(255, 255, 255, 255);

        Canvas canvas = new Canvas(bitmap);
        Rect bounds = new Rect();
        paint.getTextBounds(text, 0, text.length(), bounds);
        float x = bitmap.getWidth() / 2;
        float y = bitmap.getHeight() / 2 - 5;
        canvas.drawText(text, x, y, paint);

        return bitmap;
    }

    private static Bitmap writeOnStoryMarker(Context mContext, int drawableId, String text) {
        float scale = mContext.getResources().getDisplayMetrics().density;

        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), drawableId)
                .copy(Bitmap.Config.ARGB_8888, true);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setTextAlign(Align.CENTER);
        paint.setTextSize(scale * 14);
        paint.setAntiAlias(true);
        paint.setARGB(255, 255, 255, 255);

        Canvas canvas = new Canvas(bitmap);
        Rect bounds = new Rect();
        paint.getTextBounds(text, 0, text.length(), bounds);
        float x = bitmap.getWidth() / 2;
        float y = bitmap.getHeight() / 2 - ((paint.descent() + paint.ascent()) / 2);

        canvas.drawText(text, x, y, paint);

        return bitmap;
    }

    private static LatLng getLatLngFromBasicObject(BaseDTObject object) {
        LatLng latLng = null;
        if (object instanceof TrackObject) {
            latLng = ((TrackObject) object).startingPoint();
        } else {
            latLng = new LatLng(object.getLocation()[0], object.getLocation()[1]);
        }
        return latLng;
    }

    private static boolean draw(GoogleMap map, List<List<LatLng>> legsPoints, Context ctx) {
        for (int i = 0; i < legsPoints.size(); i++) {
            // default
            int color = ctx.getResources().getColor(R.color.path);
            List<LatLng> legPoints = legsPoints.get(i);
            drawPath(map, legPoints, color);
        }
        return true;
    }

    private static void drawPath(GoogleMap map, List<LatLng> points, int color) {
        // int x1 = -1, y1 = -1, x2 = -1, y2 = -1;
        Paint paint = new Paint();
        paint.setColor(color);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setStrokeWidth(6);

        PolylineOptions po = new PolylineOptions().addAll(points).width(6).color(color);
        Polyline pl = map.addPolyline(po);
        pl.setVisible(true);
    }

}