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

Fix heat transfer between overlapping heat sources. #90

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Woracheth
Copy link

Blending function of CSS shadow is not averaging as blurring applied but it is addition, so it creates exaggerated heat at overlapping area between two heat sources, even exceed the center of a heat source.

Heat transfer between sources should be gradually blend like normal color, RGB, then I made it on G channel and used Math.min() to make sure the edge still feathering to transparent.

bangkok

Blending function of CSS shadow is not averaging as blurring applied but
addition, so it creates exaggerated heat at overlapping area between two
heat sources, even exceed the center of a heat source. Heat transfer
between sources should be gradually blend like normal color, RGB, then I
made it on G channel and used Math.min to make sure the edge still
feathering.
@wshaver
Copy link

wshaver commented Jun 4, 2014

The amount of bleedover should really be configurable.

@Woracheth
Copy link
Author

I just made it on current code base. It should somehow solve the issue #186 and #223 .

To use it, add "absolute" property to configuration when creating HeatmapOverlay object
var heatmap = new HeatmapOverlay(map, { min:0, max:10, absolute:true });
This mode is not activated by default, and it might exhibit unexpectedly when using with useLocalExtrema: true.

I would call it "absolute mode" because it is intended for plotting absolute measured value like scatterplot. The points themselves are not actually heat sources but just heat sampling, so the more point existence in the same area won't increase heat density. Overlapping areas are just casual interpolation effect on top of plain scatterplot, not heat accumulation, so the overlapping area shouldn't have greater intensity than one of its measured points.

The technique I did was to separate face filling rendering on Red channel and silhouette edge rendering on Alpha channel.

Calculating the overlapping face filling area using ctx.globalCompositeOperation = 'lighten' can retain actual greater value no matter how many times it was overlapped. The trick was to lighten composite using fully opaque color (unless it would use minimum value at the feathering edge), so it needs separated templates for each intensity value. Although it is not true interpolation, I would leave it as maximum biased.

The silhouette edge rendering remain the same, and may still be replaced by useGradientOpacity: true . I just renamed variables from shadowXxx to edgeXxx to be consistent with faceXxx .

I have to mention that this library is only the one I have seen so far that can work on absolute values rather than relative fashion. I mean the case when points aren't overlapped, the color tone depends on the actual provided value, unlike others (e.g. Google's Heatmap Layer) those will always visualize both extreme red and green for remaining points on current viewport regardless of global value range.

@adam144dev
Copy link

adam144dev commented Jun 5, 2018

Hi,
I've just downloaded versin from this branch and while using abosolute=true have maximum not average as on screenshoot.

