NAV Navbar

台灣圖霸 | Map8 Platform

歡迎使用 Logo 28x28 台灣圖霸 | Map8 Platform 地圖平台

有任何技術疑難,或其他疑問,都歡迎您 跟我們聯絡 喔!!!

初始化地圖

Example

public class SimpleMapViewActivity extends AppCompatActivity {

    // Taiwan map boundary
    private static final LatLng TW_BOUND_CORNER_NE = new LatLng(33.4, 138.45858);
    private static final LatLng TW_BOUND_CORNER_SW = new LatLng(15, 105);
    private static final LatLngBounds TW_BOUNDS_AREA = new LatLngBounds.Builder()
            .include(TW_BOUND_CORNER_NE)
            .include(TW_BOUND_CORNER_SW)
            .build();

    private MapboxMap mapboxMap;
    private MapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Map8 access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Map8.getInstance(this, "<您的 Map8 Key>");

        MapboxMapOptions options = MapboxMapOptions.createFromAttributes(this, null)
                .camera(new CameraPosition.Builder()
                        .target(new LatLng(25.03625, 121.54885))
                        .zoom(16)
                        .bearing(0)
                        .tilt(50)
                        .build());

        // create map
        mapView = new MapView(this, options);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(@NonNull MapboxMap mapboxMap) {
                SimpleMapViewActivity.this.mapboxMap = mapboxMap;
                mapboxMap.setMaxZoomPreference(19.99);
                mapboxMap.setMinZoomPreference(6);
                mapboxMap.setLatLngBoundsForCameraTarget(TW_BOUNDS_AREA);
                mapboxMap.setStyle(new Style.Builder().fromUri("https://api.map8.zone/styles/go-life-maps-tw-style-std/style.json"), new Style.OnStyleLoaded() {
                    @Override
                    public void onStyleLoaded(@NonNull Style style) {
                        Log.d("[Map8 SDK Demo]", "mapView.getMapAsync() > onMapReady() > setStyle() > onStyleLoaded()");
                        enableLocationComponent(style);
                    }
                });
            }
        });
        setContentView(mapView);
    }

    @SuppressWarnings( {"MissingPermission"})
    private void enableLocationComponent(@NonNull Style loadedMapStyle) {
        // Check if permissions are enabled and if not request
        if (PermissionsManager.areLocationPermissionsGranted(this)) {

            // Get an instance of the component
            LocationComponent locationComponent = mapboxMap.getLocationComponent();

            // Activate with options
            locationComponent.activateLocationComponent(
            LocationComponentActivationOptions.builder(this, loadedMapStyle).build());

            // Enable to make component visible
            locationComponent.setLocationComponentEnabled(true);

            // Set the component's camera mode
            locationComponent.setCameraMode(CameraMode.TRACKING);

            // Set the component's render mode
            locationComponent.setRenderMode(RenderMode.COMPASS);
        }
    }

    // Add the mapView's own lifecycle methods to the activity's lifecycle methods
    @SuppressWarnings( {"MissingPermission"})
    @Override
    public void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
        public void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
        public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
        protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }
}

Simple map view an

攝影機視角移動地圖

Example

public class CameraAnimationActivity extends AppCompatActivity implements MapboxMap.OnMapClickListener {

    // Taiwan map boundary
    private static final LatLng TW_BOUND_CORNER_NE = new LatLng(33.4, 138.45858);
    private static final LatLng TW_BOUND_CORNER_SW = new LatLng(15, 105);
    private static final LatLngBounds TW_BOUNDS_AREA = new LatLngBounds.Builder()
            .include(TW_BOUND_CORNER_NE)
            .include(TW_BOUND_CORNER_SW)
            .build();

    private MapView mapView;
    private MapboxMap mapboxMap;
    private Marker theMarker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Map8 access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Map8.getInstance(this, "<您的 Map8 Key>");

        MapboxMapOptions options = MapboxMapOptions.createFromAttributes(this, null)
                .camera(new CameraPosition.Builder()
                        .target(new LatLng(25.03625, 121.54885))
                        .zoom(16)
                        .bearing(0)
                        .tilt(50)
                        .build());

        // create map
        mapView = new MapView(this, options);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(@NonNull final MapboxMap mapboxMap) {
                CameraAnimationActivity.this.mapboxMap = mapboxMap;
                mapboxMap.setMaxZoomPreference(19.99);
                mapboxMap.setMinZoomPreference(6);
                mapboxMap.setLatLngBoundsForCameraTarget(TW_BOUNDS_AREA);
                mapboxMap.setStyle(new Style.Builder().fromUri("https://api.map8.zone/styles/go-life-maps-tw-style-std/style.json"), new Style.OnStyleLoaded() {
                    @Override
                    public void onStyleLoaded(@NonNull Style style) {
                        Log.d("[Map8 SDK Demo]", "mapView.getMapAsync() > onMapReady() > setStyle() > onStyleLoaded()");
                        enableLocationComponent(style);

                        // Toast instructing user to tap on the map
                        Toast.makeText(CameraAnimationActivity.this, "Tap anywhere on the map", Toast.LENGTH_LONG).show();
                        mapboxMap.addOnMapClickListener(CameraAnimationActivity.this);

                        LocationComponent locationComponent = mapboxMap.getLocationComponent();
                        double currentLat = locationComponent.getLastKnownLocation().getLatitude();
                        double currentLong = locationComponent.getLastKnownLocation().getLongitude();

                        // Create an Icon object for the marker to use
                        IconFactory iconFactory = IconFactory.getInstance(CameraAnimationActivity.this);
                        Icon icon = iconFactory.fromResource(R.drawable.red_marker);

                        // Add the marker to the map
                        theMarker = mapboxMap.addMarker(new MarkerOptions()
                                .position(new LatLng(currentLat, currentLong))
                                .icon(icon));

                        mapboxMap.setOnMarkerClickListener(new MapboxMap.OnMarkerClickListener() {
                            @Override
                            public boolean onMarkerClick(@NonNull Marker marker) {
                                // Show a toast with the title of the selected marker
                                Toast.makeText(CameraAnimationActivity.this, "座標:\n" + marker.getPosition().getLatitude() + ", " + marker.getPosition().getLongitude(), Toast.LENGTH_LONG).show();
                                return true;
                            }
                        });
                    }
                });
            }
        });
        setContentView(mapView);
    }

    @SuppressWarnings( {"MissingPermission"})
    private void enableLocationComponent(@NonNull Style loadedMapStyle) {
        if (PermissionsManager.areLocationPermissionsGranted(this)) {
            LocationComponent locationComponent = mapboxMap.getLocationComponent();

            locationComponent.activateLocationComponent(
                    LocationComponentActivationOptions.builder(this, loadedMapStyle).build());

            locationComponent.setLocationComponentEnabled(true);
            locationComponent.setCameraMode(CameraMode.TRACKING);
            locationComponent.setRenderMode(RenderMode.COMPASS);
        }
    }

    @Override
    public boolean onMapClick(@NonNull LatLng point) {
        CameraPosition position = new CameraPosition.Builder()
                .target(point) // Sets the new camera position
                .zoom(17) // Sets the zoom
                .bearing(0) // Rotate the camera
                .tilt(30) // Set the camera tilt
                .build(); // Creates a CameraPosition from the builder

        mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(position), 2000);

        theMarker.setPosition(point);
        mapboxMap.updateMarker(theMarker);

        return true;
    }

    // Add the mapView's own lifecycle methods to the activity's lifecycle methods
    @Override
    public void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    public void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }
}

