Android: How do I add a translucent geofence over a map. I.e., i can fill it with a color but the map below is still visible.
This is what I wish to achieve
Use Color.argb(). To learn more, go to the Color class reference page (http://developer.android.com/reference/android/graphics/Color.html). Here is an example of making the blue translucent fill:
Color.argb(
100, //This is your alpha. Adjust this to make it more or less translucent
Color.red(Color.BLUE), //Red component.
Color.green(Color.BLUE), //Green component.
Color.blue(Color.BLUE)); //Blue component.
This will make the blue translucent color you wanted. If you have a custom hex color (0xFFFFFFFF), just adjust the first byte (0xFF......) to make it more or less translucent.
EDIT
Here is a simple function that you can use to pass in the alpha and color to adjust it:
int alphaAdjust(int alpha, int color) {
return Color.argb(
alpha, //This is your alpha. Adjust this to make it more or less translucent
Color.red(color), //Red component.
Color.green(color), //Green component.
Color.blue(color)); //Blue component.
}
You can create a Circle, it is very easy:
Having your geofence details already in place (geofLocation and geofRadius) you can do:
// Instantiates a new CircleOptions object and defines the center and radius
CircleOptions circleOptions = new CircleOptions()
.strokeColor(Color.BLACK) //Outer black border
.fillColor(Color.TRANSPARENT) //inside of the geofence will be transparent, change to whatever color you prefer like 0x88ff0000 for mid-transparent red
.center(geofLocation) // the LatLng Object of your geofence location
.radius(geofRadius)); // The radius (in meters) of your geofence
// Get back the mutable Circle
Circle circle = myMap.addCircle(circleOptions);
Try the examples listed in the documentation. I'd suggest to use a translucent circle to indicate your geofence.
Something like this:
LatLng latLng = ...
CircleOptions circleOptions = new CircleOptions();
circleOptions.center(latLng)
.fillColor(Color.argb(64, 0, 255, 0))
.strokeColor(Color.GREEN)
.strokeWidth(1)
.radius(100);
Notice the first argument in Color.argb() method (value 64), that argument controls the alpha/transculency of the circle, in this case it will be a transculent green circle. change the first argument value from 0 to 255 to achieve the transculency you desire. Here is the Color.argb method javadoc - http://developer.android.com/reference/android/graphics/Color.html#argb(int, int, int, int)
Related
How to remove border color from android google heatmap? I'm using this code to draw:
fun drawPolygons(polygons: Array<NetworkMapPolygonModel>, scale: Float) {
map?.let { map ->
val points = ArrayList<WeightedLatLng>()
polygons.forEach {
val intensity = ((it.signalStrength - 12) * -1).toDouble()
points.add(WeightedLatLng(
LatLng(it.aLatitude, it.aLongitude), intensity
))
}
val radius = 40
val provider = HeatmapTileProvider.Builder()
.weightedData(points)
.radius(radius)
.maxIntensity(48.0)
.build()
map.addTileOverlay(TileOverlayOptions().tileProvider(provider))
}
}
But google map drawing me this map:
I want to remove outer green border (in red square in the screenshot). But cannot find how to do this.
Please help!
To simply eliminate the green (and therefore the representation of data) use the gradient property of the HeatmapFileProvider.Builder to change the initial color from green (default) to yellow and change the starting threshold from the threshold that corresponded to green (0.2 default) to approximately (0.4) (you'll have to experiment with this number - at the bottom of this answer I show how to determine this and it is 0.454). And rather than a gradual fade-in from transparent I'll show how to start fully opaque at the desired color.
Before diving into modifications, understand that there is a default gradient used without being specified and it is this:
// Create the gradient.
val colors = intArrayOf(
Color.rgb(120, 225, 0), // green
Color.rgb(255, 0, 0) // red
)
val startPoints = floatArrayOf(0.2f, 1f)
val gradient = Gradient(colors, startPoints)
// Create the tile provider.
val provider = HeatmapTileProvider.Builder()
.data(latLngs)
.gradient(gradient)
.build()
In what follows, the colors array and startPoints are modified to demonstrate the point for each adjustment. So in this snippet, it shows eliminating green but transitioning from transparent to yellow (not what you are looking for but just an example).
// Create the gradient.
val colors = intArrayOf(
Color.rgb(255, 225, 0), // yellow
Color.rgb(255, 0, 0) // red
)
val startPoints = floatArrayOf(0.4f, 1f)
val gradient = Gradient(colors, startPoints)
In this answer I've used my own data representing crime statistics in Sacremento. You'll soon see why green is a good choice for the edge data.
The gradient property is composed of two controls:
colors
starting points (0.0 to 1.0)
By default colors is (GREEN,RED) and starting points is (0.2, 1.0). One thing to note is if the first starting point is non-zero (as in the defaults), the gradient from pts 0.0 to 0.2 transition from transparent to the color; otherwise it starts with the first color at pt 0.0.
Image (A) is the default settings for my data.
I then wanted to see where the green actually started (0.2) without the transition from transparent to green (0.0 - 0.2). To do this I modified the gradient to be transparent up to close to 2.0 - then introduce a border (black) from close-to-2.0 to 2.0 and the rest is defaults.
// Create the gradient.
val colors = intArrayOf(
Color.argb(0, 0, 0, 0), // transparent
Color.argb(0, 0, 0, 0), // transparent
Color.rgb(0, 0, 0), // black
Color.rgb(120, 255, 0), // green
Color.rgb(255, 0, 0) // red
)
val startPoints = floatArrayOf(0.0f, 0.15f, 0.18f, 0.2f, 1f)
val gradient = Gradient(colors, startPoints)
Image (B) is this "border" added to show where the pure green data (2.0+) starts:
And now to address the issue of removing green. The default interpolation is summarized as follows: 0 - 0.2f (transparent to green) and 0.2f - 1.0f (green to red). So somewhere in there is the interpolated yellow. For this answer, an approximation is made that the yellow is roughly 0.4 (but I'll follow up with a calculation to show how to figure that out). Again I add the border to show exactly where the yellow (0.4) is starting:
// Create the gradient.
val colors = intArrayOf(
Color.argb(0, 0, 0, 0), // transparent
Color.argb(0, 0, 0, 0), // transparent
Color.rgb(0, 0, 0), // black
Color.rgb(255, 255, 0), // yellow
Color.rgb(255, 0, 0) // red
)
val startPoints = floatArrayOf(0.0f, 0.35f, 0.38f, 0.4f, 1f)
val gradient = Gradient(colors, startPoints)
This answer demonstrates how to control the color results; the more pertinent question which I cannot answer is what information are you trying to convey: eliminating the "green" is eliminating data, whereas turning the data into a transition from yellow data using default starting points would not eliminate data but just eliminate green. (I did not post an example of this but worth considering.)
(A)
(B)
(C)
This next part of the answer focuses on color; hopefully not too far off topic.
Again, in the default gradient there are 2 percentiles specified (0.2, 1.0) and one implied starting at 0.0. And again, the colors for these three are: (0x0078E100 (fully transparent green(120,225,0)), 0xFF78E100 (opaque green), 0xFFFF0000 (opaque red)).
In the OP the question is in terms of color ("remove green border") which leads to having to make an assumption: up to what point should the removal stop. I chose to assume yellow - but since the colors represent percentiles of data really the question should be phrased in terms of percentiles to be precise.) But looking at the data representation in terms of color presents a problem: Where is the data percentile for yellow given the default gradient.
So to help this discussion I created a simple TileProvider whose purpose is to display the gradient of color in each tile rendered. Here's a sample:
This image shows one full tile and two partial tiles at the top and bottom; so here focus on the one full tile in the middle.
Some key points: (1) the tile starts at fully transparent (data percentile 0.0) and transitions to the first color in the default gradient at which point a black line segment is drawn representing the 20th percentile. From there the tile transitions from green to the second color in the default gradient (red) representing the 100th percentile. Along the way a second black line segment is drawn for the color in the gradient "closest" to "yellow".
In order to discover the closest data percentile to yellow, some knowledge of how the gradient is created is necessary. In short, the endpoints of each color segment in RGB values of the colors provided () are converted to HSV values. From these HSL values the ratio between the start and end points is applied the HSV value and then converted back to RGB.
Once the RGB color within the gradient segment is determined it's "distance" to the target ("tgt") color (YELLOW) is computed and the minimum distance found:
int deltaR = Color.red(tgt) - Color.red(ic);
int deltaG = Color.green(tgt) - Color.green(ic);
int deltaB = Color.blue(tgt) - Color.blue(ic);
double d = Math.sqrt(deltaR*0.3F*deltaR*0.3F + deltaG*0.59F*deltaG*0.59F + deltaB*0.11*deltaB*0.11);
As it turns out the data percentile closest to yellow is 45.4%. So the data presented in the final image above (3) represents the upper 54.6% of the data.
For reference here is the implementation of the TileProvider used. The mColors array is the 1000 element colors map which the heat map generates by default:
private class MyTileProvider implements TileProvider {
public MyTileProvider() {
}
#Override
public Tile getTile(int x, int y, int zoom) {
Bitmap tile = Bitmap.createBitmap(512,512,Bitmap.Config.ARGB_8888);
tile.setPixels(mColors,0,Math.max(512,mPixelsPerColor),0,0,512,512);
Log.d(TAG,"Tile gen done: "+x+","+y+" "+zoom);
return convertBitmap(tile);
}
}
For reference on gradients and heat maps use this answer:
Weighted heat maps in android
For reference on computing "color distance": https://stackoverflow.com/a/1847112/2711811
For reference on the map utils heat map implementation (a subdirectory within the andoid-maps-utils repo): https://github.com/googlemaps/android-maps-utils/tree/ac9684d627905587b020a0eac301e94478804a48/library/src/main/java/com/google/maps/android/heatmaps
I am trying to add a custom colored marker to a map. I found this answer, which states that I need to feed a BitmapDescriptorFactory with a HUE color and give that to the .icon() method of the MarkerOptions, but when I am using a converter to get the HUE value for my color (#678E00), it gives me 3 values, (76,100,27.8), but I can only provide 1 number to the BitmapDescriptorFactory. Do you know how I could do this?
The HSL (Hue, Saturation, Lightness) representation of your Hex color (#678E00) is 76, 100, 27.8.
This means that the Hue of your color is 76 which is the value that you need to feed to the defaultMarker function:
.defaultMarker(76)
Take into account that you can only set the Hue of the marker and the Saturation and Lightness won't be modified so your color will be different than the original #678E00
I am trying to create a weighted heat map for my android app project. I had a look at the google documentation for it. I don't understand how to create a new gradient using colors array and starting points array. The starting array is denoted as
The starting point for each color, given as a percentage of the maximum intensity.
What does it mean? How to relate color array with starting point array?
int[] colors = {
Color.GREEN, // green(0-50)
Color.YELLOW, // yellow(51-100)
Color.rgb(255,165,0), //Orange(101-150)
Color.RED, //red(151-200)
Color.rgb(153,50,204), //dark orchid(201-300)
Color.rgb(165,42,42) //brown(301-500)
};
float[] startpoints = {
};
I need to fill this startpoints array.
Here are some assumptions:
the gradient colorMapSize is 1000 (default) but should be set to 500
the color values length is 6
the startPoints length is 6 (has to be the same length as the colors)
the colors array is specified in OP
the startingPoints array should be { 0.1F, 0.2F, 0.3F, 0.4F, 0.6F, 1.0F }
Here's a diagram to help the discussion:
ColorMap
The first thing to understand is the colorMap. This is generated by the
Gradient builder. It is an 'int' array with size by default of 1000 but is
customizable in one of the constructors - recommend 500 in your example. So every value in this array is a color value. (The size affects the resolution of your gradient.)
Color values in the colorMap are affected by 2 controls which produce color
intervals: the startPoints array and the colors array. In your example there
is 6 color intervals.
A color interval is defined as a starting color and end color and the number
of 'colorMap' slots in range. All colorMap values for any given interval
are interpolated using the start/end colors for that range.
If the first value of the startPoints array is 0 then the first color interval
is assumed to be solid - non-zero implies a transition from transparent to the first color which seems most desirable. (See example where the startPoints is set to 0 and notice the jaggedness of the outside areas.)
If the first value of the startPoints array is not 0 then the first color
interval is defined as starting with the first color (colors[0]) and a range
of the colorMapSize * the first starting point, e.g. 500 * 0.1 = 50
and ending with the same color.
For all remaining defined colors an interval is generated - again with a
starting colorMap slot, a starting color (which is the previous color end),
and ending color (which is the current color in the table) and a range.
If the last startingPoint is not 1.0, then the last color is used for start
and stop. In this example, 1.0 is probably best.
(Opacity applies to the whole layer and simply affects the alpha channel of the color.)
Tiles
This is where 'intensity' is appreciated and the effect a WeightedLatLng can play a part.
When the map is generated it divides the viewable area into tiles - the
number of tiles is a function of zoom level. At zoom level 0 there is 1 tile,
and the arbitrary zoom level tile count is 2^n (where n is the zoom level).
Each tile is further divided into buckets. The number of buckets is a function
of zoom level and Radius (blur). Each bucket corresponds to a geographical
area within the tile - think rectangle-grid.
The points inside the current tile are obtained from the data set.
For all of the points within the geographic bounds of the tile, the intensity value of the point is added to its corresponding bucket. The intensity value for a point by default is 1. The WeightedLatLng allows you to bias a point by changing this value from 1 to some number (larger to increase importance, smaller to decrease importance). So the result is the bucket intensity tally is affected from what it would be for just LatLngs.
Once the intensity buckets are tallied, the intensity values are colorized using
the colorMap determined in the first section. Note that the range of intensity values are scaled to the size of the colorMap such that the maximum intensity value maps to the last color.
The buckets are then applied to the tile and the tile is made into a bitmap
and is rendered.
Example
For an example I used some data on crime in Sacramento. I first created a non-weighted heatmap. And then created a weighted heatmap to give an importance to auto-thefts by specifying an intensity of 5.0 (verses the default of 1.0 for any point) for crimes with an NCIC code of 2404 (vehicle-theft). The 5.0 is somewhat arbitrary and really depends on the data and what you're trying to convey.
Non-Weighted / Weighted (by vehicle theft)
And an example where the first startPoint[0] is 0.0 - which shows the lack of a transition from transparent to initial color:
Here are the relevant portions of the MapActivity:
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.getUiSettings().setZoomControlsEnabled(true);
// Add a marker in Sydney and move the camera
LatLng sydney = new LatLng(-34, 151);
mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
addHeatMapWeighted();
//addHeatMap();
}
public void addHeatMapWeighted() {
Gradient gradient = new Gradient(colors,startpoints);
List<WeightedLatLng> wDat = new CrimeData().getWeightedPositions();
HeatmapTileProvider provider = new HeatmapTileProvider.Builder().weightedData(wDat).gradient(gradient).build();
mMap.addTileOverlay(new TileOverlayOptions().tileProvider(provider));
}
public void addHeatMap() {
Gradient gradient = new Gradient(colors,startpoints);
List<LatLng> cDat = new CrimeData().getPositions();
HeatmapTileProvider provider = new HeatmapTileProvider.Builder().data(cDat).gradient(gradient).build();
mMap.addTileOverlay(new TileOverlayOptions().tileProvider(provider));
}
int[] colors = {
Color.GREEN, // green(0-50)
Color.YELLOW, // yellow(51-100)
Color.rgb(255,165,0), //Orange(101-150)
Color.RED, //red(151-200)
Color.rgb(153,50,204), //dark orchid(201-300)
Color.rgb(165,42,42) //brown(301-500)
};
float[] startpoints = {
0.1F, 0.2F, 0.3F, 0.4F, 0.6F, 1.0F
};
Start of Interval
OK, so you may have noticed that the start of each colorMap interval starts at a nice round number (0,50,100...) which doesn't quite match your requirements (51, 101...301). I would argue that your commented-ranges are not right since really that means there are 501 possible colors which is a bit odd. But if you really wanted that range as specified then you'd have to do some math on to come up with an alternate startPoints array of: (51/501,101/501,151/501,201/501,301/501,501/501) = (.101796407,.201596806,.301397206,.401197605,.600798403, 1.0)
Radius
The radius value is an input to the HeatMap's Gaussian Blur implementation. Sometimes a picture is the best explanation: this is an animated Gif which cycles through a HeatMap of radius 20 to 50 in steps of 10 (with the blurriest being radius 50).
Since the heat map is intended to convey meaning to the information, it's really left to the data presenter to assess what is the best radius effect. So for example, in the case of crime data, as a consumer of the data looking for a place to live, I'd probably gain more value from the data with some blur. On the other hand if the data was presenting fire stations then too much blur could very well lead one to believe they are covered when they are not.
Dots
Just using dots (circles) tells a more accurate story than the heat map and which at the broader zoom levels visually blurs just like a heat map albeit without the color. For example, here are two dot renderings, unweighted and weighted. The weighted bias is for drug crimes (NCIC codes [3500,3600)) which shows that drug crimes predominate this area.
HeatMap Trivia
From the referenced book (3):
The term "heat map" was trademarked in 1991 by software developer
Cormac Kinney. It was then acquired by SS&C Technologies, Inc. but
they did not extend the license and it was annulled in 2006.
References
Crime data: https://support.spatialkey.com/spatialkey-sample-csv-data/
Android code:
https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/heatmaps/
HeatMap trademark: https://books.google.com/books?id=clIWDAAAQBAJ&pg=PA219&lpg=PA219&dq=is+heatmap+trademarked&source=bl&ots=XfcTsOc2pG&sig=EPaHG95M8uXiI5WAAwpa-e3zHXE&hl=en&sa=X&ved=0ahUKEwijtKSl2IfaAhUNy1kKHdBUAdsQ6AEIlwEwCg#v=onepage&q=is%20heatmap%20trademarked&f=false
I am now working on a drawing app, and users can select their customerized colors.
Then the customerized color button would then be showing the chosen color. However, it become squared-corner once it was set to be the customerized color.
My coding is as follows:
My question is :
How to set the customBtn such that it is round-corner and filled with customerized color (best to be programmatically as the customerized color will be changed upon user's choice)
actually what are those 10 for the new float[] array represent?
Many thanks!!
Coding:
customColorCode = Color.argb(alphaSeekBar.getProgress(), redSeekBar.getProgress(),
greenSeekBar.getProgress(), blueSeekBar.getProgress());
RoundRectShape rs = new RoundRectShape(new float[] { 10, 10, 10, 10, 10, 10, 10, 10}, null, null);
ShapeDrawable sd = new ShapeDrawable(rs);
customBtn.setBackgroundDrawable(sd);
customBtn.setBackgroundColor(customColorCode);
change background color to set the different colors to button
public RoundRectShape (float[] outerRadii, RectF inset, float[] innerRadii)
Added in API level 1
RoundRectShape constructor. Specifies an outer (round)rect and an optional inner (round)rect.
Parameters
outerRadii An array of 8 radius values, for the outer roundrect. The first two floats are for the top-left corner (remaining pairs correspond clockwise). For no rounded corners on the outer rectangle, pass null.
inset A RectF that specifies the distance from the inner rect to each side of the outer rect. For no inner, pass null.
innerRadii An array of 8 radius values, for the inner roundrect. The first two floats are for the top-left corner (remaining pairs correspond clockwise). For no rounded corners on the inner rectangle, pass null. If inset parameter is null, this parameter is ignored.
I am creating bitmap, next i am drawing second solid color bitmap on top of it.
And now i want to change first bitmap, so solid color that i drawed on it will be transparent.
Or simply, i want to remove all pixels of one color from bitmap.
I havie tried every colorfilter, and xfermode with no luck, is there any other possibility to remove color other that doing it pixel by pixel?
This works for removing a certain color from a bitmap. The main part is the use of AvoidXfermode. It should also work if trying to change one color to another color.
I should add that this answers the question title of removing a color from a bitmap. The specific question is probably better solved using PorterDuff Xfermode like the OP said.
// start with a Bitmap bmp
// make a mutable copy and a canvas from this mutable bitmap
Bitmap mb = bmp.copy(Bitmap.Config.ARGB_8888, true);
Canvas c = new Canvas(mb);
// get the int for the colour which needs to be removed
Paint p = new Paint();
p.setARGB(255, 255, 0, 0); // ARGB for the color, in this case red
int removeColor = p.getColor(); // store this color's int for later use
// Next, set the alpha of the paint to transparent so the color can be removed.
// This could also be non-transparent and be used to turn one color into another color
p.setAlpha(0);
// then, set the Xfermode of the pain to AvoidXfermode
// removeColor is the color that will be replaced with the pain't color
// 0 is the tolerance (in this case, only the color to be removed is targetted)
// Mode.TARGET means pixels with color the same as removeColor are drawn on
p.setXfermode(new AvoidXfermode(removeColor, 0, AvoidXfermode.Mode.TARGET));
// draw transparent on the "brown" pixels
c.drawPaint(p);
// mb should now have transparent pixels where they were red before
user487252's solution works like a charm up until API level 16 (Jelly Bean), after which AvoidXfermode does not seem to work at all.
In my particular use case, I have rendered a page of a PDF (via APV PDFView) into a pixel array int[] that I am going to pass into Bitmap.createBitmap( int[], int, int, Bitmap.Config ). This page contains line art drawn onto a white background, and I need to remove the background while preserving the anti-aliasing.
I couldn't find a Porter-Duff mode that did exactly what I wanted, so I ended up buckling and iterating through the pixels and transforming them one by one. The result was surprisingly simple and performant:
int [] pixels = ...;
for( int i = 0; i < pixels.length; i++ ) {
// Invert the red channel as an alpha bitmask for the desired color.
pixels[i] = ~( pixels[i] << 8 & 0xFF000000 ) & Color.BLACK;
}
Bitmap bitmap = Bitmap.createBitmap( pixels, width, height, Bitmap.Config.ARGB_8888 );
This is perfect for drawing line art, since any color can be used for the lines without losing the anti-aliasing. I'm using the red channel here, but you can use green by shifting 16 bits instead of 8, or blue by shifting 24.
Pixel by pixel is not a bad option. Just don't call setPixel inside your loop. Fill an array of argb ints with getPixels, modify it in place if you don't need to preserve the original, and then call setPixels at the end. You can do this row-by-row if memory is a concern, or you can just do the whole thing in one shot. You don't need to fill a whole bitmap for your overlay color since you'd just be doing a simple replace (if current pixel is color1, set to color2).