Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Data-Driven styling samples #1771

Merged
merged 32 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
85773d5
fix: import rememberMarkerState and use the marker state correctly
dkhawk Aug 27, 2024
15cb629
feat: dataset styling Kotlin samples
kikoso Aug 9, 2024
bf6a208
feat: added DataDrivenBoundariesActivity.kt
kikoso Aug 12, 2024
cad9d49
feat: added DataDrivenBoundariesActivity
kikoso Aug 26, 2024
b45cce7
feat: added documentation
kikoso Aug 26, 2024
02e9437
feat: added header
kikoso Aug 26, 2024
baf717a
chore: changed MY_MAP_ID to DEMO_MAP_ID
kikoso Aug 26, 2024
26f89fc
feat: moved data files to raw
kikoso Aug 27, 2024
1b1c949
feat: added DataDrivenBoundariesActivity
kikoso Aug 27, 2024
c866e15
feat: removed unused files
kikoso Aug 27, 2024
67ad696
feat: map id
kikoso Aug 27, 2024
40e1fad
chore: replace System.out.println with Log.d
kikoso Aug 27, 2024
5114122
feat: added region tags
kikoso Aug 28, 2024
0174e08
feat: added different set of DDS
kikoso Oct 29, 2024
e5ece81
Merge branch 'main' into feat/dataset_styling_samples
kikoso Oct 29, 2024
214b8b7
feat: added different set of DDS
kikoso Oct 29, 2024
1a097f6
feat: replacing files
kikoso Nov 8, 2024
7791244
feat: replacing files
kikoso Nov 8, 2024
09d5017
feat: compileSdk 35
kikoso Nov 8, 2024
0e0f1c9
feat: compileSdk 35
kikoso Nov 8, 2024
f4708b4
chore: updated README file
kikoso Nov 8, 2024
23e1f85
feat: trying to force different datasets
kikoso Nov 11, 2024
3e7d42c
Merge branch 'main' into feat/dataset_styling_samples
kikoso Feb 4, 2025
c9afdaf
feat: updated samples
kikoso Feb 4, 2025
a15972e
feat: Add data-driven styling for datasets
dkhawk Feb 11, 2025
927799d
fix: moves the dataset ids to the secrets.properties file
dkhawk Feb 11, 2025
dead30e
feat: significant rewrite to of DataDrivenDatasetStylingActivity
dkhawk Feb 11, 2025
bb16736
fix: exports the data driven styling activities so they can be run di…
dkhawk Feb 11, 2025
75f7890
feat: added header
kikoso Feb 12, 2025
5cf0ce6
feat: change ubuntu-latest
kikoso Feb 12, 2025
c98c764
feat: change ubuntu-latest
kikoso Feb 12, 2025
d7e5b11
feat: change ubuntu-latest
kikoso Feb 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ jobs:
- name: Build and check
run: |
cd ApiDemos
for dir in ./*/ ; do ( cd "$dir" && ./gradlew buildDebugPreBundle ); done

for dir in ./*/ ; do
if [[ "$dir" != "./resources/" ]]; then
( cd "$dir" && ./gradlew buildDebugPreBundle )
fi
done

build-WearOS:
runs-on: ubuntu-latest
timeout-minutes: 45
Expand Down
2 changes: 1 addition & 1 deletion ApiDemos/java/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 Google LLC
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
11 changes: 10 additions & 1 deletion ApiDemos/java/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2024 Google LLC
Copyright 2025 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,6 +33,7 @@ limitations under the License.
android:maxSdkVersion="22" />

<application
android:name=".ApiDemoApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/demo_title"
Expand Down Expand Up @@ -89,6 +90,14 @@ limitations under the License.
android:name=".CircleDemoActivity"
android:exported="true"
android:label="@string/circle_demo_label" />
<activity
android:name=".DataDrivenBoundariesActivity"
android:exported="true"
android:label="@string/data_driven_boundaries_label" />
<activity
android:name=".DataDrivenDatasetStylingActivity"
android:exported="true"
android:label="@string/data_driven_styling_label" />
<activity
android:name=".EventsDemoActivity"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2025 Google LLC
*
* 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 com.example.mapdemo;

import android.app.Application;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import java.util.Objects;