Camera animation an

移動地圖標記

Example

public class MoveMarkerAnimationActivity extends AppCompatActivity implements OnMapReadyCallback, MapboxMap.OnMapClickListener {

    // Taiwan map boundary
    private static final LatLng TW_BOUND_CORNER_NE = new LatLng(33.4, 138.45858);
    private static final LatLng TW_BOUND_CORNER_SW = new LatLng(15, 105);
    private static final LatLngBounds TW_BOUNDS_AREA = new LatLngBounds.Builder()
            .include(TW_BOUND_CORNER_NE)
            .include(TW_BOUND_CORNER_SW)
            .build();

    private MapView mapView;
    private MapboxMap mapboxMap;
    private LatLng currentPosition = new LatLng(25.03625, 121.54885);
    private GeoJsonSource geoJsonSource;
    private ValueAnimator animator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Map8 access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Map8.getInstance(this, "<您的 Map8 Key>");

        MapboxMapOptions options = MapboxMapOptions.createFromAttributes(this, null)
                .camera(new CameraPosition.Builder()
                        .target(new LatLng(25.03625, 121.54885))
                        .zoom(16)
                        .bearing(0)
                        .tilt(50)
                        .build());

        // create map
        mapView = new MapView(this, options);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(this);
        setContentView(mapView);
    }

    @Override
    public void onMapReady(@NonNull final MapboxMap mapboxMap) {
        this.mapboxMap = mapboxMap;
        mapboxMap.setMaxZoomPreference(19.99);
        mapboxMap.setMinZoomPreference(6);
        mapboxMap.setLatLngBoundsForCameraTarget(TW_BOUNDS_AREA);
        mapboxMap.setStyle(new Style.Builder().fromUri("https://api.map8.zone/styles/go-life-maps-tw-style-std/style.json"), new Style.OnStyleLoaded() {
            @Override
            public void onStyleLoaded(@NonNull Style style) {

                style.addImage(("marker_icon"), BitmapFactory.decodeResource(
                        getResources(), R.drawable.red_marker));

                geoJsonSource = new GeoJsonSource("source-id",
                        Feature.fromGeometry(Point.fromLngLat(currentPosition.getLongitude(), currentPosition.getLatitude())));
                style.addSource(geoJsonSource);

                style.addLayer(new SymbolLayer("layer-id", "source-id")
                        .withProperties(
                                PropertyFactory.iconImage("marker_icon"),
                                PropertyFactory.iconIgnorePlacement(true),
                                PropertyFactory.iconAllowOverlap(true)
                        ));

                Toast.makeText(
                        MoveMarkerAnimationActivity.this,
                        "Tap on the map anywhere",
                        Toast.LENGTH_LONG
                ).show();

                mapboxMap.addOnMapClickListener(MoveMarkerAnimationActivity.this);
            }
        });
    }

    @Override
    public boolean onMapClick(@NonNull LatLng point) {
        // When the user clicks on the map, we want to animate the marker to that location.
        if (animator != null && animator.isStarted()) {
            currentPosition = (LatLng) animator.getAnimatedValue();
            animator.cancel();
        }

        animator = ObjectAnimator
                .ofObject(latLngEvaluator, currentPosition, point)
                .setDuration(2000);
        animator.addUpdateListener(animatorUpdateListener);
        animator.start();

        currentPosition = point;
        return true;
    }

    private final ValueAnimator.AnimatorUpdateListener animatorUpdateListener =
            new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    LatLng animatedPosition = (LatLng) valueAnimator.getAnimatedValue();
                    geoJsonSource.setGeoJson(Point.fromLngLat(animatedPosition.getLongitude(), animatedPosition.getLatitude()));
                }
            };

    // Class is used to interpolate the marker animation.
    private static final TypeEvaluator<LatLng> latLngEvaluator = new TypeEvaluator<LatLng>() {

        private final LatLng latLng = new LatLng();

        @Override
        public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
            latLng.setLatitude(startValue.getLatitude()
                    + ((endValue.getLatitude() - startValue.getLatitude()) * fraction));
            latLng.setLongitude(startValue.getLongitude()
                    + ((endValue.getLongitude() - startValue.getLongitude()) * fraction));
            return latLng;
        }
    };

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (animator != null) {
            animator.cancel();
        }
        if (mapboxMap != null) {
            mapboxMap.removeOnMapClickListener(this);
        }
        mapView.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }
}

Drag marker an

自定義地圖標記圖示

Example

public class PopupInfoActivity extends AppCompatActivity implements OnMapReadyCallback, MapboxMap.OnMapClickListener {

    // Taiwan map boundary
    private static final LatLng TW_BOUND_CORNER_NE = new LatLng(33.4, 138.45858);
    private static final LatLng TW_BOUND_CORNER_SW = new LatLng(15, 105);
    private static final LatLngBounds TW_BOUNDS_AREA = new LatLngBounds.Builder()
            .include(TW_BOUND_CORNER_NE)
            .include(TW_BOUND_CORNER_SW)
            .build();