const cfg = {
absolute:true,
radius: .01,
minOpacity: .5,
maxOpacity: .5,
blur: 0,
...

heatmap-absolute-max

@Woracheth
Copy link
Author

Hi @adam144dev ,

The screenshot above was captured from 2013 version, the zero blur option was not available then. I had modified internal constant just to capture and show the cause. The color transition was intended to come from blurring radius.

The color transition effect on overlapping area that you see above was side effect by accident but it looks desirable. Unfortunately, was not true averaging.

The correct average calculation is like

[8 4 2 1] --> 3.75

but what the code does was just combining intermediate result color with next point color

[[[8 4] 2] 1] --> [[6 2] 1] --> [4 1] --> 2.5

[[[1 2] 4] 8] --> [[1.5 4] 8] --> [2.75 8] --> 5.375

This example shows ratio of 50% of intermediate result and 50% of next point, which the final value tend to bias to the latest point as the ratio always weights new data point equally to whole previous aggregation. The actual 2013 code even changes ratio using opacity left from every next point.

It has to be this way because I still cannot calculate true average from stream of data points on uint8 html5 canvas buffer.
You might see that even orange and red points at the center in my first screenshot was covered by each other in alternately, because of random data point order.

My April 2018 version was reimplemented on current v2 code base which is using different technique, but still on html5 canvas.
The current code base relies on html5 canvas compositing operations. Calculating pixel by pixel in separated number array is not feasible, so calculating true average still not possible, unless we have to draw circles from scratch.
I decided to use maximum value on overlapping area because its behavior is easier to predict, so the side effect was not carried.

I understand that your application would not use blurring option because your data has ordinal nature but this library has interval nature with unknown as zero, so it will result in green(very good, next to unknown) around red(hazardous) which is misleading.

You may use this patch on current version for the side effect like in the 2013 screenshot with these assumptions in mind,

  • this is workaround to mimic 2013 version, not true averaging
  • no more than 2 point on overlapping area, so you can use 50:50 average weight
  • if you don't use scaleRadius:true option, when you zoom out, all points will eventually overlap
  • unless it is acceptable to use biased average to the latest(?) data point. (I can't guarantee that the order will always remain stable, e.g. it might be shuffled via gmaps plugin)

You may also change globalAlpha to 0.33 to weight 67% to intermediate result and 33% to every next point.

@ErikaHD
Copy link

ErikaHD commented Jul 20, 2018

Hi Woracheth,
I downloaded this version and I added it to my leaflet project but I haven't seen any difference at all (i used patrick's version before).
Does this fix work with leaflet or do i need to install something else?

I just feel that I am missing something.
Thank you

This is how the heat map looks:
screen shot 2018-07-20 at 13 02 51

@Woracheth
Copy link
Author

Hi @ErikaHD ,

Sorry if you downloaded from Woracheth/heatmap.js/build/heatmap.min.js.
It is not updated there because mine is still not official build and I want to avoid further merge conflicts.
You need to checkout the repository and build via grunt.
BTW, for your convenience, you can just use this file instead.

When you call heatmap via leaflet plugin, you must also pass absolute: true in the config object.

I didn't test on leaflet plugin, but it should work because I modified on the core heatmap, not particular plugin.

I'm not sure whether my version would solve exactly your problem. Your screenshot doesn't seem that you have exaggerated heat between points, unlike my initial problem.

@ErikaHD
Copy link

ErikaHD commented Jul 20, 2018

Hi @Woracheth,
I increased the radius level in the configuration and your file is working well. It is just doing what I need.
Thank you so much for your help. Your approach has helped me so much.

The heatmap is looking way better :-)

screen shot 2018-07-24 at 15 13 14

@levybeedev
Copy link

Great job @Woracheth

We are using the heatmap with around 70.000 points and after applying your changes in Mozilla FF and Edge we have issues with the performance (browser took lot of time to render the heatmap) , however in Chrome is working quite well. Do you have any idea what we can try to solve the performance issues?
Other than that the changes are doing the job ;)

Thanks

@MrTob
Copy link

MrTob commented Mar 8, 2020

Hi,
I've just downloaded versin from this branch and while using abosolute=true have maximum not average as on screenshoot.

const cfg = {
absolute:true,
radius: .01,
minOpacity: .5,
maxOpacity: .5,
blur: 0,
...

heatmap-absolute-max

Hey :D do you have the source code for me? i also need exactly the same thing

@Woracheth
Copy link
Author

@MrTob on my fork

@JeromeRioux
Copy link

JeromeRioux commented Jun 10, 2020

Hello @Woracheth

Would it be possible to do this for the rest of the library (not only for leaflet pluggin)? I cloned the branch and tried absolute: true in my code but it did not worked.

            heatmapInstanceRef.current = h337.create({
                // configs 
                absolute: true
            });

@prasanjitdash
Copy link

Hi @Woracheth,
I increased the radius level in the configuration and your file is working well. It is just doing what I need.
Thank you so much for your help. Your approach has helped me so much.

The heatmap is looking way better :-)

screen shot 2018-07-24 at 15 13 14

Hi @ErikaHD, do you have a demo of your display somewhere?

The temperature field looks good visually. I'm having some patchy display but that may be because my data have a log-normal distribution. Comparing with another continuous field, such as yours, would be useful. Thanks

I'm also using Leaflet v1.40, heatmap.js leaflet-heatmap.js - unsure if the latter two are the latest available version.

Thanks

demo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants