📜 ⬆️ ⬇️

API maps from 2GIS: review

Recently 2GIS pleased us all with the release of version 1.0 of our own mapping JS API. API cards of domestic production - a rare thing; Mail.ru and Rambler, for example, were not honored, although they promised ( proof once , proof two ). Let's see what happened in Novosibirsk.

Get acquainted



We will not postpone, open the " Quick Start " section and copy the proposed code. It works! True, the title shows with krakozabrami - it is, however, not surprising, since the tag with the indication of the charset is not. Well, okay, little things in life. We look at the code.
')


//   ,   : var myMap = new DG.Map('myMapId'); //   : myMap.setCenter(new DG.GeoPoint(82.927810142519,55.028936234826)); //   : myMap.setZoom(15); 

Hm Something reminds me vaguely . It is not clear why you cannot set the zoom immediately in setCenter. If you comment out the line “myMap.setZoom (15);”, the map will show the 0th scale. Interesting, and the tiles immediately begin to load? We look into the sniffer - yes, for sure. The browser downloads two sets of tiles, one for the 0th scale, the second for 15. Somehow inaccurately. Looking ahead: you can set the zoom in setCenter, but in the documentation it is not easy to find information about this, ahem.

Okay. We look further.
  //     : myMap.controls.add(new DG.Controls.Zoom()); //  : var myBalloon = new DG.Balloons.Common({ //     : geoPoint: new DG.GeoPoint(82.927810142519,55.028936234826), //   : contentHtml: '!<br>    :)' }); //  : var myMarker = new DG.Markers.Common({ //      (  ,  ,   ): geoPoint: new DG.GeoPoint(82.927810142519,55.028936234826), // ,       : clickCallback: function() { //      : if (! myMap.balloons.getDefaultGroup().contains(myBalloon)) { //    : myMap.balloons.add(myBalloon); } else { //       ,    : //  : myBalloon.show(); } } }); //  : myMap.markers.add(myMarker); }); 


At first glance, it seems nothing. There are some oddities, of course (contentHtml instead of htmlContent, the inability to set context for callbacks). But the code for determining whether a balloon is located on a map shook me:
 if (! myMap.balloons.getDefaultGroup().contains(myBalloon)) 

A simpler somehow can not? Method isOpen balun do, for example?

She has a neon inside



Okay, let's leave quickstart. Let's see what's inside.



1.5 KB preloader - excellent. Code API 200 KB - normal. Timings - so-so. CSS in 955 bytes - wtf?

The most troubling are the script headers with an API. No caching - strange. The date of the last change on January 17th means that the scripts rule without changing the version number, which, at least, is not good. There is no opportunity to fix the revision of the API. For large commercial projects, this is unnecessary and very inconvenient. (By the way, this morning the beta tester of the 2GIS-ovsky IPA flamp.ru card did not open on the morning. Be careful, after all.)

The API in the global scope of the three variables - DG (Dolce & Gabbana? Deutsche Grammophon? Not the best choice for namespace), OpenLayers (surprise!) And $.

We look into the code. And the truth is, we are met by the good old OpenLayers.