    private static final String GEOJSON_SOURCE_ID = "GEOJSON_SOURCE_ID";

    private static final String IMAGE_ID_PUSH_PIN = "IMAGE_ID_PUSH_PIN";
    private static final String LAYER_ID_PUSH_PIN = "LAYER_ID_PUSH_PIN";

    private static final String CALLOUT_LAYER_ID = "CALLOUT_LAYER_ID";

    private static final String PROPERTY_SELECTED = "selected";
    private static final String PROPERTY_NAME = "name";
    private static final String PROPERTY_CAPITAL = "capital";

    private MapView mapView;
    private MapboxMap mapboxMap;
    private GeoJsonSource source;
    private FeatureCollection featureCollection;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Map8 access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Map8.getInstance(this, "<您的 Map8 Key>");

        MapboxMapOptions options = MapboxMapOptions.createFromAttributes(this, null)
                .camera(new CameraPosition.Builder()
                        .target(new LatLng(25.03625, 121.54885))
                        .zoom(16)
                        .bearing(0)
                        .tilt(50)
                        .build());

        // create map
        mapView = new MapView(this, options);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(this);

        setContentView(mapView);
    }

    @Override
    public void onMapReady(@NonNull final MapboxMap mapboxMap) {
        mapboxMap.setMaxZoomPreference(19.99);
        mapboxMap.setMinZoomPreference(6);
        mapboxMap.setLatLngBoundsForCameraTarget(TW_BOUNDS_AREA);
        this.mapboxMap = mapboxMap;
        mapboxMap.setStyle(new Style.Builder().fromUri("https://api.map8.zone/styles/go-life-maps-tw-style-std/style.json"), new Style.OnStyleLoaded() {
            @Override
            public void onStyleLoaded(@NonNull Style style) {
                new LoadGeoJsonDataTask(PopupInfoActivity.this).execute();
                mapboxMap.addOnMapClickListener(PopupInfoActivity.this);
            }
        });
    }

    @Override
    public boolean onMapClick(@NonNull LatLng point) {
        return handleClickIcon(mapboxMap.getProjection().toScreenLocation(point));
    }

    /**
     * Sets up all of the sources and layers needed for this example
     *
     * @param collection the FeatureCollection to set equal to the globally-declared FeatureCollection
     */
    public void setUpData(final FeatureCollection collection) {
        featureCollection = collection;
        if (mapboxMap != null) {
            mapboxMap.getStyle(style -> {
                setupSource(style);
                setUpImage(style);
                setUpMarkerLayer(style);
                setUpInfoWindowLayer(style);
            });
        }
    }

    /**
     * Adds the GeoJSON source to the map
     */
    private void setupSource(@NonNull Style loadedStyle) {
        source = new GeoJsonSource(GEOJSON_SOURCE_ID, featureCollection);
        loadedStyle.addSource(source);
    }

    /**
     * Adds the marker image to the map for use as a SymbolLayer icon
     */
    private void setUpImage(@NonNull Style loadedStyle) {
        loadedStyle.addImage(IMAGE_ID_PUSH_PIN, BitmapFactory.decodeResource(
                this.getResources(), R.drawable.map_marker_push_pin_pink));
    }

    /**
     * Updates the display of data on the map after the FeatureCollection has been modified
     */
    private void refreshSource() {
        if (source != null && featureCollection != null) {
            source.setGeoJson(featureCollection);
        }
    }

    /**
     * Setup a layer with push pin icons
     */
    private void setUpMarkerLayer(@NonNull Style loadedStyle) {
        loadedStyle.addLayer(new SymbolLayer(LAYER_ID_PUSH_PIN, GEOJSON_SOURCE_ID)
                .withProperties(
                        iconImage(IMAGE_ID_PUSH_PIN),
                        iconAllowOverlap(true),
                        iconOffset(new Float[]{0f, -8f})
                ));
    }

    /**
     * Setup a layer with Android SDK call-outs
     * name of the feature is used as key for the iconImage
     */
    private void setUpInfoWindowLayer(@NonNull Style loadedStyle) {
        loadedStyle.addLayer(new SymbolLayer(CALLOUT_LAYER_ID, GEOJSON_SOURCE_ID)
                .withProperties(
                        /* show image with id title based on the value of the name feature property */
                        iconImage("{name}"),

                        /* set anchor of icon to bottom-left */
                        iconAnchor(ICON_ANCHOR_BOTTOM),

                        /* all info window and marker image to appear at the same time*/
                        iconAllowOverlap(true),

                        /* offset the info window to be above the marker */
                        iconOffset(new Float[]{-2f, -28f})
                )
                /* add a filter to show only when selected feature property is true */
                .withFilter(eq((get(PROPERTY_SELECTED)), literal(true))));
    }

    /**
     * This method handles click events for SymbolLayer symbols.
     * When a SymbolLayer icon is clicked, we moved that feature to the selected state.
     *
     * @param screenPoint the point on screen clicked
     */
    private boolean handleClickIcon(PointF screenPoint) {
        List<Feature> features = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID_PUSH_PIN);
        if (!features.isEmpty()) {
            String name = features.get(0).getStringProperty(PROPERTY_NAME);
            List<Feature> featureList = featureCollection.features();
            if (featureList != null) {
                for (int i = 0; i < featureList.size(); i++) {
                    if (featureList.get(i).getStringProperty(PROPERTY_NAME).equals(name)) {
                        if (featureSelectStatus(i)) {
                            setFeatureSelectState(featureList.get(i), false);
                        } else {
                            setSelected(i);
                        }
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * Set a feature selected state.
     *
     * @param index the index of selected feature
     */
    private void setSelected(int index) {
        if (featureCollection.features() != null) {
            Feature feature = featureCollection.features().get(index);
            setFeatureSelectState(feature, true);
            refreshSource();
        }
    }

    /**
     * Selects the state of a feature
     *
     * @param feature the feature to be selected.
     */
    private void setFeatureSelectState(Feature feature, boolean selectedState) {
        if (feature.properties() != null) {
            feature.properties().addProperty(PROPERTY_SELECTED, selectedState);
            refreshSource();
        }
    }

    /**
     * Checks whether a Feature's boolean "selected" property is true or false
     *
     * @param index the specific Feature's index position in the FeatureCollection's list of Features.
     * @return true if "selected" is true. False if the boolean property is false.
     */
    private boolean featureSelectStatus(int index) {
        if (featureCollection == null) {
            return false;
        }
        return featureCollection.features().get(index).getBooleanProperty(PROPERTY_SELECTED);
    }

    /**
     * Invoked when the bitmaps have been generated from a view.
     */
    public void setImageGenResults(HashMap<String, Bitmap> imageMap) {
        if (mapboxMap != null) {
            mapboxMap.getStyle(style -> {
                // calling addImages is faster as separate addImage calls for each bitmap.
                style.addImages(imageMap);
            });
        }
    }

    /**
     * AsyncTask to load data from the assets folder.
     */
    private static class LoadGeoJsonDataTask extends AsyncTask<Void, Void, FeatureCollection> {

        private final WeakReference<PopupInfoActivity> activityRef;

        LoadGeoJsonDataTask(PopupInfoActivity activity) {
            this.activityRef = new WeakReference<>(activity);
        }

        @Override
        protected FeatureCollection doInBackground(Void... params) {
            PopupInfoActivity activity = activityRef.get();

            if (activity == null) {
                return null;
            }

            String geoJson = loadGeoJsonFromAsset(activity, "pop_up_info_example.geojson");
            return FeatureCollection.fromJson(geoJson);
        }

        @Override
        protected void onPostExecute(FeatureCollection featureCollection) {
            super.onPostExecute(featureCollection);
            PopupInfoActivity activity = activityRef.get();
            if (featureCollection == null || activity == null) {
                return;
            }

            // This example runs on the premise that each GeoJSON Feature has a "selected" property,
            // with a boolean value. If your data's Features don't have this boolean property,
            // add it to the FeatureCollection 's features with the following code:
            for (Feature singleFeature : featureCollection.features()) {
                singleFeature.addBooleanProperty(PROPERTY_SELECTED, false);
            }

            activity.setUpData(featureCollection);
            new GenerateViewIconTask(activity).execute(featureCollection);
        }

        static String loadGeoJsonFromAsset(Context context, String filename) {
            try {
                // Load GeoJSON file from local asset folder
                InputStream is = context.getAssets().open(filename);
                int size = is.available();
                byte[] buffer = new byte[size];
                is.read(buffer);
                is.close();
                return new String(buffer, Charset.forName("UTF-8"));
            }
            catch (Exception exception) {
                throw new RuntimeException(exception);
            }
        }
    }

    /**
     * AsyncTask to generate Bitmap from Views to be used as iconImage in a SymbolLayer.
     * Call be optionally be called to update the underlying data source after execution.
     * Generating Views on background thread since we are not going to be adding them to the view hierarchy.
     */
    private static class GenerateViewIconTask extends AsyncTask<FeatureCollection, Void, HashMap<String, Bitmap>> {

        private final HashMap<String, View> viewMap = new HashMap<>();
        private final WeakReference<PopupInfoActivity> activityRef;
        private final boolean refreshSource;

        GenerateViewIconTask(PopupInfoActivity activity, boolean refreshSource) {
            this.activityRef = new WeakReference<>(activity);
            this.refreshSource = refreshSource;
        }

        GenerateViewIconTask(PopupInfoActivity activity) {
            this(activity, false);
        }

        @SuppressWarnings("WrongThread")
        @Override
        protected HashMap<String, Bitmap> doInBackground(FeatureCollection... params) {
            PopupInfoActivity activity = activityRef.get();
            if (activity != null) {
                HashMap<String, Bitmap> imagesMap = new HashMap<>();
                LayoutInflater inflater = LayoutInflater.from(activity);

                FeatureCollection featureCollection = params[0];

                for (Feature feature : featureCollection.features()) {

                    BubbleLayout bubbleLayout = (BubbleLayout)
                            inflater.inflate(R.layout.symbol_layer_info_window_layout_callout, null);

                    String name = feature.getStringProperty(PROPERTY_NAME);
                    TextView titleTextView = bubbleLayout.findViewById(R.id.info_window_title);
                    titleTextView.setText(name);

                    TextView descriptionTextView = bubbleLayout.findViewById(R.id.info_window_description);
                    String style = feature.getStringProperty(PROPERTY_CAPITAL);
                    descriptionTextView.setText(String.format("描述", style));
                    int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                    bubbleLayout.measure(measureSpec, measureSpec);

                    float measuredWidth = bubbleLayout.getMeasuredWidth();

                    bubbleLayout.setArrowPosition(measuredWidth / 2 - 5);

                    Bitmap bitmap = SymbolGenerator.generate(bubbleLayout);
                    imagesMap.put(name, bitmap);
                    viewMap.put(name, bubbleLayout);
                }

                return imagesMap;
            } else {
                return null;
            }
        }

        @Override
        protected void onPostExecute(HashMap<String, Bitmap> bitmapHashMap) {
            super.onPostExecute(bitmapHashMap);
            PopupInfoActivity activity = activityRef.get();
            if (activity != null && bitmapHashMap != null) {
                activity.setImageGenResults(bitmapHashMap);
                if (refreshSource) {
                    activity.refreshSource();
                }
            }
            Toast.makeText(activity, "Tap on the marker", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Utility class to generate Bitmaps for Symbol.
     */
    private static class SymbolGenerator {

        /**
         * Generate a Bitmap from an Android SDK View.
         *
         * @param view the View to be drawn to a Bitmap
         * @return the generated bitmap
         */
        static Bitmap generate(@NonNull View view) {
            int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
            view.measure(measureSpec, measureSpec);

            int measuredWidth = view.getMeasuredWidth();
            int measuredHeight = view.getMeasuredHeight();

            view.layout(0, 0, measuredWidth, measuredHeight);
            Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
            bitmap.eraseColor(Color.TRANSPARENT);
            Canvas canvas = new Canvas(bitmap);
            view.draw(canvas);
            return bitmap;
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mapboxMap != null) {
            mapboxMap.removeOnMapClickListener(this);
        }
        mapView.onDestroy();
    }
}

Custom marker an

限制地圖可滑區域

Example

public class RestrictCameraActivity extends AppCompatActivity implements OnMapReadyCallback {
    private static final LatLng BOUND_CORNER_NW = new LatLng(25.188526, 121.272374);
    private static final LatLng BOUND_CORNER_SE = new LatLng(24.794329, 121.672012);
    private static final LatLngBounds RESTRICTED_BOUNDS_AREA = new LatLngBounds.Builder()
            .include(BOUND_CORNER_NW)
            .include(BOUND_CORNER_SE)
            .build();

    private final List<List<Point>> points = new ArrayList<>();
    private final List<Point> outerPoints = new ArrayList<>();
    private MapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Map8 access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Map8.getInstance(this, "<您的 Map8 Key>");

        MapboxMapOptions options = MapboxMapOptions.createFromAttributes(this, null)
                .camera(new CameraPosition.Builder()
                        .target(new LatLng(25.012947, 121.464916))
                        .zoom(9)
                        .build());

        // create map
        mapView = new MapView(this, options);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(this);

        setContentView(mapView);
    }

    @Override
    public void onMapReady(@NonNull final MapboxMap mapboxMap) {
        mapboxMap.setStyle(new Style.Builder().fromUri("https://api.map8.zone/styles/go-life-maps-tw-style-std/style.json"), new Style.OnStyleLoaded() {
            @Override
            public void onStyleLoaded(@NonNull Style style) {
                // Set the boundary area for the map camera
                mapboxMap.setLatLngBoundsForCameraTarget(RESTRICTED_BOUNDS_AREA);

                // Set the minimum zoom level of the map camera
                mapboxMap.setMinZoomPreference(2);

                showBoundsArea(style);

                showCrosshair();
            }
        });
    }

    /**
     * Add a FillLayer to show the boundary area
     *
     * @param loadedMapStyle a Style object which has been loaded by the map
     */
    private void showBoundsArea(@NonNull Style loadedMapStyle) {
        outerPoints.add(Point.fromLngLat(RESTRICTED_BOUNDS_AREA.getNorthWest().getLongitude(),
                RESTRICTED_BOUNDS_AREA.getNorthWest().getLatitude()));
        outerPoints.add(Point.fromLngLat(RESTRICTED_BOUNDS_AREA.getNorthEast().getLongitude(),
                RESTRICTED_BOUNDS_AREA.getNorthEast().getLatitude()));
        outerPoints.add(Point.fromLngLat(RESTRICTED_BOUNDS_AREA.getSouthEast().getLongitude(),
                RESTRICTED_BOUNDS_AREA.getSouthEast().getLatitude()));
        outerPoints.add(Point.fromLngLat(RESTRICTED_BOUNDS_AREA.getSouthWest().getLongitude(),
                RESTRICTED_BOUNDS_AREA.getSouthWest().getLatitude()));
        outerPoints.add(Point.fromLngLat(RESTRICTED_BOUNDS_AREA.getNorthWest().getLongitude(),
                RESTRICTED_BOUNDS_AREA.getNorthWest().getLatitude()));
        points.add(outerPoints);

        loadedMapStyle.addSource(new GeoJsonSource("source-id",
                Polygon.fromLngLats(points)));

        loadedMapStyle.addLayer(new FillLayer("layer-id", "source-id").withProperties(
                fillColor(Color.RED),
                fillOpacity(.25f)
        ));
    }

    private void showCrosshair() {
        View crosshair = new View(this);
        crosshair.setLayoutParams(new FrameLayout.LayoutParams(15, 15, Gravity.CENTER));
        crosshair.setBackgroundColor(Color.GREEN);
        mapView.addView(crosshair);
    }

    @Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }
}

Restrict area an

繪製多邊形圖層

Example

public class DrawPolygonActivity extends AppCompatActivity {

    // Taiwan map boundary
    private static final LatLng TW_BOUND_CORNER_NE = new LatLng(33.4, 138.45858);
    private static final LatLng TW_BOUND_CORNER_SW = new LatLng(15, 105);
    private static final LatLngBounds TW_BOUNDS_AREA = new LatLngBounds.Builder()
            .include(TW_BOUND_CORNER_NE)
            .include(TW_BOUND_CORNER_SW)
            .build();

    private MapView mapView;

    private static final List<List<Point>> POINTS = new ArrayList<>();
    private static final List<Point> OUTER_POINTS = new ArrayList<>();

    static {
        OUTER_POINTS.add(Point.fromLngLat(121.544111, 25.051779));
        OUTER_POINTS.add(Point.fromLngLat(121.548695, 25.057678));
        OUTER_POINTS.add(Point.fromLngLat(121.555254, 25.058218));
        OUTER_POINTS.add(Point.fromLngLat(121.556052, 25.054389));
        OUTER_POINTS.add(Point.fromLngLat(121.556656, 25.053385));
        OUTER_POINTS.add(Point.fromLngLat(121.557468, 25.051451));
        OUTER_POINTS.add(Point.fromLngLat(121.544111, 25.051779));
        POINTS.add(OUTER_POINTS);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Map8 access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Map8.getInstance(this, "<您的 Map8 Key>");

        MapboxMapOptions options = MapboxMapOptions.createFromAttributes(this, null)
                .camera(new CameraPosition.Builder()
                        .target(new LatLng(25.05488, 121.55097))
                        .zoom(14)
                        .bearing(0)
                        .build());

        // create map
        mapView = new MapView(this, options);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(@NonNull MapboxMap mapboxMap) {
                mapboxMap.setMaxZoomPreference(19.99);
                mapboxMap.setMinZoomPreference(6);
                mapboxMap.setLatLngBoundsForCameraTarget(TW_BOUNDS_AREA);
                mapboxMap.setStyle(new Style.Builder().fromUri("https://api.map8.zone/styles/go-life-maps-tw-style-std/style.json"), new Style.OnStyleLoaded() {
                    @Override
                    public void onStyleLoaded(@NonNull Style style) {
                        Log.d("[Map8 SDK Demo]", "mapView.getMapAsync() > onMapReady() > setStyle() > onStyleLoaded()");
                        style.addSource(new GeoJsonSource("source-id", Polygon.fromLngLats(POINTS)));
                        style.addLayerBelow(new FillLayer("layer-id", "source-id").withProperties(
                                fillColor(Color.parseColor("#3bb2d0")),
                                fillOpacity(.5f)), "settlement-label"
                        );
                    }
                });
            }
        });
        setContentView(mapView);
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }
}

Polygon an

繪製圓形圖層

Example

public class DrawCirclePolygonActivity extends AppCompatActivity {

    // Taiwan map boundary
    private static final LatLng TW_BOUND_CORNER_NE = new LatLng(33.4, 138.45858);
    private static final LatLng TW_BOUND_CORNER_SW = new LatLng(15, 105);
    private static final LatLngBounds TW_BOUNDS_AREA = new LatLngBounds.Builder()
            .include(TW_BOUND_CORNER_NE)
            .include(TW_BOUND_CORNER_SW)
            .build();

    private static final List<Feature> CIRCLE_POINTS = new ArrayList<>();

    private MapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Map8 access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Map8.getInstance(this, "<您的 Map8 Key>");

        MapboxMapOptions options = MapboxMapOptions.createFromAttributes(this, null)
                .camera(new CameraPosition.Builder()
                        .target(new LatLng(25.037853, 121.54885))
                        .zoom(16)
                        .build());

        // create map
        mapView = new MapView(this, options);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(@NonNull MapboxMap mapboxMap) {
                mapboxMap.setMaxZoomPreference(19.99);
                mapboxMap.setMinZoomPreference(6);
                mapboxMap.setLatLngBoundsForCameraTarget(TW_BOUNDS_AREA);
                mapboxMap.setStyle(new Style.Builder().fromUri("https://api.map8.zone/styles/go-life-maps-tw-style-std/style.json"), new Style.OnStyleLoaded() {
                    @Override
                    public void onStyleLoaded(@NonNull Style style) {
                        Log.d("[Map8 SDK Demo]", "mapView.getMapAsync() > onMapReady() > setStyle() > onStyleLoaded()");

                        Feature singleFeature = Feature.fromGeometry(Point.fromLngLat(121.54885,25.037853));
                        CIRCLE_POINTS.add(singleFeature);
                        FeatureCollection featureCollection = FeatureCollection.fromFeatures(CIRCLE_POINTS);
                        style.addSource(new GeoJsonSource("source-id", featureCollection));

                        CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");

                        circleLayer.withProperties(
                                PropertyFactory.circleRadius(100.0f),
                                PropertyFactory.circleOpacity(0.5f),
                                PropertyFactory.circleColor(Color.parseColor("#3bb2d0")),
                                PropertyFactory.circleStrokeColor(Color.parseColor("#3bb2d0"))
                        );

                        style.addLayer(circleLayer);
                    }
                });
            }
        });
        setContentView(mapView);
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }
}

