📜 ⬆️ ⬇️

Leaflet - API maps from Cloudmade. Review

To Mourner - be afraid of your desires, they can come true . Joke.

Start over



On the main Leaflet API we are met with a quickstart example. From him and begin.
')
// create a CloudMade tile layer var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/YOUR-API-KEY/997/256/{z}/{x}/{y}.png', cloudmadeAttribution = 'Map data © 2011 OpenStreetMap contributors, Imagery © 2011 CloudMade', cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttribution}); // initialize the map on the "map" div var map = new L.Map('map'); // set the map view to a given center and zoom and add the CloudMade layer map.setView(new L.LatLng(51.505, -0.09), 13).addLayer(cloudmade); // create a marker in the given location and add it to the map var marker = new L.Marker(new L.LatLng(51.5, -0.09)); map.addLayer(marker); // attach a given HTML content to the marker and immediately open it marker.bindPopup("A pretty CSS3 popup.<br />Easily customizable.").openPopup(); 


The example begins with creating a layer with tiles from cloudmade. API itself seems to be also “by cloudmade”. Attention, the question is: what, for the parent / friendly project you can not make a convenient way to add a layer of tiles? Type of such:

 var cloudmade = new L.CloudMade.TileLayer(YOUR-API-KEY); 
?
Or even this:
 map.addLayer('cloudmade', { apiKey: YOUR-API-KEY }); 


I don’t know what kind of relationship Leaflet and Cloudmade have, but making a client comfortable with Cloudmade is definitely not the last task of the Leaflet API. Forcing a user to add their own copyright Cloudmade - this is some kind of common sense violence.

Tea





Unlike the Google Maps API v2 / OpenLayers, Leaflet presents a jQuery-like paradigm (most actions are done by tea). Well, so what shy something?

 var marker, map = (new L.Map('map')) .setView(new L.LatLng(51.505, -0.09), 13) .addLayer('cloudmade', { apiKey: YOUR-API-KEY }) .addLayer((marker = new L.Marker(new L.LatLng(51.5, -0.09))) .bindPopup("A pretty CSS3 popup.<br />Easily customizable.") ); marker.openPopup(); 


By the way, from the given example, the inconvenience of mixing the object approach and teaping is well noticeable - (new X ()). Y () is not the most beautiful construction in JS.

By the way, why are markers added via addLayer? L.Layer in Leaflet is a completely understandable separate entity - a layer. Why are other entities added to addLayer - markers, geometries?

And then, if there is no difference between addLayer and addMarker (i.e., the entire logic of adding is sewn into the object itself), isn't it logical to duplicate this method into the object itself?

 var map = (new L.Map('map')) .setView(new L.LatLng(51.505, -0.09), 13) .addLayer('cloudmade', { apiKey: YOUR-API-KEY }), marker = (new L.Marker(new L.LatLng(51.5, -0.09))) .appendTo(map) .bindPopup("A pretty CSS3 popup.<br />Easily customizable.") .openPopup(); 


Now, if you replace the constructors with factories, it will turn out quite nicely:

 var map = L.map('map') .setView(L.latLng(51.505, -0.09), 13)) .addLayer('cloudmade', { apiKey: YOUR-API-KEY }), marker = L.marker(L.latLng(51.5, -0.09)) .appendTo(map) .bindPopup("A pretty CSS3 popup.<br />Easily customizable.") .openPopup(); 


Compare with the original example.

By the way, looking ahead, setView and addLayers can be done directly in the constructor - why not bring this opportunity straight into the quickstart?

Pack your hands with unwashed hands.



OK, convinced I want to use your IPA. I look at the code and ... I wonder. How to connect it? Well, I understand - why overload the example with all sorts of html tags, etc., but how to connect the API should be shown. Put a tick, go to the example .

Before writing the following steps:
Include Leaflet CSS files in the head section of your document:
 <link rel="stylesheet" href="leaflet/leaflet.css" /> <!--[if lte IE 8]><link rel="stylesheet" href="leaflet/leaflet.ie.css" /><![endif]--> 

Include Leaflet javascript file somewhere tag (preferably before body close tag):
 <script src="leaflet/leaflet.js"></script> 

It is defined as the width and height:
 <div id="map" style="height: 200px"></div> <!-- width equals available horizontal space by default --> 

Something with it.


Up to this point (I have already read the dock before crawling into examples), Leaflet left an extremely pleasant impression of himself. But this small piece of example raises more questions than all the documentation combined.

First, where does the leaflet folder with the leaflet code come from on my page? It seems that this is exactly how the receipt of the library code should be described in the “Preparing your page” section. If you think that webmasters will easily notice that the links do not lead anywhere, climb on github and download the code - you are very deeply mistaken. I am sure that your support is overwhelmed with the questions “I copied the code - it doesn't work - what to do ???”

