On Tue, May 13, 2014 at 3:36 AM, Ümit Seren <[email protected]> wrote:

> Hi everyone,
>
> WARNING: long email.
>
> tl;tr version:
> I ported the Markerclusterer-Plus library 
> (<http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/examples/advanced_example.html>
> http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/examples/advanced_example.html)
> for google maps to Polymer.
> The source can be found here:
> <https://github.com/timeu/google-map-markerclusterer>
> https://github.com/timeu/google-map-markerclusterer
> The component page + demo can be found here:
> <http://timeu.github.io/google-map-markerclusterer/components/google-map-markerclusterer/>
> http://timeu.github.io/google-map-markerclusterer/components/google-map-markerclusterer/
>
> *Background:*
> I wanted to play around with polymer and web-components for some time now
> but there was always other work I had to do.
> Recently I had to work on a visualization to display quantitive values on
> a pie chart on a google-map (output of a clustering analysis).
>  <http://jsfiddle.net/timeu/kxMRQ/embedded/result/>
> http://jsfiddle.net/timeu/kxMRQ/embedded/result/
> The problem was, that we had data for 1000 markers and displaying that
> much pie charts was really slow.
> So after some research I stumbled over Markerclusterer-Plus and after some
> tweaking it worked fine 
> (Demo:<http://jsfiddle.net/timeu/gr6qJ/embedded/result/>
> http://jsfiddle.net/timeu/gr6qJ/embedded/result/)
>
> However we wanted to replace the default cluster icons with custom ones
> (cluster icon should be a pie chart with an average of all the embedded pie
> charts). Markerclusterer-Plus didn’t provide a way to specify custom
> cluster icons so I was planning to extend the library and I thought instead
> of just extending the library I could also port it to Polymer.
>
> This is my first polymer-element and although I tried to stick the
> official Polymer docs and best practices as closely as possible the code
> might not be perfect.
>
> In the course of developing this Polymer element I ran into some questions
> and issues. Maybe some Polymer Devs could provide some feedback and maybe
> comment if I chose the right approaches:
>
> *Basic structure:*
> I decided to not just write a simple wrapper for the markerclusterer-plus
> js library. Instead I tried to map the markerclusterer-plus classes to
> corresponding polymer-elements.
> The google-map-markerclusterer consists of 5 polymer-elements:
>
>    - <google-map-markerclsuterer>: This is the main element that the user
>    should use. It depends on the google-apis and google-map elements.
>    - <google-map-clustericon>: This is an internal polymer-element that
>    handles some of the clustering functionality.
>    - <google-map-defaulticon>: This is an internal polymer-element that
>    displays the default cluster icon
>    - <google-map-overlayview-marker>: This is an element that allows to
>    use any overlay as a marker.
>    - <google-map-overlayview>: This is a polymer-element that wraps an
>    OnverlayView of google maps (similar to the <google-map-marker>).
>    <google-map-markerclusterer>, <google-map-clustericon> and
>    <google-map-overlayview-marker> inherit from it.
>
> The original Markerclusterer-Plus library only supported simples markers
> but the polymer version should also support custom markers that are
> implemented via an OverlayView.
>
> Here are some things I couldn’t get my head around:
>
> *1. To embed or not to embed the element inside of <google-map> ?*
>
> <google-map-markerclusterer> depends on the <google-map> element and
> specifically on the map attribute.
> Currently the <google-map> element defines a content insertion point for
> markers so that you can write
>
> <google-map><google-map-marker></google-map>
>
> I could now extend google-map and change the template to allow to embed my
> <google-map-markerclusterer> but I wanted to avoid using inheritence too
> much. AFAIK best practice is to use composition instead of inheritence.
> So I decided to put the <google-map-markerclusterer> as sibling to a
> <google-map>. Inside a parent polymer-element I could use data-binding to
> bind the map attribute (like I did in the demo):
>
> <polymer-element name="my-app">
>   <template>
>     <google-map map="{{map}}" />
>      <google-map-markerclusterer map="{{map}}" />
>   <template>
> </polymer-element>
>
> *2.) Best practice when dealing with multiple elements that depend on a
> third party library ?*
>
> google-map-markerclusterer as well as google-map depend on the google-api.
> Therefore I put the <google-api> element into the template of
> <google-map-markerclusterer> to make sure that google-api is loaded. I
> also have to wait for the googleApiLoaded event to be fired to initialize
> the OverlayView.
>
> However google-map-markercluster can pratically only be used together with
> the <google-map> element. So does it really make sense to add the
> dependency to google-api to google-map-markerclusterer? I could use the
> approach to send events to sibling elements (described here:
> <http://www.polymer-project.org/articles/communication.html#events>
> http://www.polymer-project.org/articles/communication.html#events) but
> that puts the burdon on the client of my element to make sure that the
> event is propogated to the google-map-markerclusterer.
>
> Furthermore the base class of google-map-markerclusterer -
> google-map-overlayview - also depends on the the google-map-api to be
> loaded. Should I put the gogole-api element in both classes or is it enough
> to put it only intot he google-map-overlayview and use event bubling to
> handle the event in google-map-markerclusterer ?
> In general if some internal elements depend on the external 3rd party
> library should there templates contain it or not ?
>

Yes. If the element depends on that functionality to be useful, it should
include it.
 The loader elements in google-apis only load the library once. That means
if you have
multiple instances of <google-maps-api>, it will only load the maps library
once.

>  *3.) How to programatically add child elements to an element?*
>
> Currently the google-map-markerclusterer defines a published markers
> attribute that has a Changewatcher attached (markersChanged()) which
> generates the clusters and adds them to the map. I create an array of
> markers or overlayview elements and then set the markers attribute.
>
> However that doesn’t seem completely right to me because a marker is not a
> domain model but a custom/polymer-element. For example <google-map> uses
> a content insertion point for gogole-map-markers and a MutationObserver to
> handle the changes to the LightDOM.
> Should I take the same approach? My feeling says yes, but with
> google-map-markerclusterer I don’t add one or two markers but potentially
> thousands. I could make a loop over my marker elements array and add them
> one by one to the LightDOM. Will my MutationObserver callback be executed
> on each iteration? That would kill performance (I could probably use
> this.async to delay the costly clustering function call).
>
> Another alternative might be to use the repeat-binding together with a
> content insertion point inside of the template to add multiple markers (is
> that actually possible)?
>
Yes. But it might be just as easy to expose a .markers property on your
element and have folks set the array of marker data in JS. Inside the
element, you can use a template create to stamp out the necessarily markup.