Draw circle an

多個圓形圖層疊加

Example

public class DrawMultiCirclesActivity extends AppCompatActivity {

    // Taiwan map boundary
    private static final LatLng TW_BOUND_CORNER_NE = new LatLng(33.4, 138.45858);
    private static final LatLng TW_BOUND_CORNER_SW = new LatLng(15, 105);
    private static final LatLngBounds TW_BOUNDS_AREA = new LatLngBounds.Builder()
            .include(TW_BOUND_CORNER_NE)
            .include(TW_BOUND_CORNER_SW)
            .build();

    private static final List<Feature> CIRCLE_POINTS = new ArrayList<>();

    private MapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Map8 access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Map8.getInstance(this, MY_MAP8_KEY);

        MapboxMapOptions options = MapboxMapOptions.createFromAttributes(this, null)
                .camera(new CameraPosition.Builder()
                        .target(new LatLng(25.04624, 121.52415))
                        .zoom(14)
                        .tilt(42)
                        .build());

        // create map
        mapView = new MapView(this, options);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(@NonNull MapboxMap mapboxMap) {
                mapboxMap.setMaxZoomPreference(19.99);
                mapboxMap.setMinZoomPreference(6);
                mapboxMap.setLatLngBoundsForCameraTarget(TW_BOUNDS_AREA);
                mapboxMap.setStyle(new Style.Builder().fromUri("https://api.map8.zone/styles/go-life-maps-tw-style-std/style.json"), new Style.OnStyleLoaded() {
                    @Override
                    public void onStyleLoaded(@NonNull Style style) {
                        Log.d("[Map8 SDK Demo]", "mapView.getMapAsync() > onMapReady() > setStyle() > onStyleLoaded()");

                        mapboxMap.addPolygon(generatePerimeter(
                                new LatLng(25.04624, 121.52415),
                                0.5,
                                64,
                                Color.parseColor("#2970C0")));

                        mapboxMap.addPolygon(generatePerimeter(
                                new LatLng(25.05003, 121.52942),
                                0.3,
                                64,
                                Color.parseColor("#4E9A54")));

                        mapboxMap.addPolygon(generatePerimeter(
                                new LatLng(25.04996, 121.50792),
                                1.5,
                                64,
                                Color.parseColor("#D0104C")));
                    }
                });
            }
        });
        setContentView(mapView);
    }

    private PolygonOptions generatePerimeter(LatLng centerCoordinates, double radiusInKilometers, int numberOfSides, int fillColor) {
        List<LatLng> positions = new ArrayList<>();
        double distanceX = radiusInKilometers / (111.319 * Math.cos(centerCoordinates.getLatitude() * Math.PI / 180));
        double distanceY = radiusInKilometers / 110.574;

        double slice = (2 * Math.PI) / numberOfSides;

        double theta;
        double x;
        double y;
        LatLng position;
        for (int i = 0; i < numberOfSides; ++i) {
            theta = i * slice;
            x = distanceX * Math.cos(theta);
            y = distanceY * Math.sin(theta);

            position = new LatLng(centerCoordinates.getLatitude() + y,
                    centerCoordinates.getLongitude() + x);
            positions.add(position);
        }
        return new PolygonOptions()
                .addAll(positions)
                .fillColor(fillColor)
                .alpha(0.4f);
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }
}