Further, why force webmasters to place the css connection code themselves? Why not leave this job js-script? (By the way, graceful degradation under IE is my homage.)

But this comment “width equals available horizontal space by default” - who is it designed for? For those who have no idea about HTML? Well, this comment only confuses them. They would just explain how (a) where leaflet / leaflet.js will come from on their page and how to download the code from the github, (b) what are the magic comments in the CSS connection and why they cannot be simply deleted.

Make sure you’re including a map and a leaflet.js inclusion, or in a window.load or document.ready event handler.


You explain to webmasters that the divas are pulled by default at 100%, but at the same time you do not consider it necessary to tell what kind of window.load beast is and how to add some code to its handler?

Understand, webmasters want exactly one thing from you: copy a piece of code and make it work. He worked everywhere - even in his head, at least in his body, at least before window.onload, even after. And how I fully understand them - if I suddenly need to use a third-party library, the last thing I want to do is pick the reference example, figuring out why it does not work in my environment.

Okay, I'm distracted. We look in firebug. You guys are cheaters! JS API in 25 KB is some kind of witchcraft. There is a firm and unconditional five. By the way, your _leaflet_resize3 function is lit in global namespace (v0.3), somewhere you have forgotten var.

It is surprising more - why don't you host this library yourself and force you to connect from the user’s domain? Not well, what is the load, and it is possible to come to an agreement with a partner. But your users will not have problems with updating versions (and with critical bugs in older versions that will appear sooner or later) + with the distribution of the library, it will very soon be in the majority of users in the cache.

Dessert



All right, we finish with examples, we pass to tasty - to documentation . And immediately the question - why some links are gray and do not lead anywhere? Documentation not ready? Has something broken? Roadmap? By the way, which version of this documentation is stable to 0.2 or dev 0.3?

Begin to read.

 // initialize the map on the "map" div with a given center and zoom var map = new L.Map('map', { center: new L.LatLng(51.505, -0.09), zoom: 13 }); // create a CloudMade tile layer var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/YOUR-API-KEY/997/256/{z}/{x}/{y}.png', cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18}); // add the CloudMade layer to the map map.addLayer(cloudmade); 


It turns out that the center and zoom map can be set in the designer. And you can not ask. And what will happen if only the center is set? Or just zoom? And most importantly, who needs an uninitialized map? What is its functionality?

Again, the creation of the cloud-made layer is brought to the very beginning - yes, screw the normal way to do it! By the way, now copyrights can not be specified?

We start looking at the map options.
layers ILayer [] [] Layers will be added to the map.

It turns out that the layers can be set right in the designer. So what don't you ask in your examples?
minZoom Number 0 Minimum zoom level of the map. Overrides any minZoom set on map layers.
maxZoom Number 18 Maximum zoom level of the map. This overrides any maxZoom set on map layers.

Um If the map options always overlap the layer options and they have a default value - then why do we need layer options? Unclear…
dragging Boolean true whether it is a map be draggable with mouse / touch or not.
touchZoom Boolean zoomed by touch-zooming with two fingers.
scrollWheelZoom Boolean
doubleClickZoom Boolean can be zoomed by double clicking on it.

Three options out of four - infinitives, one - gerund. Then either drag, or (touch | scrollWheel | doubleClick) Zooming.

More interesting is another thing - why ILayers are awarded a separate option-array of layers, but IHandlers and IControls are not. Isn't that more logical?

 handlers IHandler[] ['drag', 'touchZoom', 'scrollWheel', 'doucleClick'] Map handlers that will be enabled initially 


Well and for kontrolov similarly.

The same consideration applies to the properties of the card. Why not just write:
 map.hadlers('drag').enable() 


Such a solution (a) unloads the class interface from a variety of unnecessary options and properties, (b) makes adding new controls and handlers more formal and convenient.

Suppose I want to add a ruler to my map and I want to arrange it correctly in the form of IHandler. I will have to follow L.Map and do something like this:

 var MyMap = function (id, options) { L.Map.call(this, id, options); this.ruler = new MyRulerHandlerClass(map); if (options && options.ruler) { this.ruler.enable(); } } 


Although it's enough for me to do something like
 L.handlers.register('ruler', MyRulerHandlerClass) 

if I start a static storage of IHandler cards by alias.

We turn to the events.

click MouseEvent Fired when the user clicks (or taps) the map.
dblclick MouseEvent Fired when the user double-clicks (or double-taps) the map.
moussedown mouse button on the map.

