📜 ⬆️ ⬇️

Gugarkartim on clever

Recently, it has become fashionable to use a gouglet.
Google map of the court, Google map there.
And the main thing is not to forget to display your objects on the map.
But, for some reason, the overwhelming number of services use the wrong implementation of the process of transferring tokens from the server to the client.
More precisely, they do not correctly form requests.
And to put it even more precisely - they do it.

So, what does a normal request from the client to the server look like with the wish to transfer to the client markers for a known area?
Option 1 - if we have little markers - we load them all (including initially in the code) and then draw. The smartest use different marker-managers for this ...
But what will happen if a lot of objects?

Option 2 - “hear the server, give me the markers for BBOX = (one corner), (another corner)” .
And this option is the most common.
It is used by "GdeEtot Dom" and "DomNaKarte" and "MirTesen" and even the brand new "Around Me" .
And this option is loved by Google itself, since this is how regional data from KML is loaded.
And this option is very fond of “loading off” the card and writing “I’m loading ...”
And it loads for a long time.
')
And why?
Imagine that you have 100 objects in the database (there are eight million in the Wikimapia database) ...
And you need to make a selection by the double key (x, y) FROM and TO.
And it can take (and takes) a second, or even two. And even more - the DB is not rubber.
And the slightest change in coordinates will cause a “re-fetch”.
If the databases were able to say - they are you foul language :)
You can of course facilitate the work of the database using the “spatial” formats (points and polygons, in general, initially the data on the plane) - but in any case - each request will be unique, and these requests are not cached !
And the sample will be not entirely accurate - the client will still have to sort the markers so that there is no heap at one point ...

Well - if you like it - continue to use this approach further, and I will tell you about a slightly more complex (and much longer "code" option), without these flaws.
I did not come up with the approach myself, and it was not me who did its implementation.
Moreover, I used to use Option 2 myself.
But once I saw “how it should be” and I was visited by (alien) wisdom.

So, the approach that I propose, which is not strange, is proposed (and used) by Google itself for addressing the pictures of the Google Card.
Even this approach could be used on the SERVER part by those smart programmers who, as a child, took a great interest in game deeds there and know what a quad-tree is.
I even know one service that on the server side we have markers just in the quad, but requests data from the client anyway for Option 2.
Well, how else to form a query to the database?
And here everything is simple - you just need to request the node itself :)

Let's turn to the original source - google.
We tear off a help on its api, and we go to the bottom. Until manual addressing tiles.
And we see an interesting picture ...
For example, the map consists of 4 256x256 pixels tiles, resulting in a pixel space from 512x512
Tiles are indexed using x, y coordinates from that origin. For example, it can be referenced by a unique x, y pair:



Well, in the brain chenit "clicked"?
In general, our task is not to ask to give all the data on the markers by this coordinates.
Our task is to request a TILE!
And the tile (here the dog and dug) is not one.

What do we need?
1. Understand the current viewport
2. Algorithmically convert this viewport into an array of tiles that are visible to us in this case.
3.MARK OUT that a part of the tiles can greatly go beyond the limits of the visible area, that they will exactly repeat the tile image layout of the google map, and that their size will be 256 by 256 pixels ALWAYS (well, of course, I said that strongly, you can request 128x128 and 512x512 , but it’s better to work “parallel” to the Googleboard ...
4. And after that, once again understand that the issuance of markers for one tile will be constant.
We will know the coordinates for which we need to select objects, we will know the pixel width of the sample. We will know the zoom card. Well and still that is required for the correct display :)

And most importantly - since the sample goes along constant coordinates and pixel resolution - you can cache it :)
And instead of a second or two or three, get a sample of objects for at least 30ms per card
(clarification: in the case of well-accelerated sampling, you can get up to 4 pixels in one moment (or patch Fox Fox, but not the most popular browser), and then 4 more tiles, and then more ...
Considering what is usually displayed from 6 to 8 tiles - in 100-200 ms the data will be on the client)
Another bonus of tiles - if one small tile “lacks” objects to provide data in the right amount - it can “go” up one level and transfer data from the region that has more markers.
Ie for Moscow will be high resolution. And for the tundra it is possible and less

The task of the client part - just follow what and where it was already requested.
And if you move the map - just a couple of tiles to load in a very short period.
Only new part. Refresh screen will not.

So, I lived and lived, I made 2 for good, and then I visited the wonderful project Wikimapia
Rummaged in their format of export and addressing. I took for myself the same and rushed :)
(What happened after a six-month-long seat on Wikimapia, read the topic about my neighbors )

Then there was the decoding of the Wikimapia scripts, their awareness and implantation on themselves.