> What is common best practice for dynamically adding polymer-elements to
> other polymer-elements ? Should I allow both cases (attribute and LightDOM)
> or only one ? How to deal with adding multiple elements at once ?
>
 *4.) How to deal with configurable child elements ?*
>
> The default behavior of google-map-markerclusterer is to use the
> google-map-defaulticon element to display the cluster icon.
> However I wanted to give the user the possibility to change this to a
> custom icon.
> So I decided to add an insertion point and a default icon to the template.
>
> <polymer-element name="google-map-markerclusterer">
>   <template>
>     <google-maps-api></google-maps-api>
>     <google-map-clustericon id="defaultclustericon"><google-map-defaulticon 
> defaultStyles="{{styles}}" /></google-map-clustericon>
>     <content id="clustericontemplate" 
> select="google-map-clustericon"></content>
>     <!-- markers is not used currently -->
>     <content id="markers"></content>
>   </template>
> </polymer-element>
>
> And in the attached function I do following:
>
> attached: function() {
>   var clusterIconTemplateContent = 
> this.$.clustericontemplate.getDistributedNodes();
>   if (clusterIconTemplateContent.length > 0) {
>     this.clusterIconTemplate_ = clusterIconTemplateContent[0];
>   }
>   else {
>     this.clusterIconTemplate_ = this.$.defaultclustericon;
>   }
> },
>
> This way if I only define the google-map-markerclusterer element it uses
> the defaultclustericon element in template but alternatively I can do
> something like this:
>
> <google-map-markerclusterer>
>     <google-map-clustericon>
>         <my-custom-clustericon></my-custom-clustericon>
>     </google-map-clustericon>
> </google-map-markerclusterer>
>
> Is that the right approach? And what happens if have two insertion points
> (i.e. markers and a clustericontemplate) ?
>

google-map uses an icon attribute for this:
https://github.com/PolymerLabs/google-map/blob/master/google-map.html#L91:L95