And mouseup, contextmenu, mouseenter, mouseleave? It is somehow quite strange to provide the mousedown event and not let listen to the mouseup.

load event fired when the map is initialized (for its first time).
it needs to be the redrawn content of its content (this usually happens to be a map or a zoom). Very useful for creating custom overlays.


Remove the uninitialized state of the card, and there will be no need for these two events. By the way, the load event, which says that all tiles were loaded, would be much more useful.

movesart event fired
move event
event of dragging the map.
dragstart event fired when the user starts dragging the map.
drag event fired repeatedly while the user drags the map.
dragend event fired when the user stops the map.
zoomend Event Fired when the map zoom changes.


What caused the need to have two sets of move-events - move * and drag *? To distinguish user action from software? So why the zoom is also not done? Why there is no zoomstart, zoom, scrollzoomstart, scrollzoom, scrollzoomend? Unclear.

layeradd LayerEvent Fired when a layer is added to the map.
layerremove LayerEvent fired when some layer is removed from the map.


I understand that these same events are thrown when adding markers? How should I know about this? The concept of layer has not yet been entered, the word is not even distinguished by a font as a program entity, there is no link.

locationfound LocationEvent Fired when geolocation (using locate or locateAndSetView method) went successfully.
ErrorEvent locationerror Fired when geolocation (using locate or locateAndSetView method) failed.


And where can I see these magical methods in question? After the events in the description of some projections go, there is no link. By the way, what the projections do in the description of the map interface is completely incomprehensible to me.

Map panes

An object literal that you can use. The difference is mostly in zIndex order that such overlays get.


Cool. What is it? How to access this? This is not a field, not a method and not an event - what kind of animal is this? Example no.

Go to the methods. Dividing methods into methods / Other methods (“all animals are divided into a) belonging to the Emperor, b) embalmed, c) tamed, d) dairy pigs, e) sirens, e) fabulous, g) stray dogs, h) included in this classification, i) running like crazy, k) uncountable, l) painted with a very thin brush made of camel hair, m) and others, n) who have just broken a jug , o) similar to the distance to the flies ... ") as if hinting at the main problem with the methods: there are too many of them. And, most importantly, there will be even more. If you put out “sometimes useful” methods in the interface of the main class - very soon they will become totally unuseful because of the impossibility of finding something. It is necessary to do something :) For example, if the method is a proxy to some internal object, then is it not worth opening this object instead of proxying its methods? Well, many methods are simply redundant - for example, why do we need separate methods locate and locateAndSetView, if you can just get away with the setView flag in the options of the locate method? Why do we need zoomIn / zoomOut methods when there is a setZoom?

In the methods (add | remove) Layer again there is not a single hint that such a layer is. It is curious that there are methods (add | remove) Control, but not (add | remove) Handler. And what happens if I add a new control - the .controlName field will appear in the map? Apparently - no.

In total, you already have 4 inconsistent ways to add / delete / access map entities:

1) for layers: an array of layers in options, (add | remove) Layer in methods;
2) for IHandler: a set of named options, a set of named properties, how to add new ones is not at all clear;
3) for IControl: both a set of named options / properties, and an interface (add | remove) Control for unnamed controls;
4) for MapPanes, a map method that returns a literal with a list of panees.

It will be too much, and the documentation is lame.

Markers



The marker interface is poor in comparison with the card riot, but manages to inherit its birth injury in the form of .dragging. The inability to re-set the icon is not good, but the lack of native ability to associate any data with the marker is even more unpleasant. The marker corresponds to some geographical entity, and, most likely, it has some kind of identifier, name, description, address, etc. You force the user to either inherit and expand the class, or cheat with closures, or spit on everything and just write marker.id = 'myid'. All options are not very beautiful and potentially dangerous.

Click here to see the correct map.


The wrong solution is to call the clickable object interactivity.

What should I do if I want to decide on a click - to skip this event or not? There is no access to the house node.

What should I do if I want a balun to open at the click, and a card to drag along the dredge? It is quite usual use case, see, for example, public transport stops at guglomaps.

If you are a draggable with a mouse / touch or not.


I understand that the .dragging field of the marker will be anyway, regardless of the draggable option? And what happens if I force a call to .dragging.enable ()? Why does the card have this option called dragging? After all, this is exactly the same IHandler for dragging, like the card - why does it have other options?

Where did the mousedown event go?

By the way, about the fact that the marker - this layer, I never found out. Only examples still hint to me this strange identity. The documentation also uses the term “overlay”, which is also not defined anywhere, plus it is not found in entity names either.

Popup



The first question that arises is if there is an L.Popup entity, can I pass an instance of the L.Popup class to bindPopup? According to the documentation it turns out that no. And why then the method is called bindPopup, if it binds no popup, but only data and options for it?

