|
29 | 29 | import com.google.android.gms.maps.model.Dash;
|
30 | 30 | import com.google.android.gms.maps.model.Dot;
|
31 | 31 | import com.google.android.gms.maps.model.Gap;
|
| 32 | +import com.google.android.gms.maps.model.GroundOverlay; |
32 | 33 | import com.google.android.gms.maps.model.JointType;
|
33 | 34 | import com.google.android.gms.maps.model.LatLng;
|
34 | 35 | import com.google.android.gms.maps.model.LatLngBounds;
|
|
40 | 41 | import com.google.maps.android.heatmaps.Gradient;
|
41 | 42 | import com.google.maps.android.heatmaps.WeightedLatLng;
|
42 | 43 | import io.flutter.FlutterInjector;
|
| 44 | +import io.flutter.plugins.googlemaps.Messages.FlutterError; |
43 | 45 | import java.io.IOException;
|
44 | 46 | import java.io.InputStream;
|
45 | 47 | import java.util.ArrayList;
|
@@ -849,6 +851,146 @@ static Tile tileFromPigeon(Messages.PlatformTile tile) {
|
849 | 851 | return new Tile(tile.getWidth().intValue(), tile.getHeight().intValue(), tile.getData());
|
850 | 852 | }
|
851 | 853 |
|
| 854 | + /** |
| 855 | + * Set the options in the given ground overlay object to the given sink. |
| 856 | + * |
| 857 | + * @param groundOverlay the object expected to be a PlatformGroundOverlay containing the ground |
| 858 | + * overlay options. |
| 859 | + * @param sink the GroundOverlaySink where the options will be set. |
| 860 | + * @param assetManager An instance of Android's AssetManager, which provides access to any raw |
| 861 | + * asset files stored in the application's assets directory. |
| 862 | + * @param density the density of the display, used to calculate pixel dimensions. |
| 863 | + * @param wrapper the BitmapDescriptorFactoryWrapper to create BitmapDescriptor. |
| 864 | + * @return the identifier of the ground overlay. The identifier is valid as long as the ground |
| 865 | + * overlay exists. |
| 866 | + * @throws IllegalArgumentException if required fields are missing or invalid. |
| 867 | + */ |
| 868 | + static @NonNull String interpretGroundOverlayOptions( |
| 869 | + @NonNull Messages.PlatformGroundOverlay groundOverlay, |
| 870 | + @NonNull GroundOverlaySink sink, |
| 871 | + @NonNull AssetManager assetManager, |
| 872 | + float density, |
| 873 | + @NonNull BitmapDescriptorFactoryWrapper wrapper) { |
| 874 | + sink.setTransparency(groundOverlay.getTransparency().floatValue()); |
| 875 | + sink.setZIndex(groundOverlay.getZIndex().floatValue()); |
| 876 | + sink.setVisible(groundOverlay.getVisible()); |
| 877 | + if (groundOverlay.getAnchor() != null) { |
| 878 | + sink.setAnchor( |
| 879 | + groundOverlay.getAnchor().getX().floatValue(), |
| 880 | + groundOverlay.getAnchor().getY().floatValue()); |
| 881 | + } |
| 882 | + sink.setBearing(groundOverlay.getBearing().floatValue()); |
| 883 | + sink.setClickable(groundOverlay.getClickable()); |
| 884 | + sink.setImage(toBitmapDescriptor(groundOverlay.getImage(), assetManager, density, wrapper)); |
| 885 | + if (groundOverlay.getPosition() != null) { |
| 886 | + if (groundOverlay.getWidth() == null) { |
| 887 | + throw new FlutterError( |
| 888 | + "Invalid GroundOverlay", |
| 889 | + "Width is required when using a ground overlay with a position.", |
| 890 | + null); |
| 891 | + } |
| 892 | + sink.setPosition( |
| 893 | + latLngFromPigeon(groundOverlay.getPosition()), |
| 894 | + groundOverlay.getWidth().floatValue(), |
| 895 | + groundOverlay.getHeight() != null ? groundOverlay.getHeight().floatValue() : null); |
| 896 | + } else if (groundOverlay.getBounds() != null) { |
| 897 | + sink.setPositionFromBounds(latLngBoundsFromPigeon(groundOverlay.getBounds())); |
| 898 | + } |
| 899 | + return groundOverlay.getGroundOverlayId(); |
| 900 | + } |
| 901 | + |
| 902 | + /** |
| 903 | + * Converts a GroundOverlay object to a PlatformGroundOverlay Pigeon object. |
| 904 | + * |
| 905 | + * @param groundOverlay the GroundOverlay object to convert. |
| 906 | + * @param groundOverlayId the identifier of the GroundOverlay. |
| 907 | + * @param isCreatedWithBounds indicates if the GroundOverlay was created with bounds. |
| 908 | + * @return the converted PlatformGroundOverlay object. |
| 909 | + */ |
| 910 | + static @NonNull Messages.PlatformGroundOverlay groundOverlayToPigeon( |
| 911 | + @NonNull GroundOverlay groundOverlay, |
| 912 | + @NonNull String groundOverlayId, |
| 913 | + boolean isCreatedWithBounds) { |
| 914 | + |
| 915 | + // Image is mandatory field on PlatformGroundOverlay (and it should be kept |
| 916 | + // non-nullable), therefore image must be set for the object. The image is |
| 917 | + // description either contains set of bytes, or path to asset. This info is |
| 918 | + // converted to format google maps uses (BitmapDescription), and the original |
| 919 | + // data is not stored on native code. Therefore placeholder image is used for |
| 920 | + // the image field. |
| 921 | + Messages.PlatformBitmap dummyImage = |
| 922 | + new Messages.PlatformBitmap.Builder() |
| 923 | + .setBitmap( |
| 924 | + new Messages.PlatformBitmapBytesMap.Builder() |
| 925 | + .setByteData(new byte[] {0}) |
| 926 | + .setImagePixelRatio(1.0) |
| 927 | + .setBitmapScaling(Messages.PlatformMapBitmapScaling.NONE) |
| 928 | + .build()) |
| 929 | + .build(); |
| 930 | + |
| 931 | + Messages.PlatformGroundOverlay.Builder builder = |
| 932 | + new Messages.PlatformGroundOverlay.Builder() |
| 933 | + .setGroundOverlayId(groundOverlayId) |
| 934 | + .setImage(dummyImage) |
| 935 | + .setWidth((double) groundOverlay.getWidth()) |
| 936 | + .setHeight((double) groundOverlay.getHeight()) |
| 937 | + .setBearing((double) groundOverlay.getBearing()) |
| 938 | + .setTransparency((double) groundOverlay.getTransparency()) |
| 939 | + .setZIndex((long) groundOverlay.getZIndex()) |
| 940 | + .setVisible(groundOverlay.isVisible()) |
| 941 | + .setClickable(groundOverlay.isClickable()); |
| 942 | + |
| 943 | + if (isCreatedWithBounds) { |
| 944 | + builder.setBounds(Convert.latLngBoundsToPigeon(groundOverlay.getBounds())); |
| 945 | + } else { |
| 946 | + builder.setPosition(Convert.latLngToPigeon(groundOverlay.getPosition())); |
| 947 | + } |
| 948 | + |
| 949 | + builder.setAnchor(Convert.buildGroundOverlayAnchorForPigeon(groundOverlay)); |
| 950 | + return builder.build(); |
| 951 | + } |
| 952 | + |
| 953 | + /** |
| 954 | + * Builds a PlatformDoublePair representing the anchor point for a GroundOverlay. |
| 955 | + * |
| 956 | + * @param groundOverlay the GroundOverlay object. |
| 957 | + * @return the PlatformDoublePair representing the anchor point. |
| 958 | + */ |
| 959 | + @VisibleForTesting |
| 960 | + public static @NonNull Messages.PlatformDoublePair buildGroundOverlayAnchorForPigeon( |
| 961 | + @NonNull GroundOverlay groundOverlay) { |
| 962 | + Messages.PlatformDoublePair.Builder anchorBuilder = new Messages.PlatformDoublePair.Builder(); |
| 963 | + |
| 964 | + // Position is overlays anchor point. Calculate normalized anchor point based on position and bounds. |
| 965 | + LatLng position = groundOverlay.getPosition(); |
| 966 | + LatLngBounds bounds = groundOverlay.getBounds(); |
| 967 | + |
| 968 | + // Calculate normalized latitude. |
| 969 | + double height = bounds.northeast.latitude - bounds.southwest.latitude; |
| 970 | + double normalizedLatitude = 1.0 - ((position.latitude - bounds.southwest.latitude) / height); |
| 971 | + |
| 972 | + // Constant for full circle degrees. |
| 973 | + final double FULL_CIRCLE_DEGREES = 360.0; |
| 974 | + |
| 975 | + // Calculate normalized longitude. |
| 976 | + // For longitude, if the bounds cross the antimeridian (west > east), |
| 977 | + // adjust the width accordingly. |
| 978 | + double west = bounds.southwest.longitude; |
| 979 | + double east = bounds.northeast.longitude; |
| 980 | + double width = (west <= east) ? (east - west) : (FULL_CIRCLE_DEGREES - (west - east)); |
| 981 | + |
| 982 | + // Normalize the longitude of the anchor position relative to the western boundary. |
| 983 | + // Handles cases where the ground overlay crosses the antimeridian. |
| 984 | + double normalizedLongitude = |
| 985 | + ((position.longitude < west ? position.longitude + FULL_CIRCLE_DEGREES : position.longitude) |
| 986 | + - west) |
| 987 | + / width; |
| 988 | + |
| 989 | + anchorBuilder.setX(normalizedLongitude); |
| 990 | + anchorBuilder.setY(normalizedLatitude); |
| 991 | + return anchorBuilder.build(); |
| 992 | + } |
| 993 | + |
852 | 994 | static class BitmapDescriptorFactoryWrapper {
|
853 | 995 | /**
|
854 | 996 | * Creates a BitmapDescriptor from the provided asset key using the {@link
|
|
0 commit comments