The decision is strange. Firstly, OpenLayers, to put it mildly, lagged behind life: in 2012, to work without using transforms & transitions is already somehow a moveton. Secondly, the BSD-license imposes some restrictions (http://trac.osgeo.org/openlayers/browser/license.txt), which, in general, must be respected.

$ appears in the global domain from prototype. At a minimum, carelessly, even though when you turn on a custom jQuery in $, it turns out he (what then always prevents you from removing traces of prototype presence?)

By the way, the layout goes to quirks mode in IE of any version - it is desirable to support QM in the API.

Themselves tayliki small, 20 KB each. Render them, apparently, got from the desktop version.



On the one hand, the substrate is made quite modern for web-cartography - not overloaded with details and not too contrasting, in gray tones a la Google.

On the other hand, working with fonts and sub-pixel anti-aliasing is terrible. In addition, pings to the server are very large.

The coordinate order is long-lat. Interestingly, what kind of projection is spherical like Google’s or elliptical like Yandex’s? Documentation does not give an answer. We try to impose a Yandex hybrid on top - the tiles diverge. So, nevertheless, the projection is spherical. There is not a word about this in the documentation about user layers - how then should the user cut the tiles? At random?

Okay. We finish with the preparation, we go further.

Contents of delivery



So, we are given from the functional:


For the start of the API - in principle, normal. What is surprising is the lack of pre-installed controls - didn't you have time to do it?

We read the documentation. The section about Point-GeoPoint is skipped, everything is standard. Only here over the design would work, it is absolutely impossible to read the descriptions of the interfaces of the class, everything merges.



Go to the section "Map" and stumble on "Introduction" - "Download Library" - "Version of the library." Somehow it’s unexpected to see it in the “Map” section, isn't it?

Then we will use the DG.autoload function, into which we place the initialization code of the card:

[Code]

This method was considered in the “Quick Start” section. But the DG.autoload function has certain limitations: it internally uses the window.onload event handler. Therefore, if the window.onload handler is already defined on the page, then there will be conflicts (to bypass these conflicts, you can insert the card initialization code into the existing window.onload handler).


WTF? Why redefine window.onload? Write

  window.attachEvent && window.attachEvent('onload', callback) || window.addEventListener('load', callback, false); 


will be faster than this paragraph of documentation. Why, besides, this note was removed from quickstart - so that more users step on this rake? Unreasoned somehow.

We read further:
To set the map center, call the setCenter map method:
 //       point:myMap.setCenter(point); 



What is "point:"? Inaccurately.

Delete card

myMap.destroy ()

Destroys all objects that the map contains. To destroy the myMap object itself after calling this method, we recommend assigning null to the map object:

 //  .   ,       ID: var myMap = new DG.Map("myMapId"); //  .  DOMElement : var container = document.getElementById("myMapId"); //   ,    DOM   var myMap = new DG.Map("container"); //   ,       ID var myMap = new DG.Map("myMapId"); 



What a strange piece of code? What does he have to do with destroy? Guys, well, all the same documentation on the base class must be read.

Add event handler

myMap.addEventListener (objectId, eventType, callback)

Options:
objectId String The identifier of the DOM element to attach the handler to.


Pattern break. Why do I have to specify the DOM element when attaching an event handler to the map? If you specify an id other than map.getContainerId (), nothing works. Violence over the user some.

Move map north

myMap.moveN (moveStep)

Options:
moveStep Number Yes Step, how many pixels move the map north.


Again the gap pattern. Pixels north is like? Arc degrees north - I understand, pixels up - I understand, pixels north - I do not understand. Well, the meaning of the existence of this method eludes me.

Set the minimum zoom

myMap.setMinZoom (minZoom)

Options:
minZoom Number Yes Minimum possible zoom ratio (zoom). The minimum allowed value is 1.


The minimum allowable value is 1, the zoom control also does not allow to set less than 1 scale. But if you do not set the scale when initializing the map, you will turn out to be at 0th. I wonder how so. By the way, on the 0th scale, the getBounds map gives the area [-268, 434] in longitude - have we long ago been longer than 180 degrees?

Set limits on the boundaries of the map
myMap.setBoundsRestrictions (bounds, isChangePosition)


isChangePosition? We must still be friends with English.

Developments



We have already met the addEventListener weirdness. Further more: addEventListener is, removeEventListener is not. Why, then, use DOM names to mislead the user?

Event names should be written like this: “DgClick” - g is small. Considering that in all other places DG is always written in capital letters - annoyingly inconsistent. In the examples, by the way, the map is initialized again without a zoom.

Almost every event has its own type. The meaning of having such a large number of classes eludes me. For example, for DG.Events.Map you can ask get (Min | Max) Zoom - why? Who needs this functionality?

Markers



Constructor

Options:
options.geoPoint DG.GeoPoint Geographical location of the point pointed to by the marker.
options.icon DG.Icon Picture marker. The following describes the DG.Icon class in more detail. If icon is not specified, the default image is used.
options.clickCallback Function Handler that is called when the mouse is clicked on the marker. Context of the function call: the window object.


callback per click is, of course, useful. But then you need to go further and callback at least on mouseenter / mouseleave.

Each marker must belong to a specific group. This makes it possible to perform group operations. For example, suppose we have two types of markers: one for high-rise buildings, others for private houses. Placing the first in one group, and the second - in the other, we can clearly manipulate each of the sets of markers.


Grouping markers is, of course, convenient. But why can not groups be invested in groups? This is even more convenient, and will eliminate the need to have a default group.

I note that exactly the same groups are available for baluns, graphics and layers. Why describe them all separately? Enter the common base class Group and GroupManager, the documentation will be reduced by half.

Perform an operation for each marker in the group.
myGroup.forEach (callback, context)


GHM It was impossible to set the execution context in any other method, but here it is possible. For similarity to browser Array.forEach? Well then, all other methods, starting with addEventListener, need to be brought to the browser signature. By the way, in JS 1.6 the context parameter in forEach is optional, unlike.

Baluns



Consider an example of creating a balloon:
 //       : var myBalloon = new DG.Balloons.Common({ // ,    : geoPoint: new DG.GeoPoint(82.927810142519,55.028936234826), //   : contentHtml: '!   :)' }); //    : myMap.balloons.add(myBalloon); 



The empty line in the middle of the code is <br>, which zasecapit forgotten (as opposed to quikstart). Again, carelessly - an example now does not work.

The balun parameters are again surprised by the free treatment of English and common sense (isClosed - whether to show a cross, contentSize - whether the size is fixed).

It is interesting that the balun has no events (or are not described?).

There are no hints. And would not interfere.

Geometries



I will not say a word about the geometry of a bad word — a good, good-quality graphical library with extensive customization options (the linecap options — the choice of rounding lines at the breakpoint — I, it seems, have never met). Is that surprising lack of events. (However, why surprises? The problem is known - even a transparent canvas / svg container closes the map. Solved :)). Add geodetic left.