Next, I will provide the code from the live service.
The code is presented as is and 90% saved in the original, there are no comments
The server side code was already written independently.
Used addressing saved from wikimapia looks like
{SERVERBASE} / z / 12/23/34… .xy
where z - says that we are requesting objects (u will be users, i pictures, and so on)
If you take a closer look, you can understand that you can even “hold” data on the file system, while adding new objects, simply by updating some of the files.
Each digit (0-3) tells us which piece of the next quad quad we need to go to.
Personally, I do not recommend using the range 0-3, but using 1-4, then the addressing can be stored in BIGINT (for example, Canada, we have the beginning 00 (left upper part of the map, left upper part of the map) and, in the case of storage in bigintera, this is the beginning " get bored ”)

So the code on the client is a task according to a given viewport to get an array of tiles

Some code


// used to normalize Y coordinates
var consar = new Array , -11178401,0,11178401,21943045,31952162.40979898.48922499.55776579.61606396.66513260.70612614.74959543.76840816.79171334.81093213.82676284.83979259.85051128);
// the first part is the transformation of coordinates into division itself, the name of the functions and their contents are taken from Wikimapia

function getdatakvname (x, y, curzoomkv)
{
var xdel = 0;
var ydel = 0;
var xline = 0;
var yline = 0;
var x1 = -180000000;
var x2 = 180000000;
var y1 = -85051128;
var y2 = 85051128;
var y1cons = 0;
var y2cons = 32;
var yconsdel = 0;
var n = 0;
var z = curzoomkv-1;
while (z> = 0)
{
xdel = Math.round ((x1 + x2) / 2);
if (n <4) {yconsdel = (y1cons + y2cons) / 2; ydel = consar [yconsdel];}
else {ydel = Math.round ((y1 + y2) / 2);}
if (x <= xdel) {x2 = xdel; xline = xline * 2;}
else {x1 = xdel + 1; xline = xline * 2 + 1;}
if (y <= ydel) {y2 = ydel; y2cons = yconsdel; yline = yline * 2;}
else {y1 = ydel + 1; y1cons = yconsdel; yline = yline * 2 + 1;}
z--;
n ++
}
var out = new Array ();
out .xline = xline;
out .yline = yline;
return out ;
}

function cheakpoint (x, y, xline, yline, curzoomkv)
{
var xdel = 0;
var ydel = 0;
var x1 = -180000000;
var x2 = 180000000;
var y1 = -85051128;
var y2 = 85051128;
var y1cons = 0;
var y2cons = 32;
var yconsdel = 0;
var n = 0;
var xlinetest = 0;
var ylinetest = 0;
var test = 0;
var z = curzoomkv-1;
while (z> = 0)
{
xdel = Math.round ((x1 + x2) / 2);
if (n <4) {yconsdel = (y1cons + y2cons) / 2; ydel = consar [yconsdel]}
else {ydel = Math.round ((y1 + y2) / 2)}
test = Math.pow (2, z);
xlinetest = xline & test;
ylinetest = yline & test;
if (xlinetest> 0) {x1 = xdel + 1}
else {x2 = xdel}
if (ylinetest> 0) {y1 = ydel + 1; y1cons = yconsdel}
else {y2 = ydel; y2cons = yconsdel}
z--;
n ++
}
var out = new Array ();
if ((x> = x1) && (x <= x2) && (y> = y1) && (y <= y2)) { out .res = 1}
else { out .res = 0}
return out ;
}

function retcode (xline, yline, curzoomkv)
{
var xparam = 0;
var yparam = 0;
var test = 0;
var xlinetest = 0;
var ylinetest = 0;
var line = '' ;
var z = curzoomkv-1;
while (z> = 0)
{
test = Math.pow (2, z);
xlinetest = xline & test;
ylinetest = yline & test;
if (xlinetest> 0) {xparam = 1}
else {xparam = 0}
if (ylinetest> 0) {yparam = 2}
else {yparam = 0}
linepart = xparam + yparam;
line = line + linepart;
z--;
}
return line;
}

/// and the function of the request