/**
* {@code ApiDemoApplication} is a custom Application class for the API demo.
*
* <p>This class is responsible for application-wide initialization and setup,
* such as checking for the presence and validity of the API key during the
* application's startup.</p>
*
* <p>It extends the {@link Application} class and overrides the {@link #onCreate()}
* method to perform these initialization tasks.</p>
*/
public class ApiDemoApplication extends Application {
private static final String TAG = "ApiDemoApplication";

@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate called");
checkApiKey();
}

/**
* Checks if the API key for Google Maps is properly configured in the application's metadata.
* <p>
* This method retrieves the API key from the application's metadata, specifically looking for
* a string value associated with the key "com.google.android.geo.API_KEY".
* The key must be present, not blank, and not set to the placeholder value "DEFAULT_API_KEY".
* <p>
* If any of these checks fail, a Toast message is displayed indicating that the API key is missing or
* incorrectly configured, and a RuntimeException is thrown.
* <p>
*/
private void checkApiKey() {
try {
ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = Objects.requireNonNull(appInfo.metaData);

String apiKey = bundle.getString("com.google.android.geo.API_KEY"); // Key name is important!

if (apiKey == null || apiKey.isBlank() || apiKey.equals("DEFAULT_API_KEY")) {
Toast.makeText(this, "API Key was not set in secrets.properties", Toast.LENGTH_LONG).show();
throw new RuntimeException("API Key was not set in secrets.properties");
}
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Package name not found.", e);
throw new RuntimeException("Error getting package info.", e);
} catch (NullPointerException e) {
Log.e(TAG, "Error accessing meta-data.", e); // Handle the case where meta-data is completely missing.
throw new RuntimeException("Error accessing meta-data in manifest", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// Copyright 2025 Google LLC
//
// 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 com.example.mapdemo;

import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.Feature;
import com.google.android.gms.maps.model.FeatureClickEvent;
import com.google.android.gms.maps.model.FeatureLayer;
import com.google.android.gms.maps.model.FeatureLayerOptions;
import com.google.android.gms.maps.model.FeatureStyle;
import com.google.android.gms.maps.model.FeatureType;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MapCapabilities;
import com.google.android.gms.maps.model.PlaceFeature;

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

/**
* This sample showcases how to use the Data-driven styling for boundaries. For more information
* on how the Data-driven styling for boundaries work, check out the following link:
* https://developers.google.com/maps/documentation/android-sdk/dds-boundaries/overview
*/
// [START maps_android_data_driven_styling_boundaries]
public class DataDrivenBoundariesActivity extends AppCompatActivity implements OnMapReadyCallback,
FeatureLayer.OnFeatureClickListener {

private GoogleMap map;

private FeatureLayer localityLayer = null;
private FeatureLayer areaLevel1Layer = null;
private FeatureLayer countryLayer = null;

private static final String TAG = DataDrivenBoundariesActivity.class.getName();

private static final LatLng HANA_HAWAII = new LatLng(20.7522, -155.9877); // Hana, Hawaii
private static final LatLng CENTER_US = new LatLng(39.8283, -98.5795); // Approximate geographical center of the contiguous US

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.data_driven_boundaries_demo);

SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
if (mapFragment != null) {
mapFragment.getMapAsync(this);
}

findViewById(R.id.button_hawaii).setOnClickListener(view -> centerMapOnLocation(HANA_HAWAII, 13.5f));
findViewById(R.id.button_us).setOnClickListener(view -> centerMapOnLocation(CENTER_US, 1f));
}

private void centerMapOnLocation(LatLng location, float zoomLevel) {
map.moveCamera(CameraUpdateFactory.newLatLngZoom(location, zoomLevel));
}

@RequiresApi(Build.VERSION_CODES.O)
@Override
public void onMapReady(GoogleMap googleMap) {
this.map = googleMap;
MapCapabilities capabilities = map.getMapCapabilities();
Log.d(TAG, "Data-driven Styling is available: " + capabilities.isDataDrivenStylingAvailable());

// Get the LOCALITY feature layer.
localityLayer = googleMap.getFeatureLayer(
new FeatureLayerOptions.Builder()
.featureType(FeatureType.LOCALITY)
.build()
);

// Apply style factory function to LOCALITY layer.
styleLocalityLayer();

// Get the ADMINISTRATIVE_AREA_LEVEL_1 feature layer.
areaLevel1Layer = googleMap.getFeatureLayer(
new FeatureLayerOptions.Builder()
.featureType(FeatureType.ADMINISTRATIVE_AREA_LEVEL_1)
.build()
);

// Apply style factory function to ADMINISTRATIVE_AREA_LEVEL_1 layer.
styleAreaLevel1Layer();

// Get the COUNTRY feature layer.
countryLayer = googleMap.getFeatureLayer(
new FeatureLayerOptions.Builder()
.featureType(FeatureType.COUNTRY)
.build()
);

// Register the click event handler for the Country layer.
countryLayer.addOnFeatureClickListener(this);

// Apply default style to all countries on load to enable clicking.
styleCountryLayer();
}

private void styleLocalityLayer() {
// Create the style factory function.
FeatureLayer.StyleFactory styleFactory = feature -> {
// Check if the feature is an instance of PlaceFeature,
// which contains a place ID.
if (feature instanceof PlaceFeature placeFeature) {

// Determine if the place ID is for Hana, HI.
if ("ChIJ0zQtYiWsVHkRk8lRoB1RNPo".equals(placeFeature.getPlaceId())) {
// Use FeatureStyle.Builder to configure the FeatureStyle object
// returned by the style factory function.
return new FeatureStyle.Builder()
// Define a style with purple fill at 50% opacity and
// solid purple border.
.fillColor(0x80810FCB)
.strokeColor(0xFF810FCB)
.build();
}
}
return null;
};

// Apply the style factory function to the feature layer.
if (localityLayer != null) {
localityLayer.setFeatureStyle(styleFactory);
}
}

private void styleAreaLevel1Layer() {
FeatureLayer.StyleFactory styleFactory = feature -> {
if (feature instanceof PlaceFeature placeFeature) {

// Return a hueColor in the range [-299,299]. If the value is
// negative, add 300 to make the value positive.
int hueColor = placeFeature.getPlaceId().hashCode() % 300;
if (hueColor < 0) {
hueColor += 300;
}
return new FeatureStyle.Builder()
// Set the fill color for the state based on the hashed hue color.
.fillColor(Color.HSVToColor(150, new float[]{hueColor, 1f, 1f}))
.build();
}
return null;
};

// Apply the style factory function to the feature layer.
if (areaLevel1Layer != null) {
areaLevel1Layer.setFeatureStyle(styleFactory);
}
}

// Set default fill and border for all countries to ensure that they respond
// to click events.
@RequiresApi(Build.VERSION_CODES.O)
private void styleCountryLayer() {
FeatureLayer.StyleFactory styleFactory = feature -> new FeatureStyle.Builder()
// Set the fill color for the country as white with a 10% opacity.
// This requires minApi 26
.fillColor(Color.argb(0.1f, 0f, 0f, 0f))
// Set border color to solid black.
.strokeColor(Color.BLACK)
.build();

// Apply the style factory function to the country feature layer.
if (countryLayer != null) {
countryLayer.setFeatureStyle(styleFactory);
}
}

// Define the click event handler.
@Override
public void onFeatureClick(FeatureClickEvent event) {
// Get the list of features affected by the click using
// getPlaceIds() defined below.
List<String> selectedPlaceIds = getPlaceIds(event.getFeatures());
if (!selectedPlaceIds.isEmpty()) {
FeatureLayer.StyleFactory styleFactory = feature -> {
// Use PlaceFeature to get the placeID of the country.
if (feature instanceof PlaceFeature) {
if (selectedPlaceIds.contains(((PlaceFeature) feature).getPlaceId())) {
return new FeatureStyle.Builder()
// Set the fill color to red.
.fillColor(Color.RED)
.build();
}
}
return null;
};

// Apply the style factory function to the feature layer.
if (countryLayer != null) {
countryLayer.setFeatureStyle(styleFactory);
}
}
}

// Get a List of place IDs from the FeatureClickEvent object.
private List<String> getPlaceIds(List<Feature> features) {
List<String> placeIds = new ArrayList<>();
for (Feature feature : features) {
if (feature instanceof PlaceFeature) {
placeIds.add(((PlaceFeature) feature).getPlaceId());
}
}
return placeIds;
}
}
// [END maps_android_data_driven_styling_boundaries]
Loading