Controls



Positioning controls is another example of violence against the user's brain.
 myMap.controls.add(new DG.Controls.Zoom(), null, new DG.ControlPosition(DG.ControlPosition.TOP_RIGHT, new DG.Point(20,10))) 


And this is all in order to stick the control to the right corner with an offset of 20.10.
I understand that Big Brothers have the same balalaika - but this is not a reason to copy bad decisions. Why instead
 new DG.ControlPosition(DG.ControlPosition.TOP_RIGHT, new DG.Point(20,10))) 

do not write
 'topright', [20, 10] 
?
Or even like this:
 { top: 20, right: 10 } 
?

And at the same time carry this setting into the constructor control, so as not to write null instead of the group name in controls.add?

Of the controls, only the zoom is available - did not have time to do the rest? Or with the makers of any problems? Apparently, to compensate for the lack of built-in elements, opened the interface for creating custom - DG.Controls.Abstract.

By the way, I tried to add new DG.Controls.Abstract () to the map - it worked properly, no exceptions threw. So this is not abstract, but base. Binding the extend inheritance method to an abstract class does not seem to be the right solution — it’s quite a universal method, so why limit its use to a concrete class.

The interface itself looks quite reasonable - except that the getStates () / setState () helpers look absolutely useless.

Layers



The map API allows you to overlay custom layers on top of a geographic map layer. This makes it possible to display on the map virtually any objects, routes, areas. For example, it is easy to show the city’s coverage by Internet providers, or to graphically display the delivery areas of a store or pizzeria with the time and cost of delivery. You can also specify the route of travel to your offices or the route of a city tour. In short, the possibilities are limited only by fantasy.


... as well as the need to guess the projection, as we have already found out.

options.countTileServers Number The number of subdomains for the tile server.

Browsers have a limit on the number of simultaneously downloaded files from one domain. To get around this restriction, you can load tiles from several subdomains. For optimal card loading speed, we strongly recommend setting this value to 4.

The parameter is used in conjunction with the options.tilePrefix parameter. By default, options.countTileServers is zero - tiles are loaded from one server.

options.tilePrefix String The prefix of the subdomain name of the tile server.

For example, the url parameter is https: //example.com/$ {z} / $ {x} / $ {y} .png. If you define options.tilePrefix as mytile, we get the URL of the subdomains of the tile server in the form: https: //mytile1.example.com/, https: //mytile2.example.com/, https: //mytile3.example.com/ and so on, depending on the options.countTileServers parameter. Therefore, the parameter is used in conjunction with the options.tilePrefix parameter.


Uh What kind of magic? Why not just do something like this: https: // mytile $ {n} .example.com / $ {z} / $ {x} / $ {y} .png?

AJAX



The meaning of the existence of the AJAX API is not completely clear. Any ajax-api of any framework is more convenient and functional, why does the cartographic API even get into this glade? By the way, you have a typo - fail t ure.

Generally



In general, the 2GIS API leaves a dual impression.

On the one hand, yes, a full-fledged API, developed in a short time, with a good functional component proper (90% of the needs will be covered), the groundwork for expansion is left.

With another:

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


All Articles