Multi circle layer an

路徑線段動畫展示

Example

public class LineAnimationActivity extends AppCompatActivity implements OnMapReadyCallback {

    private static final String DOT_SOURCE_ID = "dot-source-id";
    private static final String LINE_SOURCE_ID = "line-source-id";

    private MapView mapView;
    private MapboxMap mapboxMap;

    private GeoJsonSource pointSource;
    private GeoJsonSource lineSource;
    private List<Point> routeCoordinateList;
    private List<Point> markerLinePointList = new ArrayList<>();
    private int routeIndex;
    private Point originPoint = Point.fromLngLat(121.5438079, 25.041573);
    private Point destinationPoint = Point.fromLngLat(121.552777, 25.033116);
    private Animator currentAnimator;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Map8 access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Map8.getInstance(this, "<您的 Map8 Key>");

        MapboxMapOptions options = MapboxMapOptions.createFromAttributes(this, null)
                .camera(new CameraPosition.Builder()
                        .target(new LatLng(25.037682, 121.548785))
                        .zoom(14)
                        .build());

        // create map
        mapView = new MapView(this, options);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(this);
        setContentView(mapView);
    }

    @Override
    public void onMapReady(@NonNull MapboxMap mapboxMap) {
        this.mapboxMap = mapboxMap;
        mapboxMap.setStyle(new Style.Builder().fromUri("https://api.map8.zone/styles/go-life-maps-tw-style-std/style.json"), new Style.OnStyleLoaded() {
            @Override
            public void onStyleLoaded(@NonNull Style style) {
                new LoadGeoJson(LineAnimationActivity.this).execute();
            }
        });
    }

    private void drawLines(@NonNull FeatureCollection featureCollection) {
        if (mapboxMap != null) {
            mapboxMap.getStyle(new Style.OnStyleLoaded() {
                @Override
                public void onStyleLoaded(@NonNull Style style) {
                    mapboxMap.easeCamera(CameraUpdateFactory.newLatLngBounds(
                            new LatLngBounds.Builder()
                                    .include(new LatLng(originPoint.latitude(), originPoint.longitude()))
                                    .include(new LatLng(destinationPoint.latitude(), destinationPoint.longitude()))
                                    .build(), 50), 5000);

                    initData(style, featureCollection);
                }
            });
        }
    }

    /**
     * Add data to the map once the GeoJSON has been loaded
     *
     * @param featureCollection returned GeoJSON FeatureCollection from the Directions API route request
     */
    private void initData(Style fullyLoadedStyle, @NonNull FeatureCollection featureCollection) {
        if (featureCollection.features() != null) {
            LineString lineString = ((LineString) featureCollection.features().get(0).geometry());
            if (lineString != null) {
                routeCoordinateList = lineString.coordinates();
                initSources(fullyLoadedStyle, featureCollection);
                initSymbolLayer(fullyLoadedStyle);
                initDotLinePath(fullyLoadedStyle);
                animate();
            }
        }
    }

    /**
     * Set up the repeat logic for moving the icon along the route.
     */
    private void animate() {
        // Check if we are at the end of the points list
        if ((routeCoordinateList.size() - 1 > routeIndex)) {
            Point indexPoint = routeCoordinateList.get(routeIndex);
            Point newPoint = Point.fromLngLat(indexPoint.longitude(), indexPoint.latitude());
            currentAnimator = createLatLngAnimator(indexPoint, newPoint);
            currentAnimator.setDuration(100);
            currentAnimator.start();
            routeIndex++;
        }
    }

    private static class PointEvaluator implements TypeEvaluator<Point> {

        @Override
        public Point evaluate(float fraction, Point startValue, Point endValue) {
            return Point.fromLngLat(
                    startValue.longitude() + ((endValue.longitude() - startValue.longitude()) * fraction),
                    startValue.latitude() + ((endValue.latitude() - startValue.latitude()) * fraction)
            );
        }
    }

    private Animator createLatLngAnimator(Point currentPosition, Point targetPosition) {
        ValueAnimator latLngAnimator = ValueAnimator.ofObject(new PointEvaluator(), currentPosition, targetPosition);
        latLngAnimator.setDuration((long) TurfMeasurement.distance(currentPosition, targetPosition, "meters"));
        latLngAnimator.setInterpolator(new LinearInterpolator());
        latLngAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                animate();
            }
        });
        latLngAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Point point = (Point) animation.getAnimatedValue();
                pointSource.setGeoJson(point);
                markerLinePointList.add(point);
                lineSource.setGeoJson(Feature.fromGeometry(LineString.fromLngLats(markerLinePointList)));
            }
        });

        return latLngAnimator;
    }

    /**
     * Add various sources to the map.
     */
    private void initSources(@NonNull Style loadedMapStyle, @NonNull FeatureCollection featureCollection) {
        loadedMapStyle.addSource(pointSource = new GeoJsonSource(DOT_SOURCE_ID, featureCollection));
        loadedMapStyle.addSource(lineSource = new GeoJsonSource(LINE_SOURCE_ID));
    }

    /**
     * Add the marker icon SymbolLayer.
     */
    private void initSymbolLayer(@NonNull Style loadedMapStyle) {
        loadedMapStyle.addImage("moving-red-marker", BitmapFactory.decodeResource(
                getResources(), R.drawable.pink_dot));
        loadedMapStyle.addLayer(new SymbolLayer("symbol-layer-id", DOT_SOURCE_ID).withProperties(
                iconImage("moving-red-marker"),
                iconSize(1f),
                iconOffset(new Float[] {5f, 0f}),
                iconIgnorePlacement(true),
                iconAllowOverlap(true)
        ));
    }

    /**
     * Add the LineLayer for the marker icon's travel route. Adding it under the "road-label" layer, so that the
     * this LineLayer doesn't block the street name.
     */
    private void initDotLinePath(@NonNull Style loadedMapStyle) {
        loadedMapStyle.addLayerBelow(new LineLayer("line-layer-id", LINE_SOURCE_ID).withProperties(
                lineColor(Color.parseColor("#F13C6E")),
                lineCap(Property.LINE_CAP_ROUND),
                lineJoin(Property.LINE_JOIN_ROUND),
                lineWidth(4f)), "road-label");
    }

    private static class LoadGeoJson extends AsyncTask<Void, Void, FeatureCollection> {

        private WeakReference<LineAnimationActivity> weakReference;

        LoadGeoJson(LineAnimationActivity activity) {
            this.weakReference = new WeakReference<>(activity);
        }

        @Override
        protected FeatureCollection doInBackground(Void... voids) {
            try {
                LineAnimationActivity activity = weakReference.get();
                if (activity != null) {
                    InputStream inputStream = activity.getAssets().open("map.geojson");
                    return FeatureCollection.fromJson(convertStreamToString(inputStream));
                }
            } catch (Exception exception) {
                Log.e("[Map8 SDK Demo]", "Exception Loading GeoJSON: " + exception.toString());
            }
            return null;
        }

        static String convertStreamToString(InputStream is) {
            Scanner scanner = new Scanner(is).useDelimiter("\\A");
            return scanner.hasNext() ? scanner.next() : "";
        }

        @Override
        protected void onPostExecute(@Nullable FeatureCollection featureCollection) {
            super.onPostExecute(featureCollection);
            LineAnimationActivity activity = weakReference.get();
            if (activity != null && featureCollection != null) {
                activity.drawLines(featureCollection);
            }
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }
}