autoPan Boolean
closeButton Boolean true control of the popups.

Diverse options names. If autoPan = automatically move, then closeButton = close the button, and not at all the “presence of the close button”.

By the way, what is the point of setting the html content if there is no access to the DOM? Since I want to set a rich html content balun, then I definitely want to add an interactive one - but I don’t have access to either html or the popup opened event.

There are no interactivity settings for the popup - but in vain, they are more necessary here than in the marker. For example, a scroll always always goes to the map, which means it is impossible to place the scrolled content in a popup.

Layers



About pechalku with cloudmade layers, I have already mentioned. For some reason, for other services (WMS), a separate class is set up for convenience, but not for the first one.

What minZoom and maxZoom are and how the map settings are overrived is not clarified. It is also not clear why the “tile size” setting was entered. Typical use cases - to add a layer in another projection (wgs84, for example), stretch the tiles of the last scale and add a +1 or +2 scale to the map - this setting does not solve.

About the usefulness of the event "all the tiles are loaded," I have already mentioned.

What is TileLayer.Canvas I just do not understand. The documentation describes exactly one method for him, and he does not give an example of why he is needed.

Graphics



Graphic elements inherit the same print with clickable as markers. By the way, why is draggable not bolted to them? The number of events thrown by the object as everything goes deeper into the documentation is reduced :)

The setStyle method specifying options is strange.

Geometries are defined by arrays of points — somehow inconsistently. The point itself must be set via new LatLng, and in the polyline it is possible to thrust the geometry with an ordinary array, and not special. object. And what will happen if the user makes this splice array bypassing the API method? There is no update method for graphics.

The noClip option with the comment “Disabled polyline clipping.” Baffled me. Especially the lack of the same option in other geometries.

What do I know about circles?



A circle drawn by linecap on the line is a cool idea. The only problem is that an “honest” circle with a radius of n meters in the Mercator projection is not a circle at all or even an ellipse, but a complex figure. Okay, we put an "honest" circle nobody needs. But still: what radius is chosen for calculations? To the north, to the south, to the west, to the east?

By the way, the position of the center of the circle can be changed, but the radius is impossible. Why?

L.CircleMarker
A circle of a radius specified in pixels. Extends Circe (here is a typo - forgotten) . Use Map # addLayer to add it to the map.


At emphasis, I do not see differences from just Circle. Under the name “CircleMarker” I would think that this is a circle + a marker, but the description does not give any leads.

The radius is 10 and can be altered by passing a “radius” member in the path options object.


Why is this done? Who interfered with the radius in the signature? Or is this a way to make radius change available? Why then just need a Circle? In an amicable way, it’s not necessary to expand CircleMarker, and Options -> CircleOptions, since an additional field appears in the options.

Groups



A LayerGroup group in which you can put not only layers, but also markers / graphics is very strange. Link ILayer still does not lead anywhere. The clearLayers method, which removes all the children of the group (and does not clear the tile layers, as I personally would think from the name) is also very strange.

But the FeatureGroup group, which adds a promotion of events to the LayerGroup and a popup, is already quite strange at all. What should someone say the name of Feature? Why this functionality can not be nailed to the base group and not to do the separation of the Layer / Feature?

Geojson



Firstly, the decision itself of the “cumulative” GeoJSON looks strange. Or two times addGeoJSON can not be called? Documentation does not give an answer.

 var geojson = new L.GeoJSON(); geojson.on('featureparse', function(e) { // do something with e.layer depending on e.properties }); geojson.addGeoJSON(geojsonObj); map.addLayer(geojson); 


The most interesting and hidden behind the "do something". And what can you do? The group does not give accessors to child objects. If I added a FeatureCollection via geojson - what can I do with it? Or FeatureCollection cannot be added, then cumulative addGeoJSON?

And by the way, why are JSON everywhere big, and in geojson small? And you have a typo: coordsToLatlng and coordsToLatlngs should be “Lng” with a big one.

Eventually



What do we have in the end?

The disadvantages of the functional, suppose, can be easily attributed to the small size of the library (25Kb is a record). (But what is there to save, if the library weighs less than one standard tile?)

Non-consistent interfaces for adding different entities to the map — I suppose, growing pains. I think, after all, they need to be reduced to a release in a single interface.

Graphically and aesthetically, the library (and the Claudmade substrate) leaves a very pleasant impression, and for this much will be forgiven to it. Well, the author is hopeful of inconsistency and irrationality, I hope that he will refine the file, since the version is still only 0.3, you can afford to tear backward compatibility.

Source: https://habr.com/ru/post/137010/


All Articles