It defaults to the normal pin, but allows users to override with an image.

>  *5.) Issues with microtasks*
>
> The google-map-markerclusterers listens to the *idle* event of
> google-maps which is called whenever the map becomes idle after *zooming*or
> *panning*.
> When the even is fired the google-map-markerclusterer will re-run the
> clustering, remove existing clusters and add the new clusters. To remove a
> marker or overlayview from a gogole-map, setMap(null) has to be called on
> the overlayview/marker.
> Initially I used the changeWatcher (mapChanged) to handle this.
> However I ran into a weird problem that if I use the mousewheel to quickly
> zoom in and out it can happen that the clusters are not properly removed.
> This is probably due to the fact that the markers are removed from the
> array before the changeWatcher had time to call setMap(null). I tried to
> use this.async and Platform.flush() but nothing helped.
> I ended up creating setters (setMap()) on my elements that directly set
> the map on the overlay/marker instead of using the changeWatchers.
>
> Are there any best practices to cope with that case ?
>
> *Some additional remarks: *
>
> *Data-binding in styles (Polyfill): *
>
> Data-binding inside of styles using the Polyfills doesn’t work (I think
> this is a known issue). Probably this won’t be fixed.
> So far I have to define style changes twice:
>
> :host {
>     position:absolute;
>     left:-{{iconOffset[1]}};
>     top:-{{iconOffset[0]}};
> }
>
> and then in the code to get to work with the polyfills.
>
> this.style.left = "-"+this.iconOffset[1];this.style.top = 
> "-"+this.iconOffset[0];
>
> There was also a weird issue that Chrome somehow didn’t properly display
> one element that was styled, although the styles were correct. When I
> manually unchecked one of the styles of that element in Chrome Dev Tools
> and then checked it again, the element was displayed correctly. As a
> workaround I added just set a constant style programtaically (
> this.$.text.style.position="absolute";) in my code.
> Not sure if this is a bug
>
Correct. If you want to bind to styles, you should do it in a style
attribute on the element.

<span style="color: {{color}}">

> *Binding and insertion points:*
>
>
> <http://stackoverflow.com/questions/23522037/data-binding-between-nested-polymer-elements>
> http://stackoverflow.com/questions/23522037/data-binding-between-nested-polymer-elements
>
> Because I chose the approach to extract a configurable polymer-element
> (clustericon) from the insertion point and add it to
> google-map-markerclusterer I can’t relay on data-binding to pass in some
> settings (gridSize,etc).
> As a workaround I set the attributes programatically (element.gridSize =
> this.gridSize).
> Is there a better way ?
>
> *document.importNode(element,true) doesn’t copy instance variables ?*
>
> Could it be that document.importNode() doesn’t copy the instance
> variables ?
>
> For example in the default setup the google-map-defaulticon is displayed
> with a standard style (defind as a static variables).
> However the user can change pass a different style array to the
> google-map-markerclsuterer. When the user changes the style array it gets
> propogated to the <google-map-defaulticon> (placeholder) that is included
> in the template of <google-map-markerclusterer>. But when I then use
> document.importNode to clone this element the styles variable is null and
> the default style is applied again.
> The current workaround is to use a static variable to solve this.
>
> In general I must say I really enjoyed working with Polymer and I can see
> the huge potential (great work on that note!).
> I hope I could convey the issues, I had clearly enough.
> I also plan to write a blog-post on my experience porting
> markerclusterer-plus to polymer.
>

Awesome! Looking forward to that.

>  cheers
> Uemit
>
> Follow Polymer on Google+: plus.google.com/107187849809354688692
> ---
> You received this message because you are subscribed to the Google Groups
> "Polymer" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/polymer-dev/64c5fa95-90c1-43c1-af15-49323d29e6ae%40googlegroups.com<https://groups.google.com/d/msgid/polymer-dev/64c5fa95-90c1-43c1-af15-49323d29e6ae%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
> For more options, visit https://groups.google.com/d/optout.
>

Follow Polymer on Google+: plus.google.com/107187849809354688692
--- 
You received this message because you are subscribed to the Google Groups 
"Polymer" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/polymer-dev/CACGqRCAaQzQxgfmAODkiydyEJSxQcXJ-T5g3RdmWWQzQKWA_PQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to