Line animate an

建立群聚標示

Example

public class ShowClusterActivity extends AppCompatActivity {

    // Taiwan map boundary
    private static final LatLng TW_BOUND_CORNER_NE = new LatLng(33.4, 138.45858);
    private static final LatLng TW_BOUND_CORNER_SW = new LatLng(15, 105);
    private static final LatLngBounds TW_BOUNDS_AREA = new LatLngBounds.Builder()
            .include(TW_BOUND_CORNER_NE)
            .include(TW_BOUND_CORNER_SW)
            .build();

    private MapView mapView;
    private MapboxMap mapboxMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Map8 access token is configured here. This needs to be called either in your application
        // object or in the same activity which contains the mapview.
        Map8.getInstance(this, "<您的 Map8 Key>");

        MapboxMapOptions options = MapboxMapOptions.createFromAttributes(this, null)
                .camera(new CameraPosition.Builder()
                        .target(new LatLng(24.95997, 121.22649))
                        .zoom(13)
                        .bearing(0)
                        .tilt(50)
                        .build());

        // create map
        mapView = new MapView(this, options);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(@NonNull MapboxMap mapboxMap) {
                ShowClusterActivity.this.mapboxMap = mapboxMap;
                mapboxMap.setMaxZoomPreference(19.99);
                mapboxMap.setMinZoomPreference(6);
                mapboxMap.setLatLngBoundsForCameraTarget(TW_BOUNDS_AREA);
                mapboxMap.setStyle(new Style.Builder().fromUri("https://api.map8.zone/styles/go-life-maps-tw-style-std/style.json"), new Style.OnStyleLoaded() {
                    @Override
                    public void onStyleLoaded(@NonNull Style style) {
                        Log.d("[Map8 SDK Demo]", "mapView.getMapAsync() > onMapReady() > setStyle() > onStyleLoaded()");

                        addClusteredGeoJsonSource(style);
                        style.addImage(
                                "port",
                                BitmapFactory.decodeResource(ShowClusterActivity.this.getResources(), R.drawable.port),
                                true
                        );
                    }
                });
            }
        });
        setContentView(mapView);
    }

    @Override
    public void onStart() {
        super.onStart();
        mapView.onStart();
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
    }

    @Override
    public void onStop() {
        super.onStop();
        mapView.onStop();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mapView.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }


    private void addClusteredGeoJsonSource(@NonNull Style loadedMapStyle) {

        // Add a new source from the GeoJSON data and set the 'cluster' option to true.
        try {
            loadedMapStyle.addSource(
                    new GeoJsonSource("housesale",
                            new URI("asset://housesale.geojson"),
                            new GeoJsonOptions()
                                    .withCluster(true)
                                    .withClusterMaxZoom(14)
                                    .withClusterRadius(50)
                    )
            );
        } catch (URISyntaxException uriSyntaxException) {
            Log.e("[Map8 SDK Demo]", "Check the URL " + uriSyntaxException.getMessage());
        }

        int[][] layers = new int[][] {
                new int[] {150, ContextCompat.getColor(this, R.color.mapboxRed)},
                new int[] {20, ContextCompat.getColor(this, R.color.mapboxGreen)},
                new int[] {0, ContextCompat.getColor(this, R.color.mapbox_blue)}
        };

        float[][] layers_radius = new float[][] {
                new float[] {150, 40f},
                new float[] {20, 30f},
                new float[] {0, 20f}
        };

        //Creating a marker layer for single data points
        SymbolLayer unclustered = new SymbolLayer("unclustered-points", "housesale");

        unclustered.setProperties(
                iconImage("port"),
                iconAllowOverlap(true)
        );
        loadedMapStyle.addLayer(unclustered);

        for (int i = 0; i < layers.length; i++) {
            //Add clusters' circles
            CircleLayer circles = new CircleLayer("cluster-" + i, "housesale");
            circles.setProperties(
                    circleColor(layers[i][1]),
                    circleRadius(layers_radius[i][1])
            );

            Expression pointCount = toNumber(get("point_count"));

            // Add a filter to the cluster layer that hides the circles based on "point_count"
            circles.setFilter(
                    i == 0
                            ? all(has("point_count"),
                            gte(pointCount, literal(layers[i][0]))
                    ) : all(has("point_count"),
                            gte(pointCount, literal(layers[i][0])),
                            lt(pointCount, literal(layers[i - 1][0]))
                    )
            );
            loadedMapStyle.addLayer(circles);
        }

        //Add the count labels
        SymbolLayer count = new SymbolLayer("count", "housesale");
        count.setProperties(
                textField(Expression.toString(get("point_count"))),
                textSize(16f),
                textColor(Color.WHITE),
                textIgnorePlacement(true),
                textAllowOverlap(true),
                // *** IMPORTANT, 必須要指定字型,Cluster 才能運作
                textFont(new String[]{"Noto Sans Regular"})
        );
        loadedMapStyle.addLayer(count);
    }

}

Cluster an


台灣圖霸感謝您的支持與愛護!

有任何疑問,或是指教,都非常歡迎您找我們詢問。

非常感謝!



https://map8.zone