function request_geo_objectsin ()
{
if (! allow_geo_requests) return ;
require_block = {};
map = GetMap ();
curzoomkv = map.getZoom () - 1;
bounds = map.getBounds ();
bounds_sw = bounds.getSouthWest ();
bounds_ne = bounds.getNorthEast ();
var x1point = Math.round (bounds_sw.lng () * 1000000);
var y1point = Math.round (bounds_sw.lat () * 1000000);
var x2point = Math.round (bounds_ne.lng () * 1,000,000);
var y2point = Math.round (bounds_ne.lat () * 1,000,000);
if (x1point <-180000000) {x1point = -180000000} if (x2point <-180000000) {x2point = -180000000} if (x1point> 180000000) {x1point = 180000000} if (x2point> 180000000) {x2point = 180000000} if ( y1point <-85051128) {y1point = -85051128} if (y2point <-85051128) {y2point = -85051128} if (y1point> 85051128) {y1point = 85051128} if (y2point> 85051128) {y2point = 85051128}

outar = [];
outar = getdatakvname (x1point, y1point, curzoomkv);
var xline = outar.xline;
var yline = outar.yline;
var maks = Math.pow (2, curzoomkv) -1;
var vlez = 0;
var xsdvig = 0;
var xlinet = xline;
var ylinet = yline;
while (vlez! = 1)
{
outar = cheakpoint (x2point, y1point, xlinet, ylinet, curzoomkv);
vlez = outar.res;
xsdvig ++;
xlinet = xlinet + 1;
if (xlinet> maks) {xlinet = 0}
}
vlez = 0;
var ysdvig = 0;
var xlinet = xline;
var ylinet = yline;
while (vlez! = 1)
{
outar = cheakpoint (x1point, y2point, xlinet, ylinet, curzoomkv);
vlez = outar.res;
ysdvig ++;
ylinet = ylinet + 1;
if (ylinet> maks) {ylinet = 0}
}
var temp = " ;
var newtemp = '' ;
var ylinesave = yline;
var ysdvigsave = ysdvig;
while (xsdvig> 0)
{
while (ysdvig> 0)
{
temp = '0' + retcode (xline, yline, curzoomkv);
var lineleng = 0;
var newtemp = '' ;
while (lineleng <temp.length)
{
newtemp = newtemp + "/" + temp.substring (lineleng, lineleng + 3);
lineleng = lineleng + 3;
}
var letbe = "z" ;
{
newtemp = 'z' + newtemp + gzipext;
xml_url = newtemp;
require_block [temp] = xml_url; // + "? x =" + x1point + "& y =" + y1point + "& x2 =" + x2point + "& y2 =" + y2point;
}
ysdvig -; yline ++;
if (yline> maks) {yline = 0}
}
yline = ylinesave;
ysdvig = ysdvigsave;
xsdvig--;
xline ++;
if (xline> maks) {xline = 0}

}
var i = 0;
clearNavRequestTimeouts ();
for (gotarrn in require_block)
// GO TO REQUEST A TILE!


that's all :)
It remains to receive the request on the server side and convert it back to coordinates
$ x1 = -180000000;
$ x2 = 180000000;
$ y1 = -85051128;
$ y2 = 85051128;

$ y1cons = 0;
$ y2cons = 32;
$ yconsdel = 0;

$ consar = Array (-85051128, -83979259 , -82676284, -81093213, -79171334, -76840816, -74019543, -70612614, -66513260, -61606396, -55776579, -48922499, -40979898, -31952162, -21943045, -11178401.0,11178401,21943045,31952162.40979898.48922499.55776579.61606396.66513260.70612614.74959543.76840816.79171334.81093213.82676284.83979259.85051128);


$ z = 1;
// RQ is the request digits; if not strange, the request is equal to getZoom () + 1
$ L = strlen ($ RQ);
$ ZM = $ L;
for ($ i = 0; $ i <$ L; ++ $ i)
{

$ test = intval ($ RQ [$ i]);
$ xdel = round (($ x1 + $ x2) / 2);
if ($ i <4) {$ yconsdel = ($ y1cons + $ y2cons) / 2; $ ydel = $ consar [$ yconsdel];}
else {$ ydel = round (($ y1 + $ y2) / 2);}

switch ($ test)
{
case 0: $ p1 = 0; $ p2 = 1; break ;
case 1: $ p1 = 1; $ p2 = 1; break ;
case 2: $ p1 = 0; $ p2 = 0; break ;
case 3: $ p1 = 1; $ p2 = 0; break ;
}
if ($ p1) {$ x1 = $ xdel + 1;}
else {$ x2 = $ xdel;}
if ($ p2) {$ y2 = $ ydel; $ y2cons = $ yconsdel;}
else {$ y1 = $ ydel + 1; $ y1cons = $ yconsdel;}
}

$ x1 = $ x1 / 1000000;
$ x2 = $ x2 / 1,000,000;
$ y1 = $ y1 / 1000000;
$ y2 = $ y2 / 1,000,000;

// BOT AND COORDINATES OF OUR REQUESTED BOX!
* This source code was highlighted with Source Code Highlighter .


I hope the described method will find its readers, and, most importantly, will help someone to make their project (and the whole world after it) a little bit better.

In action, you can look at this case either on the “source” of this Wikimapia approach, or on the “recipient” - my Neighbors

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


All Articles