summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/symbol/symbolManager.java.ejs
blob: 3d52867ac63d12fc3d41cf46f0993a082b83a89c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<%
  const type = locals.type;
  const properties = locals.properties;
  const doc = locals.doc;
-%>
// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.

package com.mapbox.mapboxsdk.symbol;

import android.graphics.PointF;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.annotation.VisibleForTesting;
import android.support.v4.util.LongSparseArray;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.style.layers.PropertyValue;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;

import java.util.ArrayList;
import java.util.List;

import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;

/**
 * The symbol manager allows to add symbols to a map.
 */
public class SymbolManager {

  public static final String ID_GEOJSON_SOURCE = "mapbox-android-symbol-source";
  public static final String ID_GEOJSON_LAYER = "mapbox-android-symbol-layer";

  // map integration components
  private GeoJsonSource geoJsonSource;
  private SymbolLayer symbolLayer;

  // callback listeners
  private OnSymbolClickListener symbolClickListener;

  // internal data set
  private final LongSparseArray<Symbol> symbols = new LongSparseArray<>();
  private final List<Feature> features = new ArrayList<>();
  private long currentMarkerId;

  /**
   * Create a symbol manager, used to manage symbols.
   *
   * @param mapboxMap the map object to add symbols to
   */
  @UiThread
  public SymbolManager(@NonNull MapboxMap mapboxMap) {
    this(mapboxMap, new GeoJsonSource(ID_GEOJSON_SOURCE), new SymbolLayer(ID_GEOJSON_LAYER, ID_GEOJSON_SOURCE)
      .withProperties(
        getLayerDefinition()
      )
    );
  }

  /**
   * Create a symbol manager, used to manage symbols.
   *
   * @param mapboxMap     the map object to add symbols to
   * @param geoJsonSource the geojson source to add symbols to
   * @param symbolLayer   the sybol layer to visualise symbols with
   */
  @VisibleForTesting
  public SymbolManager(MapboxMap mapboxMap, @NonNull GeoJsonSource geoJsonSource, SymbolLayer symbolLayer) {
    this.geoJsonSource = geoJsonSource;
    this.symbolLayer = symbolLayer;
    mapboxMap.addSource(geoJsonSource);
    mapboxMap.addLayer(symbolLayer);
    mapboxMap.addOnMapClickListener(new MapClickResolver(mapboxMap));
  }

  /**
   * Create a symbol on the map from a LatLng coordinate.
   *
   * @param latLng place to layout the symbol on the map
   * @return the newly created symbol
   */
  @UiThread
  public Symbol createSymbol(@NonNull LatLng latLng) {
    Symbol symbol = new Symbol(this, currentMarkerId);
    symbol.setLatLng(latLng);
    symbols.put(currentMarkerId, symbol);
    currentMarkerId++;
    return symbol;
  }

  /**
   * Delete a symbol from the map.
   *
   * @param symbol to be deleted
   */
  @UiThread
  public void deleteSymbol(@NonNull Symbol symbol) {
    symbols.remove(symbol.getId());
    updateSource();
  }

  /**
   * Get a list of current symbols.
   *
   * @return list of symbols
   */
  @UiThread
  public LongSparseArray<Symbol> getSymbols() {
    return symbols;
  }

  /**
   * Trigger an update to the underlying source
   */
  public void updateSource() {
    // todo move feature creation to a background thread?
    features.clear();
    Symbol symbol;
    for (int i = 0; i < symbols.size(); i++) {
      symbol = symbols.valueAt(i);
      features.add(Feature.fromGeometry(symbol.getGeometry(), symbol.getFeature()));
    }
    geoJsonSource.setGeoJson(FeatureCollection.fromFeatures(features));
  }

  /**
   * Set a callback to be invoked when a symbol has been clicked.
   * <p>
   * To unset, use a null argument.
   * </p>
   *
   * @param symbolClickListener the callback to be invoked when a symbol is clicked, or null to unset
   */
  public void setOnSymbolClickListener(@Nullable OnSymbolClickListener symbolClickListener) {
    this.symbolClickListener = symbolClickListener;
  }

  private static PropertyValue<?>[] getLayerDefinition() {
    return new PropertyValue[]{
<% for (const property of properties) { -%>
<% if (supportsPropertyFunction(property)) { -%>
     <%- camelizeWithLeadingLowercase(property.name) %>(get("<%- property.name %>")),
<% } -%><% } -%>
    };
  }

  // Property accessors
<% for (const property of properties) { -%>
<% if (!supportsPropertyFunction(property)) { -%>
  /**
   * Get the <%- camelize(property.name) %> property
   *
   * @return property wrapper value around <%- propertyType(property) %>
   */
  public <%- propertyType(property) %> get<%- camelize(property.name) %>() {
    return symbolLayer.get<%- camelize(property.name) %>().value;
  }

  /**
   * Set the <%- camelize(property.name) %> property
   *
   * @param value property wrapper value around <%- propertyType(property) %>
   */
  public void set<%- camelize(property.name) %>(<%- propertyType(property) %> value) {
    symbolLayer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(value));
  }

<% } -%>
<% } -%>


  /**
   * Inner class for transforming map click events into symbol clicks
   */
  private class MapClickResolver implements MapboxMap.OnMapClickListener {

    private MapboxMap mapboxMap;

    private MapClickResolver(MapboxMap mapboxMap) {
      this.mapboxMap = mapboxMap;
    }

    @Override
    public void onMapClick(@NonNull LatLng point) {
      if (symbolClickListener == null) {
        return;
      }

      PointF screenLocation = mapboxMap.getProjection().toScreenLocation(point);
      List<Feature> features = mapboxMap.queryRenderedFeatures(screenLocation, ID_GEOJSON_LAYER);
      if (!features.isEmpty()) {
        long symbolId = features.get(0).getProperty(Symbol.ID_KEY).getAsLong();
        Symbol symbol = symbols.get(symbolId);
        if (symbol != null) {
          symbolClickListener.onSymbolClick(symbols.get(symbolId));
        }
      }
    }
  }

}