var latsign = "+";
if (latd.value.replace("-", "") != latd.value) latsign = "-";
var longsign = "+";
if (longd.value.replace("-", "") != longd.value) longsign = "-";
var alat = eval(latd.value);
alat = Math.abs(alat);
var alatsuffix = 0;
var along = eval(longd.value);
along = Math.abs(along);
var alongsuffix = 0;
if (latm.value != "0" || lats.value != "0") {
alatsuffix = eval((latm.value) / 60.0);
alatsuffix = eval(alatsuffix + (lats.value) / 3600.0);
} else if (latd.value.replace(".", "") != latd.value) {
alatsuffix = 0;
}
if (longm.value != "0" || longs.value != "0") {
alongsuffix = eval((longm.value) / 60.0);
alongsuffix = eval(alongsuffix + (longs.value) / 3600.0);
} else if (longd.value.replace(".", "") != longd.value) {
alongsuffix = 0;
}
var lata = eval(alatsuffix + alat);
var latis=latsign + lata;
var xlatis = latis.replace("-", "").replace("+", "");
if (xlatis == latis) latis = "+" + latis;
var longa = eval(alongsuffix + along);
var longis=longsign + longa;
var retis = ["", ""];
Today’s tutorial’s title is a bit cheekier than it really is, as we talk about a change to the software integration of a Weather Reporting web service for the geographicals suite here at this blog. The previous arrangements we detailed below at Integrating Global Weather Ajax with Geographicals Tutorial as shown below.
The previous weather reporting integration used a Web Service and for the changed arrangement we more or less screen scrape with the wonderfully generous World Weather Online website … thanks a lot. And thanks, too, for the link that helped me find this website, as well as the excellent link here that helped with the international country codes that came in handy for the changed arrangements.
Wanted to more or less have the changes in one place and it more or less happened that way, with the vast majority of changes needed to the calledget_weather.php code. The callingplace_latlong.php called get_weather.php in a different place, but this is only done because am not overly certain the old arrangements will not resurrect themselves. The old way was a bit different in that it did not need any State/Territory type information to proceed, but sometimes our screen scraping method does need to ask for this information.
Think it is good with software integration to aim to keep at least one aspect of the resultant code the same, and so it is here, with the same iframe and Ajax arrangements kept.
Did you know?
If you’ve examined, closely, the changes to get_weather.php above you may have wondered how I could get away with URLs involving city and country (codes) and sometimes state parts and yet, with the code, we are pretty slack about uppercase/lowercase stringency. However, did you notice the URL template we first got as a model to try to achieve …
… the .ASPX extension tells you this is ASP.Net and would be hosted on a Windows server (and this all happens courtesy of the early days with MS-DOS and some interesting file naming informational link is here), where files in uppercase are the same as those in lowercase or a mix of both, and in this way we get away with some slackness regarding the construction of our URL. If the hosting was Linux or Unix there would be a much more complicated task needed by the programmer, or, as would probably be more likely the case, the website URLs would be arranged differently.
Software integration takes many forms, and Ajax functionality is that little bit different, perhaps, in that with the tablet and mobile phone “touch” (with all its associated terminology like “gesture recognition”, “pinch” and “smart zoom”) is a concept, with more currency than the “click” and “hover” (of the original web-based systems) and this has increased the choices for ways to achieve things, with there being more accepted mobile world “ways” sometimes a bit different to the laptop/notebook/desktop “ways”. For instance, with Ajax, and the laptop/notebook/desktop world, I tend to want to do a lot with the “hover” (onmouseover) event, but this does not always translate well in the mobile phone/tablet world. In the mobile phone/tablet world multiple window use can get a bit awkward with the smaller screens.
In today’s tutorial we show some integration techniques using HTML “select” (dropdown) and/or “<a …><img … /></a>” (button) approaches, with the “hover” event doing the same as the “click” event. Whether you like this approach is debatable and you may want to trial such approaches with potential users when you come to creating commercial products. Ideally with catering for all of mobile phone, tablet, desktop and laptop/notebook users you can use the same base code (you may disagree here), with Javascript, HTML and CSS methods employed to compartmentalize device-dependent code using all the various forms of “if” in the softwares.
So the “Get the Weather” functionality of two days ago has a geographical component, that is for sure … the “where” in life is soooo programmable, isn’t it? … so we can integrate it into other Geographical software components we’ve been developing here on this blog.
Today’s part two (of two) of integration tasks, which includes the requirement that some Geographicals of unknown placename should “map” themselves to the nearest placename (via a function called find_nearest (with respect to whatever data source your software uses)), adds the “Get the Weather” Ajax software functionality to each of the “independent child” functions of the Geographicals suite … Sun Angles, Moon Angles, Great Circle Distance and Coriolis Force. Part one (of two) of integration tasks of yesterday was to associate the “Get the Weather” with a placename in the world. This is the more obvious integration task and we show two bits of integration to make that happen way below.
Here is a new link to some downloadable PHP programming source code explaining changes made specifically for the “Get the Weather” software component of the Sun Angles functionality here.
Here is a new link to some downloadable PHP programming source code explaining changes made specifically for the “Get the Weather” software component of the Moon Angles functionality here.
Here is a new link to some downloadable PHP programming source code explaining changes made specifically for the “Get the Weather” software component of the Great Circle Distance functionality here.
Here is a new link to some downloadable PHP programming source code explaining changes made specifically for the “Get the Weather” software component of the Coriolis Force functionality here.
Here is a new link to some downloadable PHP programming source code explaining changes made specifically for the Geographical supervisor of the “Get the Weather” software component (and others) here.
And here is the “independent child”Sun Angles live run (which now includes “Get the Weather”).
And here is the “independent child”Moon Angles live run (which now includes “Get the Weather”).
Sometimes (well, in my experience) you get the best “kick” out of integrating software components, rather than the writing of the component itself. Sounds weird, doesn’t it, but the fact is, the more functionality you can build into existing software frameworks you have built up, the more useful software becomes, rather than a whole lot of disparate functionality, which may be very cute, but the procedures for which can be forgotten by people in the length of time it takes to wander up the hallway with the mobile phone attached to one’s cranium (would the Queen do this?!).
So yesterday’s“Get the Weather” functionality has a geographical component, that is for sure … the “where” in life is soooo programmable, isn’t it? … so we can integrate it into other Geographical software components we’ve been developing here on this blog.
Part one (of two) of integration tasks is to associate the “Get the Weather” with a placename in the world. This is the more obvious integration task and we show two bits of integration to make that happen below.
Here is a new link to some downloadable PHP programming source code explaining changes made specifically for the “Get the Weather” software component (from tutorial below) here.
Here is a new link to some downloadable PHP programming source code explaining changes made specifically for the Geographical supervisor of the “Get the Weather” software component (and others) here … lots of the changes here involve readiness for the other piece of integration that we will cover at another tutorial tomorrow.
And finally here is the live run to test all the integration (which now includes “Get the Weather”) yourself.
Ajax techniques make your web pages very dynamic and useful, and there will be fewer changes of webpage required when using Ajax, because information derived from a data source (maybe a feed, maybe a database read, maybe a local source of data, maybe a web service website (as for this tutorial … thanks to GlobalWeather here)) make many changes of webpage obsolete.
Today we use HTTP Get methods to procure the data, but you will see other methods that can be used on that link above.
Let’s have a look at Wikipedia’s thoughts on Ajax below.
Ajax (also AJAX; /ˈeɪdʒæks/; an acronym for Asynchronous JavaScript and XML)[1] is a group of interrelated web development techniques used on the client-side to create asynchronous web applications. With Ajax, web applications can send data to, and retrieve data from, a server asynchronously (in the background) without interfering with the display and behavior of the existing page. Data can be retrieved using the XMLHttpRequest object. Despite the name, the use of XML is not required (JSON is often used instead. See AJAJ), and the requests do not need to be asynchronous.[2]
Ajax is not a single technology, but a group of technologies. HTML and CSS can be used in combination to mark up and style information. The DOM is accessed with JavaScript to dynamically display, and allow the user to interact with, the information presented. JavaScript and the XMLHttpRequest object provide a method for exchanging data asynchronously between browser and server to avoid full page reloads.
Have a look at the PHP downloadable code which you could rename to get_weather.php
Try a live run here.
Ajax is a client-side meets server-side melding of PHP (or ASP.Net) and Javascript and HTML and CSS and allows you to stay on the web page you are on doing many more things, rather than constantly changing web pages the way that HTML form tag makes you do. Ajax works with XMLHttpRequest object to quiz the server-side while staying on the client side. You may have guessed that we have been working up to this, and I refer you to the previous tutorial about JavaScript and the DOM Tutorial and PHP + JavaScript + HTML Primer Tutorial for important information to learn before tackling Ajax. Ajax shares similar restrictions to iFrames in limiting you to work within your own domain, generally speaking. Ajax normally makes use of the onmouseover (hence the amateurish added rendition of a cursor, where I was hovering over the option tag, but couldn’t take a snapshot of this … ie. too lazy to get the camera!) and onmouseout events of HTML elements and you may notice the less than ideal Internet Explorer behaviour for this Ajax code, and that is because for Internet Explorer the option tag has no onmouseover nor onmouseout event defined, so we did an awful kludge.
Earlier tutorials …
JavaScript is a tremendous web client-side language to learn. You may have heard of a server-side JavaScript, but this tutorial only deals with client-side work. This tutorial builds a JavaScript layer on top of the PHP tutorial made earlier, showing how the DOM can be used to change the look of your webpage dynamically, even if most of it is in an iFrame (but there are limits).
PHP is a wonderful language to learn. It is usually associated with being a web server-side language (as with this tutorial, where it is being shown on a local MAMP web server) but can be a command line tool as well. If you like PHP you may eventually like ASP.Net and/or Python, and vice versa. It has sophisticated data structures, Object Oriented (the thinking that you can build classes with data and methods which define objects created as you run the program … eg. you might write a class for book and have data members for things like numPages and publisher, author, creationDate and have methods called things like getCreationDate, setCreationDate, getAuthor, setAuthor allowing the user to use these methods rather than changing the data members themselves … heaven forbid that!) code concepts, and really combines well with JavaScript (as a client-side language).
Download programming source code and rename to ajax.php (but Ajax only works within the domain you use it, and this code mentions www.rjmprogramming.com.au so just use it for reference purposes or rewrite for purposes that suit you within your domain of interest).
Regarding this topic I really like “JavaScript & Ajax” seventh edition by Tom Negrino and Dori Smith
Did you know …
JavaScript makes a great easy-access Calculator?
Try typing the lines below into the address bar of your favourite browser:
English Translated Verb Conjugation Countdown Timer Tutorial
We had occasion to revisit our inhouse Italian/French/Spanish Verb Conjugation web application “redirector” (in that it is third parties doing the bulk of the work, thanks), further to the English Translated Verb Conjugation Emoji Tutorial‘s last mention.
It struck us, that what could improve the user experience, could be a …
30 second (timed for) …
automated click piece of functionality … that is flagged to the user via a …
countdown timer (presented as an HTML progress …
<progress id=myprog title="Click to stop the automated countdown to clicking for conjugations screen" data-allow="y" onclick="this.setAttribute('data-allow','');" style=visibility:hidden; value=30 max=30 min=0></progress>
… element) …
that if clicked, holds off on the automation ideas regarding the Conjugation Emoji Button 📖 clicking …
function backin() {
if (myxhr.readyState == 4) {
if (myxhr.status == 200) {
if (myxhr.responseText) {
var towrds=myxhr.responseText.split('"translatedText":"');
//alert('' + myxhr.responseText);
if (eval('' + towrds.length) >= 2) {
//if (myxhr.responseText.indexOf('correre') != -1) { alert(towrds[2]); alert(towrds[1]); alert(towrds[3]); alert(towrds[4]); }
document.getElementById('ini').value='';
document.getElementById('ini').placeholder=towrds[1].split('"')[0].split('.')[0].split('!')[0].split('?')[0].replace(/\'/g,'');
document.getElementById('emojiconjugate').innerHTML='📖'; //' <progress id=myprog title="Click to stop the automated countdown to clicking for conjugations screen" data-allow="y" onclick="event.stopProgagation(); this.setAttribute(' + "'data-allow',''" + '); return false;" style=display:inline-block; value=30 max=30 min=0></progress>';
document.getElementById('myprog').value=30;
document.getElementById('myprog').setAttribute('data-allow','y');
if (1 == 1) {
document.getElementById('myprog').style.visibility='visible';
} else {
document.getElementById('myprog').style.display='inline-block';
}
setTimeout(myprogit, 1000);
setTimeout(function(){
if (eval('' + ('' + document.getElementById('myprog').getAttribute('data-allow').length)) > 0) {
document.getElementById('emojiconjugate').click();
}
}, 30000);
}
}
}
}
}
function myprogit() {
if (document.getElementById('myprog')) {
if (eval('' + ('' + document.getElementById('myprog').getAttribute('data-allow').length)) == 0) {
if (1 == 1) {
document.getElementById('myprog').style.visibility='hidden';
} else {
document.getElementById('myprog').style.display='none';
}
}
if (eval('' + document.getElementById('myprog').value) > 0) {
document.getElementById('myprog').value=eval(-1 + eval('' + document.getElementById('myprog').value));
setTimeout(myprogit, 1000);
} else {
if (1 == 1) {
document.getElementById('myprog').style.visibility='hidden';
} else {
document.getElementById('myprog').style.display='none';
}
}
}
}
function twofocus(ih) {
if (ih != '') {
document.getElementById('ini').focus();
document.getElementById('ine').focus();
document.getElementById('emojiconjugate').innerHTML=' ';
document.getElementById('myprog').value=30;
document.getElementById('myprog').setAttribute('data-allow','y');
if (1 == 1) {
document.getElementById('myprog').style.visibility='hidden';
} else {
document.getElementById('myprog').style.display='none';
}
}
}
We chose the latter methodology for today’s work because visibility: hidden; still allows for the room that element would have taken up, so by the time we do apply visibility: visible; there is less annoying jiggering around going on with the webpage look when this happens.
if the user is entering an English verb to the top left … we could start …
filling in the Italian or French or Spanish verb translation (via the textbox’s placeholder attribute, hence, faintly) over to the top right … as well as …
add an emoji (button) 📖 (📖) in that top right area too … in addition to the usual …
brilliant WordReference.com information presented below this top row in the table
Clicking that emoji (button) 📖 (📖) in that top right area (or merely moving or tabbing in and out of focus at that top right textbox) would link up with WordReference.com verb conjugations better, we think, simulating what happens if the user starts their quest with the top right non-English verb textbox.
… to find some practical improvements to some of our existent web applications regarding the use of Emoji Image (in today’s case in the form of background images).
We think the existent French and Italian and Spanish flags as background images are a bit too imposing and so we intervened as per the Javascript …
function emoji_images_maybe(inlineurl) {
switch (inlineurl) {
case "url('it.jpg')":
document.getElementById('mybodtr').style.height='90px';
document.getElementById('englishtoitalian').style.height='90px';
return "linear-gradient(rgba(255,255,255,0.6),rgba(255,255,255,0.6)),url([italianFlagEmojiImageDataURIGoesHere]);
break;
case "url('fr.jpg')":
document.getElementById('mybodtr').style.height='90px';
document.getElementById('englishtoitalian').style.height='90px';
return "linear-gradient(rgba(255,255,255,0.6),rgba(255,255,255,0.6)),url([frenchFlagEmojiImageDataURIGoesHere]);
break;
case "url('es.jpg')":
document.getElementById('mybodtr').style.height='90px';
document.getElementById('englishtoitalian').style.height='90px';
return "linear-gradient(rgba(255,255,255,0.6),rgba(255,255,255,0.6)),url([spanishFlagEmojiImageDataURIGoesHere]);
break;
Italian French Spanish Verb Conjugation Text to Speech Tutorial
In following up on Italian and French and Spanish Verb Conjugation Event Tutorial as shown below we’ve increased functionality of English translations by adding Google Translate Text to Speech capabilities to …
Italian
French
Spanish
… via a new “loudspeaker” icon.
Some of the talking points with today’s changes involve …
a “reveal” idea whereby the showing of an HTML element is controlled by its Javascript DOM [element].style.width CSS property, whereby the element is effectively invisible at width:1px and in our case today becomes visible, at width:20px, and, thus, clickable, for Google Translatepopup window translation and text to speech capabilities via the control of …
UX-wise it is good to forewarn users with an option when it comes to functionality involving sound
use of Javascript DOM document.getElementsByTagName(‘img’) as a means by which to manipulate HTML elements that are not necessarily provided with an ID global property
As per the other tutorials in this thread, even with new Google Translate Text to Speech and Translation capabilities, nothing changes today about the techniques used today doing away with any need for a server side language by channelling the Ajax jQuery thoughts we presented with Ajax jQuery Primer Tutorial to make the most of the great resource that WordReference.com is. This happens in our HTML and Javascript programming source code you could call italian_conjugation.html, which changed to add in French and Spanish tense contextual verb conjugations in this way, with this live run link.
… and the conjugations from WordReference.com by offering onmouseover (ie. hover) or onclick (or mobile touch) event logic for conjugations offered by using the wonderful MyMemory resource to translate these verb conjugations back into English, and present them in an additional column with a different background colour.
As per the other tutorials in this thread, but even more so with gleaning information from the MyMemory API via a get method, nothing changes about the techniques used today doing away with any need for a server side language by channelling the Ajax jQuery thoughts we presented with Ajax jQuery Primer Tutorial to make the most of the great resource that WordReference.com is. This happens in our HTML and Javascript programming source code you could call italian_conjugation.html, which changed to add in French and Spanish tense contextual verb conjugations in this way, with this live run link.
the phrase “does not translate” is often relevant with translations between spoken languages
we may not have seen enough of the patterns giving us leads from the excellent WordReference.com conjugation webpages how to conjugate for some of the English prefix/suffix scenarios we’ve coded for … if you try this application out and see this is the case we’d love to hear from you, and you can email us at rmetcalfe@rjmprogramming.com.au or use the Feedback web application mentioned in Feedback Email Attachment Iframe Tutorial.
we’re only conjugating verbs
Even amongst the conjugating language “triplets” above, noticed that when it comes to the “tense” involved, there can be variations, but don’t need to tell a lot of you this old news. Did set me to thinking a bit about the The Tower of Babel story from the Bible, though. What would the world be like if we all spoke the one language? Esperanto, everyone?
There’s a link between “language” and “life”. That’s why a language without “verbs” is not a language. And the Earth back in those dark days before any life, had no conduits for “language”. And it’s hard to see how “life” sort of started up? But I guess chemistry experts might be able to tell us how this might have come about.
“Language” is all about patterns, and mirrors human progress with its “pattern” and “organization” and “flexibility” as a huge part of why we as “humans” got to be such agents of change on Earth. Just wish there had really been a more successful Doctor Dolittle in human history that could have got the “inside goss” (so to speak) on what we could have done better to protect the world’s environments.
As per the other tutorials in this thread, nothing changes about the techniques used today doing away with any need for a server side language by channelling the Ajax jQuery thoughts we presented with Ajax jQuery Primer Tutorial to make the most of the great resource that WordReference.com is. This happens in our HTML and Javascript programming source code you could call italian_conjugation.html, which changed to add in French and Spanish tense contextual verb conjugations in this way, with this live run link.
Hope you try it out, and even contemplate sending us some feedback.
Again, we saw that we could extend the functionality of the recent Italian and French and Spanish Verb Conjugation Tutorial as shown below, by, for Italian, to start with, trying to help the native English speaker out for where to look on the “conjugation” table presented for the English verb of interest, regarding conjugation information that might match their (verb) tense of interest.
As you probably well know, every language on Earth has its peculiarities regarding how we express ourselves with regard to time, and a lot of that is associated with the verbs, or action words we use, and in the case of …
Italian
French
Spanish
… that expression of the context of time in the grammatical usage, especially for people speaking the language, is reflected by conjugations made to the verb. Conversely, as I, a native English speaker, got to think about as this web application proceeded, English has words like “am” and “have” and “having” and “been” and “will” and “shall” and “is” and “are” and “was” and “were” and “would” and “should” and “has” and “had” and “to” and ‘”be” and “being” preceeding verbs, which can have, basically, two suffixes “-ing” and “-ed” (expressing present participles and past participles respectively) to try to do what conjugation does for Italian (we program for today) and French and Spanish languages.
But there’s more to “tense” than Past, Present and Future as you well can imagine should you learn a language other than your native tongue, which you tend to “go along with the flow” perhaps unaware that “tense” exists, for some learners. There are concepts as layers on top about the context of the time the person is speaking relative to the time they are or were talking about … it gets complex … so you get concepts like “Present Perfect Progressive” (which we did a tutorial about at HTML/Javascript Present Perfect Progressive Primer Tutorial) if you get right into the ins and outs of all this grammar … which you might need to do to master that second language.
Yet again, nothing changes about the techniques used today doing away with any need for a server side language by channelling the Ajax jQuery thoughts we presented with Ajax jQuery Primer Tutorial to make the most of the great resource that WordReference.com is. This happens in our HTML and Javascript programming source code you could call italian_conjugation.html, which changed to add in Italian “tense” thoughts in this way, with this live run link.
Again, we hope you try some Italian, with specified “tense” prefix words and suffix endings, to see how the new functionality helps you out with Italian verb conjugations.
Italian and French and Spanish Verb Conjugation Tutorial
We saw that we could extend the functionality of yesterday’s Italian Verb Conjugation Primer Tutorial as shown below, by accessing other resources from the great WordReference.com, adding French and Spanish verb conjugation to yesterday’s Italian verb conjugation.
As a rule we tend to find that replacing text with HTML select “dropdown” menus can help out this adaption pretty effectively. The other feature of today’s Javascript coding is the use of eval to team with the language code to direct user traffic to the correct parts of the WordReference.com website.
Along the way we added some background “flag” imagery we found at Science Kids … thanks, heaps.
Down the little brick road we also added a couple of hashtag navigators, HTML a links that just navigate within the page, allowing the user to move from the conjugation yellow zone to the translation zone (if you translated from English), as much as anything because the conjugation may need to be prompted by picking the “verb” amongst the list of “translated” possibilities, which you can then feed into the rightmost HTML input type=text textbox to, more than likely, get the (verb) conjugation you may have missed with the first pass.
Nothing changes about the techniques used today doing away with any need for a server side language by channelling the Ajax jQuery thoughts we presented with Ajax jQuery Primer Tutorial to make the most of the great resource that WordReference.com is. This happens in our HTML and Javascript programming source code you could call italian_conjugation.html, which changed to add in French and Spanish in this way, with this live run link.
Learning Italian as a native English speaker is best done when you are young, and beginning recently on this quest, I learnt a bit of this.
To me, what stuck out, was how easy we get it in English with regard to (the lack of) conjugating verbs, or articles, or adjectives, in our grammar.
Is it that, in English, we can say something in a hurry and, sort of, wait to fix it up later, because we don’t conjugate verbs in our mind, or is this not how it works in other languages? Actually, am pretty sure no, because conjugation is done so fast in the minds of Italian speakers that it is no issue … hard to imagine, though, from where I’m standing … well, actually, sitting. Am not here to say, but know it is this, that teachers of Italian to English native speakers, concentrate on in early lessons.
With this in mind, we don’t for one second pretend we are not using the wonderful resources at WordReference.com with today’s web application, but we thank them for their brilliance, and just rearrange things that you could glean perfectly well from here but need to take a few more steps to reach the conjugation (today it’s just verbs) web page bits, whereas we throw the conjugation bits straight at you. And yes, we do try to cater for the irregular verbs, and where they are regular you should see the word “regular” mentioned in the yellow zone conjugation areas … because we all know … well, you know what we mean?!
The techniques used today do away with any need for a server side language by channelling the Ajax jQuery thoughts we presented with Ajax jQuery Primer Tutorial to make the most of the great resource that WordReference.com is. This happens in our HTML and Javascript programming source code you could call italian_conjugation.html with this live run link.
So we hope you enjoy this break from our usual (other way around) ESL game (if it’s a game) to some “Conjugate, Italian Style” play.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
Could it be applied to our inhouse Google Charts PHP web applications?
And if so, is the work generic enough to just be applied to our gchartgen.js external Javascript they, mostly, all use?
And proceeding forward, if it works, how can it help? (Everybody needs a dorothy dixer?!)
… and the jury’s in …
Yes
95%
Email and SMS communications could all be hashtag navigation based on URLs not needing PHP mail nearly as much
In arriving at our “hived off” (because it is useful just itself for other “parents” out there (as what we like to think of as a “tool”), we’re thinking) hashtagdata_self_post.js …
if (('' + location.href).indexOf('/GeoChart') == -1) {
checkfortotalhash();
}
… proof of concept start we made a discovery that surprised us with …
when an intervention point for external Javascript occurs before the webpage’s document.body onload event …
you do better scouring location.href rather than document.URL (though, curiously, document.title is okay) and that location.href may contain hashtagging data (or we were dreaming … anyway, we’ve coded for either) … and …
is okay recognizing location.hash hashtagging data … but …
we found a setTimeout arrangement of adding a form to document.body.innerHTML helps get past the document.body onload event starting timing
… and exceptions for web applications doing their own thaing here should be considered.
navigational arrangement “proof of concept” methodology converting hashtagged incoming data into an HTML form method=POST target=_self action=[HereIsLookingAtYouKid] navigation back onto itself … and when happy just slapped that into the changedgchartgen.js external Javascript helper … along with …
email (mailto:) and SMS (sms:) URL tweaks to change ? to # to bypass the PHP server woooooorrrrrrllllllddd (and any PHP mail servers) for the Javascript client mailto: and sms: communication conduit wooooooorrrrrllllldddd … causing within a Javascript function called iftoobig … the changes …
if (eval(eval(urlin.length) + eval(urldata.length) + eval(urldata0.length)) < 800 || urlin.split('?')[0].split('#')[0].length < 800) { //1000) { // vs 2000
return urlin.replace('?','#');
}
Bear in mind, this is not ignoring the serverside. You’ll notice the chance of form method=POST coming into consideration. You need a serverside language to handle that method=POST … it’s just that not involving a PHP mail server and the mail command, when the amounts of data are substantial, so often, is the improvement, to our mind.
All that being so URLs such as the Google Chart Pie Chart (meddled around with) one …
… style of arrangement in the past, but we’ve found it useful recently (a couple of times) because we find we want to, more and more, use …
hashtagged data
reassembled into a …
form
method=POST
action=[URLofInterestOfPHP]
target=_self
… arrangement …
… that way keeping any window.parent and window.self and/or window.opener and window.self connections intact, and at the same time, being able to involve very long (mostly hashtagged data) URLs in the arrangements.
Let’s show you an example. In the recently talked about Google ChartsImage Chart Map Chart inhouse interfacer called image_chart.php downaways into the code it now goes …
<?php
$screenheight='0';
<br>
// ;Continent;CC1|CC2|:blLAT,blLONG,trLAT,trLONG:width,height:scblX,scblY,sctrX,sctrY
$continfo=';Europe;IS|CY|:35.16666,-27.6,67.0,33.36666:468,450:422,560,890,140'; // 53 523
$continfo.=';Australia;AU|AU|:-44,113.65,-10.26667,161.28333:600,450:422,560,866,140';
$continfo.=';Asia;FI|WS|:-14,37,81,179.9:600,450:422,560,866,140';
$continfo.=';America;GS|US|:-56,-179.9,77,-35:600,450:422,560,866,140';
$continfo.=';Africa;TF||:-35,-17,37,52:600,450:422,560,890,140';
if (isset($_GET['screenheight'])) {
$screenheight=str_replace('+',' ',urldecode($_GET['screenheight']));
}
if (isset($_POST['screenheight'])) {
$screenheight=str_replace('+',' ',urldecode($_POST['screenheight']));
}
<br>
if (isset($_GET['nothing'])) {
exit;
} else if ($screenheight != '0' && !isset($_GET['returnxytoparent']) && (!isset($_POST['returnxytoparent']) && !isset($_POST['ix']))) {
echo "<html>
<head>
<script type=text/javascript>
function tryit() {
if (('' + location.hash).indexOf('returnxytoparent=') != -1) {
document.getElementById('returnxytoparent').value=decodeURIComponent(('' + location.hash).split('returnxytoparent=')[1].split('&')[0].split('#'));
document.getElementById('submy').click();
}
}
</script>
</head>
<body onload=tryit();>
<form style=display:none; target=_self action=/PHP/GeoChart/image_chart.php method=POST>
<input type=hidden name=returnxytoparent id=returnxytoparent value=\"\"></input>
<input type=hidden name=screenheight value=" . $screenheight . "></input>
<input type=submit style=display:none; value=Submit id=submy></input>
</form>
</body>
</html>";
exit;
} else if (isset($_GET['returnxytoparent']) || (isset($_POST['returnxytoparent']) && !isset($_POST['ix']))) {
// more code to do with real returnxytoparent data follows
}
// more code follows
?>
… called from our inhouse Region Picker in this way now (where inhouse Javascript function windowdotdotopen can be thought of as window.open for these purposes) …
… setting up a two way understanding between window.opener Region Picker and window.self Google Charts Image Chart Map Chart inhouse interfacer. Using that target=_self arrangement above allows …
despite the “double dipping” transfer of hashtagged URL data (when longer than 750 characters, that is) into method=POST form data (of considerable size) …
that understanding between window.open Region Picker and window.self Google Charts Image Chart Map Chart inhouse interfacer remains intact
Yayyyyyy!
If this was interesting you may be interested in this too.
If this was interesting you may be interested in this too.
Google Charts Geo Chart Region Picker Alignment Tutorial
In our inhouse Region Picker we like the way the right hand webpage side involved some useful …
position: sticky;
… positioning, useful within the realms of what is going on on the right hand side.
But now we’re wanting some horizontal integration, linking commonalities of left and right regarding the country of interest, as a user’s eyes scan across the webpage, at pivotal times.
… all feature the great [element].scrollIntoView() way to programmatically scroll, rather than use (the sometimes ignored) window.scrollTo([left],[top]); or location.href=’#[elementID]’; approaches. It has the advantage of not altering location.hash (if you see that as a plus). We’d rather not add complexity, so, yes, we prefer the [aCountryElement].scrollIntoView(); call approach, at those pivotal times. This accounts for the left hand side of the webpage agreeing to the right hand side’s vertical scroll position. But what about the other way around? There, we started applying a CSS padding-top property to relevant right hand side elements (via DOM [element].style.paddingTop=[pixelsToExtendDown]px;), at pivotal points in the programming flow.
And on double clicking a right hand side button to start creating those background Geo Chart SVG based “images” we turn on a progress cursor, at least for non-mobile platforms, because the user needs patience here, in the changedregions_via_countries.htmlRegion Picker.
background-image:url(‘data:image/svg+xml;utf8, blahde blah ‘); nor background-image:url(‘data:image/svg+xml;base64, blahde blah ‘); ideas were not working for us … but today, we started to try …
overlay HTML div position:absolute; opacity:0.5; z-index:-4; …
We can’t remember a “foreground overlay” scenario so resembling a “background image feeling” end result, the transparent colour introduced into the Google ChartsGeo ChartSVG being crucial to help make this all work.
We want to be able to control the way a Google Charts Geo Chart can be nested within an HTML div element, for instance. We started the day wanting to be able to make …
a Google Charts Geo Chart be a background image to a div element … alas, on this first draft we couldn’t get there (but will continue with the research here) … whereas we succeeded …
adding the Google Charts Geo Chart interfacer’s resultant SVG data as the innerHTML (ie. content) …
<?php echo ”
function newbackin() {
if (dmyxhr.readyState == 4) {
if (dmyxhr.status == 200) {
if (dmyxhr.responseText) {
var m_t='image/jpeg';
var h_t='179';
var w_t='320';
var dbits = dmyxhr.responseText.split('\"height\": ');
if (dbits.length > 1) {
h_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"mime_type\": \"');
if (dbits.length > 1) {
m_t=dbits[1].split('\"')[0];
}
dbits = dmyxhr.responseText.split('\"width\": ');
if (dbits.length > 1) {
w_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"data\":');
dbits = dmyxhr.responseText.split('\"data\":');
if (dbits.length > 1) {
// replace all '_' with '/' and all '-' with '+' thanks to https://stackoverflow.com/questions/757675/website-screenshots
dgsbi='<img alt=\"Blog Posting Image\" style=\"width:' + w_t + 'px;height:' + h_t + 'px;\" width=' + w_t + ' height=' + h_t + ' src=\"data:' + m_t + ';base64,' + dbits[1].split('\"')[1].split('\"')[0].replace(/\_/g,'/').replace(/\-/g,'+') + '\"></img>';
//alert('dgsbi=' + dgsbi);
}
}
}
}
}
function ajaxit(urlin) {
if (urlin.length > 0) {
aurl=urlin;
if (window.XMLHttpRequest) {
dmyxhr = new window.XMLHttpRequest;
}
else {
try {
dmyxhr = new ActiveXObject('Msxml2.XMLHTTP');
} catch (othermicrosoft) {
try {
dmyxhr = new ActiveXObject('Microsoft.XMLHTTP');
} catch (failed) {
dmyxhr = false;
}
}
}
var xurl = 'https://www.googleapis.com/pagespeedonline/v1/runPagespeed?url=' + encodeURIComponent(urlin) + '&screenshot=true';
if (dmyxhr) {
dmyxhr.onreadystatechange = newbackin;
dmyxhr.open('GET', xurl, true);
dmyxhr.send(null);
}
}
}
function wbtoa(instris) {
var outstris=instris;
while (outstris.indexOf(String.fromCharCode(10)) != -1) {
outstris=outstris.replace(String.fromCharCode(10),'');
}
var xzs=prompt(outstris, outstris);
return outstris.replace(/\\\"/g, \"'\");
}
In that Nearest Places form part of the TimeZone places webpage, we noticed that even when shaping to enter a TimeZone Placename in the first textbox we made no attempt to fill in …
latitude
longitude
… when we have the information to do so, and even if we’re misunderstanding a placename designation, that should not stop us from trying, because those two numerical fields above can be corrected, and the form resubmitted, in these scenarios.
Apart from making a PHP derived Javascript variable be made available to the code, we “wrap” …
GeoJson World Countries Drag and Drop Makeover Nuance Tutorial
Nuance alert! We’re not sure if you noticed, but if you tried out the Drag and Drop functionality in the World Countries web application of yesterday’s GeoJson World Countries Drag and Drop Makeover Tutorial you may have noticed …
for a country with lots of TimeZone places, like Brazil, you could get a decent Google Chart Geo Chart map up … but if you were to click the “Map?” link down the bottom of that iframe …
it would fail to show a Google Chart Map Chart for that country’s TimeZone places
This fix, believe it or not, is interesting, perhaps only in an “internal use only” sense, we grant you. But our solution got us delving even more into hashtagging data, so that the solution we came up with was a hybrid whereby …
stayed with PHP $_GET[] (ie. address bar ? and & argumented) data (versus using PHP $_POST[] methodologies) … but …
where it came to the &data=[most of the data] part, other than its first character, we hashtagged the rest
… so that the logic flows as per usual, but we intervene at places and flesh it out via location.hash (client side only) means. We won’t bore you too much with all the places of intervention except the receiving map.php’s “easiest to get” intervention …
In addition to the Wikipedia information, at the very least, presented following a successful drag and drop operation, from today, we also start presenting a new HTML iframe element containing …
Nearest TimeZone places along with Google Charts for each unique country involved
we wanted the first popup window content be aligned to the top and left … and then …
we thought it would be good to also, in “Drag and Drop land”, relevant countries nearby to the user’s drop point TimeZone Places be showing below that (and it panned out the best way to show this, for us, was via an iframe pointing at another incarnation of the tz_places.php webpage, because it could have GET arguments iso, iso2, iso3 etcetera to point at ISO 2 letter country codes, which we made more readily available (via a new data-ccglobal data attribute applied to the select option subelements presented) for the changed external Javascript countries.js we decided should get into the mix via a new Javascript function …
function tzagain(inhtml) {
var outhtml=inhtml, dccs=[], getarg='?', theone=1;
if (inhtml.indexOf('left:0px;') != -1 && inhtml.indexOf(' data-cc=') != -1) {
dccs=inhtml.split(' data-cc=');
for (var ii=1; ii<dccs.length; ii++) {
if (getarg == '?') {
getarg+='iso=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2);
theone++;
} else if (getarg.indexOf('=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2)) == -1) {
getarg+='&iso' + theone + '=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2);
theone++;
}
}
if (getarg != '?') {
if (inhtml.indexOf('</bo' + 'ody>') != -1) {
outhtml=inhtml.split('</bo' + 'dy>')[0] + '<iframe src="./tz_places.php' + getarg + '" style="position:absolute;left:0px;top:120px;width:100%;height:900px;"></iframe></body></html>';
} else {
outhtml=inhtml + '<iframe src="./tz_places.php' + getarg + '" style="position:absolute;left:0px;top:120px;width:100%;height:900px;"></iframe>';
}
}
}
return outhtml;
}
… that superfluous looking ?rand=blah measure being pretty useful really regarding getting around the cache keeping old external Javascript in its mind after changes.
Curiously, the grandparent regions.php starting off all this needed no changing! We hope you like these tweaks!
add similar drag and drop logic into our World Coastlines GeoJson web application … and along the way, also for the World Countries web application …
hold off involving the (pretty kludgy looking) vertical scrollbar of our drag and drop pin’s underlying HTML iframe …
<iframe scrolling=no frameborder=0 name=iftr id=iftr style=display:none; srcdoc="<body style=background-color:transparent;><p id=mg title='Wikipedia country page below via drag and drop to world map' draggable='true'>📍</p><br><br><div id=myh1></div><script type='text/javascript' src='./countries.js?rand=321156747657' defer></script></body>" data-src=></iframe>
… until the first drag operation starting, calls on …
parent.document.getElementById('iftr').scrolling='yes';
… proving a Javascript DOM control of the “scrolling” attribute works (as we weren’t sure, having never done this before)
It’s worth beavering away at a guinea pig web application until (just about complete) satisfaction (for now) before having a parallel set of code changes happening simultaneously, we’ve always found.
So, what happened in “external Javascript land”? No need for a “regions.js” here, as parent.document.URL can be scrutinised in that “external Javascript land” to discover which web application is the parent, and act accordingly. So changed were our changed external Javascript countries.js in …
our GeoJson World Countries web application Drag and Drop logic worked on an iPhone … but …
our GeoJson World Countries web application Drag and Drop logic did not work on an iPad
They’re both iOS! And usually the smaller iPhone has the problem and the larger iPad is okay when there is an odd scenario happening. So, what gives? Well, the odd thing is, it was just rearrangements of code and iframe srcdoc usage …
<iframe frameborder=0 name=iftr id=iftr style=display:none; srcdoc="<body style=background-color:transparent;><p id=mg title='Wikipedia country page below via drag and drop to world map' draggable='true'>📍</p><br><br><div id=myh1></div><script type='text/javascript' src='./countries.js?rand=321156747657' defer></script></body>" data-src=></iframe>
… that ended up helping us fix the issues. Figure this, on iPad our emoji pin could not be made visible down the bottom left of iPad screen but could be made to work in the title elements section?! Of course, we might have been having a bad day, but in our defence, even debugging in Safari via …
iPad Safari web browser invocation …
Apple white lead from iPad to MacBook Air …
MacBook Air Safari web browser Develop menu dropdown got us to debugging functionality
“long hover” (ie. on non-mobile, wait for a long while after the onmouseover event initiation to see whether the user is still hovering) … and today, a bit like that, is the new, for us …
“long drag” (the user waits a long time between the drag initialization and the drop event)
… and because we found “dawdling” on a drag and drop fairly unnatural, we think this “long drag” idea “has legs”, in that it works well as a deliberate act made by a user, knowing at the end they benefit from their actions. For us, with our GeoJSON Map web application, yesterday, with GeoJson World Drag and Drop Pin Tutorial, the drag and drop led to …
Wikipedia country information webpage … and today, we allow a “long drag” get you to …
Google Maps drop position information webpage … if the “long drag” is for 10 or more seconds …
Google Earth drop position information webpage … if the “very long drag” is for 20 or more seconds
onclick event logic … and today, we start to also include …
drag and drop event logic (like, but nuanced as explained below, the experimental drag and drop ideas included in the recent Planet Moon Game Tutorial) … the nuanced differences involving …
the drag part of the events occurs in an iframe (populated via small amount of srcdoc HTML and Javascript) able to reference its originator via window.parent …
drop part of the events occurs in that originator parent and so several Javascript function called by the child reside in the parent … and …
the child “drag” event controller uses the new external Javascript countries.js …
// countries.js
// July, 2023
// RJM Programming
// Help out countries.html and countries.php
var pos3=0, pos4=0, tdid='', poligono, punto, coone='', prectis;
// var poligono = [[2,9],[8,6],[12,10],[15,2],[10,4],[5,1]];
// var punto = [6, 5];
function pointInPolygon(polygon, point) { // thanks to https://community.appinventor.mit.edu/t/geofence-check-if-a-point-is-inside-a-polygon-javascript-map/57091
var odd = false;
for (var i = 0, j = polygon.length - 1; i < polygon.length; i++) {
if (((polygon[i][1] > point[1]) !== (polygon[j][1] > point[1]))
&& (point[0] < ((polygon[j][0] - polygon[i][0]) * (point[1] - polygon[i][1]) / (polygon[j][1] - polygon[i][1]) + polygon[i][0]))) {
odd = !odd;
}
j = i;
}
return odd;
}
function andlaterstill() {
if (9 == 6) { // temporary
if (tdid != '') {
document.getElementById(tdid).innerHTML=document.getElementById(tdid).innerHTML.substring(0,1);
} else if (document.getElementById('mytable').innerHTML.indexOf(clonedatatwo) != '') {
document.getElementById('myh1').innerHTML=document.getElementById('myh1').innerHTML.split('</table>')[0] + '</table>';
}
if (document.getElementsByTagName('div')[0].innerHTML.indexOf(clonedatatwo) != -1) {
document.getElementsByTagName('div')[0].innerHTML=document.getElementsByTagName('div')[0].innerHTML.replace(clonedatatwo,'');
} else if (document.getElementsByTagName('div')[0].innerHTML.indexOf(clonedatatwo.replace('dragging','')) != -1) {
document.getElementsByTagName('div')[0].innerHTML=document.getElementsByTagName('div')[0].innerHTML.replace(clonedatatwo.replace('dragging',''),'');
} else if (document.body.innerHTML.split('<table')[0].indexOf(clonedatatwo.replace('dragging','')) != -1) {
document.body.innerHTML=document.body.innerHTML.replace(document.body.innerHTML.split('<table')[0], document.body.innerHTML.split('<table')[0].replace(clonedatatwo.replace('dragging',''),''));
} else if (document.body.innerHTML.split('<table')[0].indexOf(clonedatatwo) != -1) {
document.body.innerHTML=document.body.innerHTML.replace(document.body.innerHTML.split('<table')[0], document.body.innerHTML.split('<table')[0].replace(clonedatatwo,''));
}
}
tdid='';
}
function getprectis() {
if (window.opener) {
if (window.opener.document.getElementsByTagName('body')[0]) {
return window.opener.document.body.getBoundingClientRect();
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
return parent.document.body.getBoundingClientRect();
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
return parent.document.body.getBoundingClientRect();
}
}
return null;
}
function wod(ididea) {
if (window.opener) {
if (window.opener.document.getElementsByTagName(ididea)[0]) {
return window.opener.document.getElementsByTagName(ididea)[0];
} else if (window.parent) {
if (parent.document.getElementsByTagName(ididea)[0]) {
return parent.document.getElementsByTagName(ididea)[0];
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName(ididea)[0]) {
return parent.document.getElementsByTagName(ididea)[0];
}
}
return null;
}
function ccit() {
var divs, esot=[], bodyois=null;
if (window.opener) {
if (window.opener.document.getElementsByTagName('body')[0]) {
bodyois=window.opener.document.getElementsByTagName('body')[0];
divs=window.opener.document.getElementsByTagName('div');
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
bodyois=parent.document.getElementsByTagName('body')[0];
divs=parent.document.getElementsByTagName('div');
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
bodyois=parent.document.getElementsByTagName('body')[0];
divs=parent.document.getElementsByTagName('div');
}
}
function andqlater() {
//alert('HeRe');
tdid='';
var ppig='[]', coo='', coos=[], ip=0;
var squares; //=window.opener.document.getElementsByTagName('area');
if (window.opener) {
if (window.opener.document.getElementsByTagName('body')[0]) {
squares=window.opener.document.getElementsByTagName('area');
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
squares=parent.document.getElementsByTagName('area');
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
squares=parent.document.getElementsByTagName('area');
}
}
window.addEventListener("DOMContentLoaded", () => {
const source = document.querySelector("#mg");
console.log('source.id=' + source.id);
source.addEventListener("dragstart", (ev) => {
console.log("dragStart");
// Change the source element's background color
// to show that drag has started
ev.currentTarget.classList.add("dragging");
// Clear the drag data cache (for all formats/types)
ev.dataTransfer.clearData();
// Set the drag's format and data.
// Use the event target's id for the data
ev.dataTransfer.setData("text/plain", ev.target.id);
//ev.dataTransfer.setData("text/html", ev.target.outerHTML);
});
source.addEventListener("dragend", (ev) =>
ev.target.classList.remove("dragging")
);
const target = wod('body'); //window.opener.document.getElementsByTagName('body')[0];
target.id='usemap';
console.log('target.id=' + target.id);
target.addEventListener("dragover", (ev) => {
console.log("dragOver");
ev.preventDefault();
});
target.addEventListener("drop", (ev) => {
console.log("Drop");
ev.preventDefault();
// Get the data, which is the id of the source element
const data = ev.dataTransfer.getData("text");
const source = document.getElementById(data);
… where heap memory concerns related to the global variables memory used in our GeoJson World Coastlines webpage could cause mobile platform usage reloads of the web application, reminiscent of the external Javascript concerns we had back at GeoJson World Countries SVG Overlay Safari Error Tutorial.
There, as for here, mobile usage got better by swapping global variable usage for HTML content static PHP …
GeoJson World Coastline Function Noun Naming Tutorial
We’re working on an extension to yesterday’s GeoJson World Coastline Rivers Quiz Tutorial‘s Rivers Quiz functionality within our GeoJson World Coastlines web application, and have …
settled on an approach … but …
not yet finished on deployment issues
… but it is this approach we wanted to talk about today.
Our approach borrows from Object Oriented Programming (OOP) the idea that …
just as with OOP thinking class names are like nouns and the methods within that class are like verbs … we, with our approach …
help readability of our non-OOP functional code by including those nouns and verbs, as well as ideas like use of plurals to indicate array involvement, with our Javascript function naming
… we can best illustrate to you via showing you new functions and variables and modified code to show you this approach in code …
var rivers='', arivers=[], iguess=-1, isofar=' ', jscore=0, jgoes=0, elema=null, contexta=null, rectisleft=0, rectistop=0, isokto=true;
var populations='', apopulations=[], jguess=-1, jsofar=' ', both=false, jlastn='';
var idone=false;
function askapopulation() {
var another=false;
var origboth=both;
var midbit='';
var thing='population';
if (!both) { midbit='Append spaces to also answer a question regarding the Rivers Quiz, or R to just do Rivers Quiz.'; } else { thing='river'; }
var retthis=prompt('What is the name of this new red population area plotted on the world map? ' + midbit + ' Enter ? to get given more time looking at (longitude,latitude) = (' + apopulations[jguess].split(':')[1].split(',')[0] + ',' + apopulations[jguess].split(':')[1].split(',')[1] + ')', '');
if (retthis == null) {
both=false;
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (retthis.toLowerCase().trim() == 'r') {
both=false;
isokto=true;
getariver();
return '';
} else if (retthis.trim() == '?') {
if (retthis.trim() != retthis && !origboth) { both=true; }
setTimeout(askapopulation, 9000);
return '';
} else if (retthis.trim() == '') {
if (retthis != '' && !origboth) { thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
if (retthis != '' && !origboth) { isokto=true; both=true; getariver(); }
} else if (jlastn.toLowerCase().indexOf(retthis.toLowerCase()) != -1 && jlastn.toLowerCase() == retthis.toLowerCase() && retthis.trim().length >= 1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (jlastn.toLowerCase().indexOf(retthis.toLowerCase()) == -1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (jlastn.toLowerCase() == retthis.toLowerCase()) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Well done! Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (retthis.trim().length >= 1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
}
if (both) { contexta.clearRect(0,0,360,180); }
if (both && !origboth) { isitok=true; getariver(); return ''; }
if (another) { if (both) { getariver(); getapopulation(); } else { getapopulation(); } } else { contexta.clearRect(0,0,360,180); }
return '';
}
function plotapopulation(which) {
if (isokto) { contexta.clearRect(0,0,360,180); }
//if (both) { isokto=true; }
jlastn=apopulations[which].split(':')[0];
var rest=apopulations[which].split(':')[1];
var restlonglat=[]; //rest.split(',');
restlonglat=rest.split(',');
if (eval('' + restlonglat.length) >= 2) {
contexta.fillStyle = 'red';
contexta.fillRect(eval(180.0 + eval('' + restlonglat[0])), eval(90.0 - eval('' + restlonglat[1])),1,1);
contexta.fill();
}
}
It’s time to turn our attention away from GeoJson World Countries, as talked about with yesterday’s GeoJson World Countries Plotted Ports Tutorial, and back to GeoJson World Coastline ideas. Why? We want to add a …
Rivers Quiz
… via the (generously provided) HTTP://geojson.xyz rivers lake centerlines GeoJSON data we download and then uploaded to become rivers.geojson data file. Now we were wondering out of …
use the URL to this GeoJSON file as the “src” attribute of an HTML iframe …
<iframe id=ifrivers onload=getthejson(this); style=display:none; src=./rivers.geojson></iframe>
… element (and then access the content via the onload event …
var rivers='', arivers=[], iguess=-1, isofar=' ', jscore=0, jgoes=0, elema=null, contexta=null, rectisleft=0, rectistop=0;
function getthejson(iois) {
var aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
rivers='' + aconto.body.innerHTML;
setTimeout(populaterivers, 500);
}
}
}
… function) would suffice, or if we would end up using …
Ajax call
… to access this data, and were a bit surprised the former method was all fine. Of course there are snazzy inbuilt Javascript hierarchical calls you can make to process the data, but we find, with GeoJSON data, in the client realm (where we’re keen to stay with today’s work (though PHP serverside can, of course, be purloined to do all this work, should you have that available)), of Javascript, we just need very basic string functions …
The progress with GeoJson World Countries helped too. We knew to add another HTML canvas layer as per …
document.body (now with the new onmousemove=airportplot(event); event logic) lowest level …
overlayed by HTML canvas element plotted with world country linework … now including …
overlayed by HTML canvas element dedicated to nearest airport plotting …
<canvas id=myacanvas height='180' width='360' style='background-color:transparent;z-index:55;display:inline-block;position:absolute;top:0px;left:0px;'></canvas>
overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay
… and to, at document.body onload logic …
// ... and extended document.body onload event logic has added, up near its top ...
elema = document.getElementById('myacanvas');
contexta = elema.getContext('2d');
… and supplement with another HTML sub “emoji button” ❓ ( ❓ ) type element …
function askariver() {
var another=false;
var retthis=prompt('What is the name of this new blue river plotted on the world map? Enter ? to get given more time looking at (longitude,latitude) = (' + arivers[iguess].split(':')[1].split(',')[0] + ',' + arivers[iguess].split(':')[1].split(',')[1] + ')', '');
if (retthis == null) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (retthis.trim() == '?') {
setTimeout(askariver, 8000);
return '';
} else if (retthis.trim() == '') {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (lastn.toLowerCase().indexOf(retthis.toLowerCase()) == -1) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (lastn.toLowerCase() == retthis.toLowerCase()) {
jgoes++;
jscore++;
another=confirm('Well done! Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (retthis.trim().length >= 1) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else {
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
}
if (another) { getariver(); } else { contexta.clearRect(0,0,360,180); }
return '';
}
… to work the Rivers Quiz. Finally, though, for all good practicalities we also need those zoom logics out of GeoJson World Countries logic, via “emoji button” 🔎 ( 🔎 ) …
where to modularise … we think “data collection” commonality is a good reason, and so we make these changes to intair.php
making an (“animated emoji”) button dual purpose on top of originally being a single purpose button …
<sub title='Show Nearby Airports' onclick='doair=how(true,this); twothousand*=2; this.title=this.title.substring(0,4) + String.fromCharCode(105) + String.fromCharCode(110) + String.fromCharCode(103) + this.title.replace(this.title.split(String.fromCharCode(32))[0] + String.fromCharCode(32), String.fromCharCode(32));' data-type=9992 style=cursor:pointer; id=portsub>✈</sub>
… working with the intairsuffix global variable that could add a new GET argument where both the “port” label in &port=[value] and that [value] can affect behaviour from the intair.php PHP helper tool above …
var intairsuffix='', zhra=null, zhrb=null, kklat=0, kklong=0, doair=false, vsll=[-999.0], answered=true;
function how(atr, isub) {
var curgd=isub.getAttribute('data-type');
if (('' + curgd) == '9992') { // airport
if (intairsuffix != '') {
intairsuffix='&port=air';
isub.title='Showing Nearby Airports and Ports';
document.getElementById('title').value='Nearby Timezone Places and Airports and Ports';
}
} else if (('' + curgd) == '128674') { // port
if (intairsuffix == '') {
if (doair) {
intairsuffix='&port=air';
isub.title='Showing Nearby Airports and Ports';
document.getElementById('title').value='Nearby Timezone Places and Airports and Ports';
} else {
intairsuffix='&port=y';
isub.title='Showing Nearby Ports';
document.getElementById('title').value='Nearby Timezone Places and Ports';
}
}
}
return true;
}
function feedhow() {
var isub=document.getElementById('portsub');
var curgd=isub.getAttribute('data-type');
if (('' + curgd) == '9992') { // airport
isub.innerHTML='🚢';
isub.setAttribute('data-type', '128674');
} else if (('' + curgd) == '128674') { // port
isub.innerHTML='✈';
isub.setAttribute('data-type', '9992');
}
}
Ajax asynchronous usage for second half of a synchronous previous usage …
var intairsuffix='', zhra=null, zhrb=null, kklat=0, kklong=0, doair=false, vsll=[-999.0], answered=true;
function stateChangedb() {
if (zhrb.readyState == 4) {
if (zhrb.status == 200) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
var onepixelequals=izoom;
var bts=zhrb.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh<bts.length; ijh++) {
var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
if (!inarray(vs,vsll)) {
vsll.push(vs);
drawaac(
eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
);
}
}
}
}
}
}
function stateChangeda() {
if (zhra.readyState == 4) {
if (zhra.status == 200) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
var onepixelequals=izoom;
var bts=zhra.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh<bts.length; ijh++) {
var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
if (!inarray(vs,vsll)) {
vsll.push(vs);
drawaac(
eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
);
}
}
}
if (intairsuffix.indexOf('&port=air') != -1) {
zhrb = new XMLHttpRequest();
zhrb.onreadystatechange=stateChangedb;
zhrb.open('get', '/HTMLCSS/intair.php?num=6&lat=' + kklat + '&long=' + kklong + '&port=y', true);
zhrb.send(null);
}
answered=true;
}
}
}
… keeps a fastish synchronous call (that we enforce via that answered global variable) but truely invokes an asynchronous arrangement extracting Nearby Ports data to plot, as applicable
Introducing the Map Chart recognition of nearby Airports with yesterday’s GeoJson World Countries Nearest Airports Tutorial‘s progress on our latest GeoJson World Countries PHP web application, it set us to seeing …
the combination of Google Directions‘s talents allowing you to reposition on the fly … and …
the onmousemove event, at least for our non-mobile users
… could mean that if we pre-plot airports on the world map, given that the user has clicked the ✈ ( ✈ ) “Show an Interest in Airports” emoji button, as a non-mobile user hovers over the world map, this pre-plotting might help trip planners with their travel options, should air travel be part of their interest, in the same way it is an option up at Google Directions in our changedcountries.phpweb application you can also try below. The overlay scenario now reads …
document.body (now with the new onmousemove=airportplot(event); event logic) lowest level …
overlayed by HTML canvas element plotted with world country linework … now including …
overlayed by HTML canvas element dedicated to nearest airport plotting …
<canvas id=myacanvas height='180' width='360' style='background-color:transparent;z-index:55;display:inline-block;position:absolute;top:0px;left:0px;'></canvas>
overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay
… worked by new (sometimes Ajax) Javascript code …
function inarray(needle, haystack) { // thanks to https://stackoverflow.com/questions/784012/javascript-equivalent-of-phps-in-array
var length = haystack.length;
for (var i = 0; i < length; i++) {
if (haystack[i] == needle) return true;
}
return false;
}
function stateChangeda() {
if (zhra.readyState == 4) {
if (zhra.status == 200) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
var onepixelequals=izoom;
var bts=zhra.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh<bts.length; ijh++) {
var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
if (!inarray(vs,vsll)) {
vsll.push(vs);
drawaac(
eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
);
}
}
}
answered=true;
}
}
}
function naira(klat, klong) {
if (answered && doair) {
answered=false;
zhra = new XMLHttpRequest();
zhra.onreadystatechange=stateChangeda;
zhra.open('get', '/HTMLCSS/intair.php?num=6&lat=' + klat + '&long=' + klong, true);
zhra.send(null);
}
}
function airportplot(e) {
if (answered) {
var rectis=null; //document.body.getBoundingClientRect();
var blat=0, blong=0;
var topllong=-180.0;
var topllat=90.0;
onepixelequals=eval(0.0 + eval(1.0 * izoom));
e = e || window.event;
e.preventDefault();
if (e.touches) {
if (e.touches[0].pageX) {
naira(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY)); //if (drawac(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY))) e = e; }
} else {
rectis=document.body.getBoundingClientRect();
naira(eval(-rectis.left + e.touches[0].clientX), eval(-rectis.top + e.touches[0].clientY)); //if (drawac(eval(-rectis.left + e.touches[0].clientX), eval(-rectis.top + e.touches[0].clientY))) { e = e; }
}
} else if (e.pageX || e.pageY) {
blat=eval(eval(eval(topllat * onepixelequals - eval(-rectistop + e.pageY) * 1)) / onepixelequals);
blong=eval(eval(eval(topllong * onepixelequals + eval(-rectisleft + e.pageX) * 1)) / onepixelequals);
if ((blat >= -90.0 && blat <= 90.0) && (blong >= -180.0 && blong <= 180.0)) {
naira(blat, blong); //if (drawac(eval(-rectisleft + e.pageX), eval(-rectistop + e.pageY))) { e = e; }
}
} else {
rectis=document.body.getBoundingClientRect();
naira(eval(-rectis.left + e.clientX), eval(-rectis.top + e.clientY)); //if (drawac(eval(-rectis.left + e.clientX), eval(-rectis.top + e.clientY))) { e = e; }
}
}
}
// ... and extended document.body onload event logic has added, up near its top ...
elema = document.getElementById('myacanvas');
contexta = elema.getContext('2d');
add interfacing functionality to the excellent Google Directions part of Google Maps, perhaps to help with Trip planning, or even just to associate a Placename with a latitude and longitude as clicked by the user, via the very simple URL arrangement … https://www.google.com/maps/dir/[decimalLatitudeDegrees]/[decimalLongitudeDegrees]
… helped out by new Javascript functions …
var lastlats=[], lastlongs=[], lastlat=-99.0, lastlong=-99.0, thislat=0.0, thislong=0.0;
function preface(inblurb) {
var extras='';
var outblurb=inblurb;
if (Math.abs(eval('' + lastlat)) > 0.0 || Math.abs(eval('' + lastlong)) > 0.0) {
if (Math.abs(eval('' + lastlat)) <= 90.0 && Math.abs(eval('' + lastlong)) <= 180.0) {
extras=' Add G for Google Directions between (' + lastlat + ',' + lastlong + ') to (' + thislat + ',' + thislong + ') and spaces (also more trip legs) to hashtag navigate to Google Charts later. ';
}
}
return extras + outblurb;
}
function alats(inlat) {
if (inlat == 0 && lastlats.length == 0) { inlat=inlat; } else { lastlats.push(inlat); }
return inlat;
}
Onto yesterday’s GeoJson World Countries TimeZone Times Tutorial GeoJson World Countries web application’s capabilities we want to add zooming, that doesn’t rely on web browser functionality (which continues to work). With that in mind we create a new emoji ( 🔎 ) 🔎 link, with this onclick event code …
var jzoom=1.0, izoom=location.search.split('zoom=')[1] ? eval(decodeURIComponent(location.search.split('zoom=')[1].split('&')[0])) : 1.0;
… to multiply the webpage zoom factor in a programmatical way. To acheive this, we have a two way approach (as you might have surmised from above) …
for mobile, the logic is easier by introducing a new meta name=viewport …
<meta id="myviewport" name="viewport" content="width=device-width, initial-scale=1, minimum-scale=0.1, maximum-scale=8, user-scalable=yes" >
… tag … while …
for non-mobile we needed to realize that event.pageX and event.pageY co-ordinates grow in proportion to the zoom factor, and that better latitude and longitude determining lines of code would go …
function canvasclick(e) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
onepixelequals=eval(0.0 + eval(1.0 * izoom));
//document.title='canvasclick';
e = e || window.event;
e.preventDefault();
if (e.touches) {
if (e.touches[0].pageX) {
//lastl='Longitude,Latitude coordinates are ' + eval(topllong + eval(-rectis.left + e.touches[0].pageX) * onepixelequals) + ',' + eval(topllat - eval(-rectis.top + e.touches[0].pageY) * onepixelequals);
if (drawc(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY))) {
thislat=eval(topllat - eval(-rectistop + e.touches[0].pageY) * onepixelequals);
thislong=eval(topllong + eval(-rectisleft + e.touches[0].pageX) * onepixelequals);
//console.log('rectistop=' + rectistop + ' and rectisleft=' + rectisleft + ' and rectisy=' + rectisy + ' and thislat=' + thislat);
document.getElementById('nearestif').src='/PHP/tz_places.php?place=&latitude=' + encodeURIComponent('' + eval(topllat - eval(-rectistop + e.touches[0].pageY) * onepixelequals)) + '&longitude=' + encodeURIComponent('' + eval(topllong + eval(-rectisleft + e.touches[0].pageX) * onepixelequals)) + '&ntztontz=y';
}
} else {
Some readers might be aware of our “theory regarding adverbs” and “web applications” on the net …
the most catered for adverb relates to the “where of life” … and the second banana is …
the “when of life”
… and, further to yesterday’s GeoJson World Countries SVG Overlay Safari Error Tutorial‘s emphasis on the “where of life”, today we add in a bit of the “when of life”, something right down the line of the remit of TimeZone talents.
Seriously though, a lot of us dream of the rest of the world on a world map, and wonder what time it is in other parts of the world. Phone call to relatives? A reminder SMS call? Email a game collaboration? It could all be part of life’s rich tapestry!
The expresion of this, for us, today, improving the communications with our current GeoJsom World Countries web application, take the form of emoji clocks from the 12 hour clock example forms such as …
1 o’clock is 🕐 🕐
2 o’clock is 🕑 🕑
12 o’clock is 🕛 🕛
2:30 is 🕝 🕝
11:30 is 🕦 🕦
12:30 is 🕧 🕧
… to show in “prompt” and “confirm” popup windows, as well as Map Chart maps … via new Javascript functions …
Also, in these same places we add in Time Place country ISO-2 character code based emoji flags, adding to information and colour pizazz in the informational parts to the workings of our changedcountries.phpweb application you can also try below.
On discovering our first solution theory of turning yesterday’s mapsvg.js external Javascript work into an async piece of work made no difference to this situation, we surmised that the huge amount of content held in the Javascript (ie. client side) global variable appendtoinnerHTML was causing memory issues. We couldn’t shift much to do with the overall amount of “data” needing to be handled, in order to implement country SVG colour infilling, but we could shift the data from being …
client side (external) Javascript held … to, instead, (have that data) be (determined on the) …
server side PHP filling in the contents of our (relevant) HTML div id=svgd ahead of the document.body onload event timing …
<?php
$icnt=0;
function apptohtmstuff($coordsare, $origc) {
global $icnt;
$minl=-1;
$mint=-1;
$maxl=-1;
$maxt=-1;
$zysare=explode(',', $coordsare);
$svgcis='';
for ($ij=0; $ij<sizeof($zysare); $ij+=2) {
if ($minl < 0) {
$minl=$zysare[$ij];
$maxl=$zysare[$ij];
$mint=$zysare[1 + $ij];
$maxt=$zysare[1 + $ij];
} else {
if ($zysare[$ij] < $minl) { $minl=$zysare[$ij]; }
if ($zysare[$ij] > $maxl) { $maxl=$zysare[$ij]; }
if ($zysare[1 + $ij] < $mint) { $mint=$zysare[1 + $ij]; }
if ($zysare[1 + $ij] > $maxt) { $maxt=$zysare[1 + $ij]; }
}
}
this improved web application stability …
…everywhere … and an idea we’d ditched yesterday of …
idea to pre-colour “land” parts of the world GeoJson map green (ahead of the document.body onload event) also came good (after causing problems yesterday)
… meaning now, “overlay” wise, we could say …
document.body lowest level …
overlayed by HTML canvas element plotted with world country linework …
overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay
… and we (reckon we’ve) improved the colour coding user experience at the same time.
We noticed that tweaks in the changed …
var lastflagged='', appendtoinnerHTML='', waitplease=false; // used to make sure "area" element onclick code precedes any document.body onmousedown or ontouchdown code
… also lessened the burden on the client side by only asking any Javascript DOM command operations act on single HTML element at a time, not a whole swathe of hosted ones, in any operation.
What deducible data item needs to be determined for these Geo Charts to work? We need a way to deduce ISO-2 character country codes from the ISO-3 character codes existing in the GeoJson “countries.geojson” data from yesterday’s work. We happened upon the extremely generous mapping data webpage to help with these ISO-2 character deductions …
… in our image map area elements PHP creation code above. As you can see, extra “intelligence”, moving forward, is contained in area element global data attributes.
Geo Chart can involve emoji (🏠 &127968;) or image (SVG) circle based symbology for the “User Clicked Place” and nearby TimeZone places respectively …
all these symbols can be clicked to open popup windows containing TimeZone Place Wikipedia webpages of relevance …
an emoji national flag (eg. Zambia “ZA” could be used to derive 🇿🇦 🇿🇦 flag emoji) derived from those ISO-2 character codes can supplement the GeoJson (more ISO-3 character based) names presented in the underlying data, in the Geo Chart title …
contextualizing the accompanying Map Chart … and …
vice versa regarding hovering over symbology (which works on Map Chart, but not Geo Chart) …
within the Map Chart iframe a “Geo” link can glean a “zoomed out” world Geo Chart view of your TimeZone places
Know your GeoJson! Yes, pretty obviously, any two GeoJson datasets might display the same in that “map plotting” sense, but one might have different and/or more “intelligence” than the other. Often, an XML has more “intelligence” than equivalent HTML (barring the use of global data attributes, that is), as today’s Corollacorollary.
Luckily for programmers all over, the organization of TimeZones has had an International flavour in its development and maintenance. As such, given the “purely coastline” GeoJson data involved in our fledgling PHP web application of yesterday’s GeoJson World Coastline Primer Tutorial a useful arrangement for improvement involves …
document.body onclick event co-ordinates … able to be converted to …
longitude, latitude (easily, only because of our simplistic map projection, of course) … onfed to …
… can have us helping out your curious web “clicking” user with the 3 nearest TimeZone places, as a reference as to where they are “clicking” in the world.
in a discrete click methodology of interest, you could adopt a non-mobile “onmousedown” logic set that does not get interfered with by a mobile “ontouchdown” logic set (perhaps leaving “onclick” event, which both non-mobile and mobile both recognise, for another event logic role) … and …
neither will interrupt the mobile gestures associated with swiping and pinching, which refer to the events “ontouchstart” and “ontouchend” at either end of their lifespan
And so, we arrive at a long planned for tilt at Image Map functionality that we often turn to Mobilefish.Com and its excellent Image Map Creation to help us out … but not today?! Why not? We have a funny set of needs, they being …
our Image Map’s image will have a variable set of width x height dimensions …
our Image Map’s image will be transparent
our Image Map needs to have a hole left aside inside it where the functionality that originally existed (and pointed to WordPress Blog content like you are reading), is still working
Google Charts Geo Chart Region Picker Alignment Tutorial
In our inhouse Region Picker we like the way the right hand webpage side involved some useful …
position: sticky;
… positioning, useful within the realms of what is going on on the right hand side.
But now we’re wanting some horizontal integration, linking commonalities of left and right regarding the country of interest, as a user’s eyes scan across the webpage, at pivotal times.
… all feature the great [element].scrollIntoView() way to programmatically scroll, rather than use (the sometimes ignored) window.scrollTo([left],[top]); or location.href=’#[elementID]’; approaches. It has the advantage of not altering location.hash (if you see that as a plus). We’d rather not add complexity, so, yes, we prefer the [aCountryElement].scrollIntoView(); call approach, at those pivotal times. This accounts for the left hand side of the webpage agreeing to the right hand side’s vertical scroll position. But what about the other way around? There, we started applying a CSS padding-top property to relevant right hand side elements (via DOM [element].style.paddingTop=[pixelsToExtendDown]px;), at pivotal points in the programming flow.
And on double clicking a right hand side button to start creating those background Geo Chart SVG based “images” we turn on a progress cursor, at least for non-mobile platforms, because the user needs patience here, in the changedregions_via_countries.htmlRegion Picker.
background-image:url(‘data:image/svg+xml;utf8, blahde blah ‘); nor background-image:url(‘data:image/svg+xml;base64, blahde blah ‘); ideas were not working for us … but today, we started to try …
overlay HTML div position:absolute; opacity:0.5; z-index:-4; …
We can’t remember a “foreground overlay” scenario so resembling a “background image feeling” end result, the transparent colour introduced into the Google ChartsGeo ChartSVG being crucial to help make this all work.
We want to be able to control the way a Google Charts Geo Chart can be nested within an HTML div element, for instance. We started the day wanting to be able to make …
a Google Charts Geo Chart be a background image to a div element … alas, on this first draft we couldn’t get there (but will continue with the research here) … whereas we succeeded …
adding the Google Charts Geo Chart interfacer’s resultant SVG data as the innerHTML (ie. content) …
<?php echo ”
function newbackin() {
if (dmyxhr.readyState == 4) {
if (dmyxhr.status == 200) {
if (dmyxhr.responseText) {
var m_t='image/jpeg';
var h_t='179';
var w_t='320';
var dbits = dmyxhr.responseText.split('\"height\": ');
if (dbits.length > 1) {
h_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"mime_type\": \"');
if (dbits.length > 1) {
m_t=dbits[1].split('\"')[0];
}
dbits = dmyxhr.responseText.split('\"width\": ');
if (dbits.length > 1) {
w_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"data\":');
dbits = dmyxhr.responseText.split('\"data\":');
if (dbits.length > 1) {
// replace all '_' with '/' and all '-' with '+' thanks to https://stackoverflow.com/questions/757675/website-screenshots
dgsbi='<img alt=\"Blog Posting Image\" style=\"width:' + w_t + 'px;height:' + h_t + 'px;\" width=' + w_t + ' height=' + h_t + ' src=\"data:' + m_t + ';base64,' + dbits[1].split('\"')[1].split('\"')[0].replace(/\_/g,'/').replace(/\-/g,'+') + '\"></img>';
//alert('dgsbi=' + dgsbi);
}
}
}
}
}
function ajaxit(urlin) {
if (urlin.length > 0) {
aurl=urlin;
if (window.XMLHttpRequest) {
dmyxhr = new window.XMLHttpRequest;
}
else {
try {
dmyxhr = new ActiveXObject('Msxml2.XMLHTTP');
} catch (othermicrosoft) {
try {
dmyxhr = new ActiveXObject('Microsoft.XMLHTTP');
} catch (failed) {
dmyxhr = false;
}
}
}
var xurl = 'https://www.googleapis.com/pagespeedonline/v1/runPagespeed?url=' + encodeURIComponent(urlin) + '&screenshot=true';
if (dmyxhr) {
dmyxhr.onreadystatechange = newbackin;
dmyxhr.open('GET', xurl, true);
dmyxhr.send(null);
}
}
}
function wbtoa(instris) {
var outstris=instris;
while (outstris.indexOf(String.fromCharCode(10)) != -1) {
outstris=outstris.replace(String.fromCharCode(10),'');
}
var xzs=prompt(outstris, outstris);
return outstris.replace(/\\\"/g, \"'\");
}
In that Nearest Places form part of the TimeZone places webpage, we noticed that even when shaping to enter a TimeZone Placename in the first textbox we made no attempt to fill in …
latitude
longitude
… when we have the information to do so, and even if we’re misunderstanding a placename designation, that should not stop us from trying, because those two numerical fields above can be corrected, and the form resubmitted, in these scenarios.
Apart from making a PHP derived Javascript variable be made available to the code, we “wrap” …
GeoJson World Countries Drag and Drop Makeover Nuance Tutorial
Nuance alert! We’re not sure if you noticed, but if you tried out the Drag and Drop functionality in the World Countries web application of yesterday’s GeoJson World Countries Drag and Drop Makeover Tutorial you may have noticed …
for a country with lots of TimeZone places, like Brazil, you could get a decent Google Chart Geo Chart map up … but if you were to click the “Map?” link down the bottom of that iframe …
it would fail to show a Google Chart Map Chart for that country’s TimeZone places
This fix, believe it or not, is interesting, perhaps only in an “internal use only” sense, we grant you. But our solution got us delving even more into hashtagging data, so that the solution we came up with was a hybrid whereby …
stayed with PHP $_GET[] (ie. address bar ? and & argumented) data (versus using PHP $_POST[] methodologies) … but …
where it came to the &data=[most of the data] part, other than its first character, we hashtagged the rest
… so that the logic flows as per usual, but we intervene at places and flesh it out via location.hash (client side only) means. We won’t bore you too much with all the places of intervention except the receiving map.php’s “easiest to get” intervention …
In addition to the Wikipedia information, at the very least, presented following a successful drag and drop operation, from today, we also start presenting a new HTML iframe element containing …
Nearest TimeZone places along with Google Charts for each unique country involved
we wanted the first popup window content be aligned to the top and left … and then …
we thought it would be good to also, in “Drag and Drop land”, relevant countries nearby to the user’s drop point TimeZone Places be showing below that (and it panned out the best way to show this, for us, was via an iframe pointing at another incarnation of the tz_places.php webpage, because it could have GET arguments iso, iso2, iso3 etcetera to point at ISO 2 letter country codes, which we made more readily available (via a new data-ccglobal data attribute applied to the select option subelements presented) for the changed external Javascript countries.js we decided should get into the mix via a new Javascript function …
function tzagain(inhtml) {
var outhtml=inhtml, dccs=[], getarg='?', theone=1;
if (inhtml.indexOf('left:0px;') != -1 && inhtml.indexOf(' data-cc=') != -1) {
dccs=inhtml.split(' data-cc=');
for (var ii=1; ii<dccs.length; ii++) {
if (getarg == '?') {
getarg+='iso=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2);
theone++;
} else if (getarg.indexOf('=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2)) == -1) {
getarg+='&iso' + theone + '=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2);
theone++;
}
}
if (getarg != '?') {
if (inhtml.indexOf('</bo' + 'ody>') != -1) {
outhtml=inhtml.split('</bo' + 'dy>')[0] + '<iframe src="./tz_places.php' + getarg + '" style="position:absolute;left:0px;top:120px;width:100%;height:900px;"></iframe></body></html>';
} else {
outhtml=inhtml + '<iframe src="./tz_places.php' + getarg + '" style="position:absolute;left:0px;top:120px;width:100%;height:900px;"></iframe>';
}
}
}
return outhtml;
}
… that superfluous looking ?rand=blah measure being pretty useful really regarding getting around the cache keeping old external Javascript in its mind after changes.
Curiously, the grandparent regions.php starting off all this needed no changing! We hope you like these tweaks!
add similar drag and drop logic into our World Coastlines GeoJson web application … and along the way, also for the World Countries web application …
hold off involving the (pretty kludgy looking) vertical scrollbar of our drag and drop pin’s underlying HTML iframe …
<iframe scrolling=no frameborder=0 name=iftr id=iftr style=display:none; srcdoc="<body style=background-color:transparent;><p id=mg title='Wikipedia country page below via drag and drop to world map' draggable='true'>📍</p><br><br><div id=myh1></div><script type='text/javascript' src='./countries.js?rand=321156747657' defer></script></body>" data-src=></iframe>
… until the first drag operation starting, calls on …
parent.document.getElementById('iftr').scrolling='yes';
… proving a Javascript DOM control of the “scrolling” attribute works (as we weren’t sure, having never done this before)
It’s worth beavering away at a guinea pig web application until (just about complete) satisfaction (for now) before having a parallel set of code changes happening simultaneously, we’ve always found.
So, what happened in “external Javascript land”? No need for a “regions.js” here, as parent.document.URL can be scrutinised in that “external Javascript land” to discover which web application is the parent, and act accordingly. So changed were our changed external Javascript countries.js in …
our GeoJson World Countries web application Drag and Drop logic worked on an iPhone … but …
our GeoJson World Countries web application Drag and Drop logic did not work on an iPad
They’re both iOS! And usually the smaller iPhone has the problem and the larger iPad is okay when there is an odd scenario happening. So, what gives? Well, the odd thing is, it was just rearrangements of code and iframe srcdoc usage …
<iframe frameborder=0 name=iftr id=iftr style=display:none; srcdoc="<body style=background-color:transparent;><p id=mg title='Wikipedia country page below via drag and drop to world map' draggable='true'>📍</p><br><br><div id=myh1></div><script type='text/javascript' src='./countries.js?rand=321156747657' defer></script></body>" data-src=></iframe>
… that ended up helping us fix the issues. Figure this, on iPad our emoji pin could not be made visible down the bottom left of iPad screen but could be made to work in the title elements section?! Of course, we might have been having a bad day, but in our defence, even debugging in Safari via …
iPad Safari web browser invocation …
Apple white lead from iPad to MacBook Air …
MacBook Air Safari web browser Develop menu dropdown got us to debugging functionality
“long hover” (ie. on non-mobile, wait for a long while after the onmouseover event initiation to see whether the user is still hovering) … and today, a bit like that, is the new, for us …
“long drag” (the user waits a long time between the drag initialization and the drop event)
… and because we found “dawdling” on a drag and drop fairly unnatural, we think this “long drag” idea “has legs”, in that it works well as a deliberate act made by a user, knowing at the end they benefit from their actions. For us, with our GeoJSON Map web application, yesterday, with GeoJson World Drag and Drop Pin Tutorial, the drag and drop led to …
Wikipedia country information webpage … and today, we allow a “long drag” get you to …
Google Maps drop position information webpage … if the “long drag” is for 10 or more seconds …
Google Earth drop position information webpage … if the “very long drag” is for 20 or more seconds
onclick event logic … and today, we start to also include …
drag and drop event logic (like, but nuanced as explained below, the experimental drag and drop ideas included in the recent Planet Moon Game Tutorial) … the nuanced differences involving …
the drag part of the events occurs in an iframe (populated via small amount of srcdoc HTML and Javascript) able to reference its originator via window.parent …
drop part of the events occurs in that originator parent and so several Javascript function called by the child reside in the parent … and …
the child “drag” event controller uses the new external Javascript countries.js …
// countries.js
// July, 2023
// RJM Programming
// Help out countries.html and countries.php
var pos3=0, pos4=0, tdid='', poligono, punto, coone='', prectis;
// var poligono = [[2,9],[8,6],[12,10],[15,2],[10,4],[5,1]];
// var punto = [6, 5];
function pointInPolygon(polygon, point) { // thanks to https://community.appinventor.mit.edu/t/geofence-check-if-a-point-is-inside-a-polygon-javascript-map/57091
var odd = false;
for (var i = 0, j = polygon.length - 1; i < polygon.length; i++) {
if (((polygon[i][1] > point[1]) !== (polygon[j][1] > point[1]))
&& (point[0] < ((polygon[j][0] - polygon[i][0]) * (point[1] - polygon[i][1]) / (polygon[j][1] - polygon[i][1]) + polygon[i][0]))) {
odd = !odd;
}
j = i;
}
return odd;
}
function andlaterstill() {
if (9 == 6) { // temporary
if (tdid != '') {
document.getElementById(tdid).innerHTML=document.getElementById(tdid).innerHTML.substring(0,1);
} else if (document.getElementById('mytable').innerHTML.indexOf(clonedatatwo) != '') {
document.getElementById('myh1').innerHTML=document.getElementById('myh1').innerHTML.split('</table>')[0] + '</table>';
}
if (document.getElementsByTagName('div')[0].innerHTML.indexOf(clonedatatwo) != -1) {
document.getElementsByTagName('div')[0].innerHTML=document.getElementsByTagName('div')[0].innerHTML.replace(clonedatatwo,'');
} else if (document.getElementsByTagName('div')[0].innerHTML.indexOf(clonedatatwo.replace('dragging','')) != -1) {
document.getElementsByTagName('div')[0].innerHTML=document.getElementsByTagName('div')[0].innerHTML.replace(clonedatatwo.replace('dragging',''),'');
} else if (document.body.innerHTML.split('<table')[0].indexOf(clonedatatwo.replace('dragging','')) != -1) {
document.body.innerHTML=document.body.innerHTML.replace(document.body.innerHTML.split('<table')[0], document.body.innerHTML.split('<table')[0].replace(clonedatatwo.replace('dragging',''),''));
} else if (document.body.innerHTML.split('<table')[0].indexOf(clonedatatwo) != -1) {
document.body.innerHTML=document.body.innerHTML.replace(document.body.innerHTML.split('<table')[0], document.body.innerHTML.split('<table')[0].replace(clonedatatwo,''));
}
}
tdid='';
}
function getprectis() {
if (window.opener) {
if (window.opener.document.getElementsByTagName('body')[0]) {
return window.opener.document.body.getBoundingClientRect();
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
return parent.document.body.getBoundingClientRect();
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
return parent.document.body.getBoundingClientRect();
}
}
return null;
}
function wod(ididea) {
if (window.opener) {
if (window.opener.document.getElementsByTagName(ididea)[0]) {
return window.opener.document.getElementsByTagName(ididea)[0];
} else if (window.parent) {
if (parent.document.getElementsByTagName(ididea)[0]) {
return parent.document.getElementsByTagName(ididea)[0];
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName(ididea)[0]) {
return parent.document.getElementsByTagName(ididea)[0];
}
}
return null;
}
function ccit() {
var divs, esot=[], bodyois=null;
if (window.opener) {
if (window.opener.document.getElementsByTagName('body')[0]) {
bodyois=window.opener.document.getElementsByTagName('body')[0];
divs=window.opener.document.getElementsByTagName('div');
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
bodyois=parent.document.getElementsByTagName('body')[0];
divs=parent.document.getElementsByTagName('div');
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
bodyois=parent.document.getElementsByTagName('body')[0];
divs=parent.document.getElementsByTagName('div');
}
}
function andqlater() {
//alert('HeRe');
tdid='';
var ppig='[]', coo='', coos=[], ip=0;
var squares; //=window.opener.document.getElementsByTagName('area');
if (window.opener) {
if (window.opener.document.getElementsByTagName('body')[0]) {
squares=window.opener.document.getElementsByTagName('area');
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
squares=parent.document.getElementsByTagName('area');
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
squares=parent.document.getElementsByTagName('area');
}
}
window.addEventListener("DOMContentLoaded", () => {
const source = document.querySelector("#mg");
console.log('source.id=' + source.id);
source.addEventListener("dragstart", (ev) => {
console.log("dragStart");
// Change the source element's background color
// to show that drag has started
ev.currentTarget.classList.add("dragging");
// Clear the drag data cache (for all formats/types)
ev.dataTransfer.clearData();
// Set the drag's format and data.
// Use the event target's id for the data
ev.dataTransfer.setData("text/plain", ev.target.id);
//ev.dataTransfer.setData("text/html", ev.target.outerHTML);
});
source.addEventListener("dragend", (ev) =>
ev.target.classList.remove("dragging")
);
const target = wod('body'); //window.opener.document.getElementsByTagName('body')[0];
target.id='usemap';
console.log('target.id=' + target.id);
target.addEventListener("dragover", (ev) => {
console.log("dragOver");
ev.preventDefault();
});
target.addEventListener("drop", (ev) => {
console.log("Drop");
ev.preventDefault();
// Get the data, which is the id of the source element
const data = ev.dataTransfer.getData("text");
const source = document.getElementById(data);
… where heap memory concerns related to the global variables memory used in our GeoJson World Coastlines webpage could cause mobile platform usage reloads of the web application, reminiscent of the external Javascript concerns we had back at GeoJson World Countries SVG Overlay Safari Error Tutorial.
There, as for here, mobile usage got better by swapping global variable usage for HTML content static PHP …
GeoJson World Coastline Function Noun Naming Tutorial
We’re working on an extension to yesterday’s GeoJson World Coastline Rivers Quiz Tutorial‘s Rivers Quiz functionality within our GeoJson World Coastlines web application, and have …
settled on an approach … but …
not yet finished on deployment issues
… but it is this approach we wanted to talk about today.
Our approach borrows from Object Oriented Programming (OOP) the idea that …
just as with OOP thinking class names are like nouns and the methods within that class are like verbs … we, with our approach …
help readability of our non-OOP functional code by including those nouns and verbs, as well as ideas like use of plurals to indicate array involvement, with our Javascript function naming
… we can best illustrate to you via showing you new functions and variables and modified code to show you this approach in code …
var rivers='', arivers=[], iguess=-1, isofar=' ', jscore=0, jgoes=0, elema=null, contexta=null, rectisleft=0, rectistop=0, isokto=true;
var populations='', apopulations=[], jguess=-1, jsofar=' ', both=false, jlastn='';
var idone=false;
function askapopulation() {
var another=false;
var origboth=both;
var midbit='';
var thing='population';
if (!both) { midbit='Append spaces to also answer a question regarding the Rivers Quiz, or R to just do Rivers Quiz.'; } else { thing='river'; }
var retthis=prompt('What is the name of this new red population area plotted on the world map? ' + midbit + ' Enter ? to get given more time looking at (longitude,latitude) = (' + apopulations[jguess].split(':')[1].split(',')[0] + ',' + apopulations[jguess].split(':')[1].split(',')[1] + ')', '');
if (retthis == null) {
both=false;
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (retthis.toLowerCase().trim() == 'r') {
both=false;
isokto=true;
getariver();
return '';
} else if (retthis.trim() == '?') {
if (retthis.trim() != retthis && !origboth) { both=true; }
setTimeout(askapopulation, 9000);
return '';
} else if (retthis.trim() == '') {
if (retthis != '' && !origboth) { thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
if (retthis != '' && !origboth) { isokto=true; both=true; getariver(); }
} else if (jlastn.toLowerCase().indexOf(retthis.toLowerCase()) != -1 && jlastn.toLowerCase() == retthis.toLowerCase() && retthis.trim().length >= 1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (jlastn.toLowerCase().indexOf(retthis.toLowerCase()) == -1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (jlastn.toLowerCase() == retthis.toLowerCase()) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Well done! Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (retthis.trim().length >= 1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
}
if (both) { contexta.clearRect(0,0,360,180); }
if (both && !origboth) { isitok=true; getariver(); return ''; }
if (another) { if (both) { getariver(); getapopulation(); } else { getapopulation(); } } else { contexta.clearRect(0,0,360,180); }
return '';
}
function plotapopulation(which) {
if (isokto) { contexta.clearRect(0,0,360,180); }
//if (both) { isokto=true; }
jlastn=apopulations[which].split(':')[0];
var rest=apopulations[which].split(':')[1];
var restlonglat=[]; //rest.split(',');
restlonglat=rest.split(',');
if (eval('' + restlonglat.length) >= 2) {
contexta.fillStyle = 'red';
contexta.fillRect(eval(180.0 + eval('' + restlonglat[0])), eval(90.0 - eval('' + restlonglat[1])),1,1);
contexta.fill();
}
}
It’s time to turn our attention away from GeoJson World Countries, as talked about with yesterday’s GeoJson World Countries Plotted Ports Tutorial, and back to GeoJson World Coastline ideas. Why? We want to add a …
Rivers Quiz
… via the (generously provided) HTTP://geojson.xyz rivers lake centerlines GeoJSON data we download and then uploaded to become rivers.geojson data file. Now we were wondering out of …
use the URL to this GeoJSON file as the “src” attribute of an HTML iframe …
<iframe id=ifrivers onload=getthejson(this); style=display:none; src=./rivers.geojson></iframe>
… element (and then access the content via the onload event …
var rivers='', arivers=[], iguess=-1, isofar=' ', jscore=0, jgoes=0, elema=null, contexta=null, rectisleft=0, rectistop=0;
function getthejson(iois) {
var aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
rivers='' + aconto.body.innerHTML;
setTimeout(populaterivers, 500);
}
}
}
… function) would suffice, or if we would end up using …
Ajax call
… to access this data, and were a bit surprised the former method was all fine. Of course there are snazzy inbuilt Javascript hierarchical calls you can make to process the data, but we find, with GeoJSON data, in the client realm (where we’re keen to stay with today’s work (though PHP serverside can, of course, be purloined to do all this work, should you have that available)), of Javascript, we just need very basic string functions …
The progress with GeoJson World Countries helped too. We knew to add another HTML canvas layer as per …
document.body (now with the new onmousemove=airportplot(event); event logic) lowest level …
overlayed by HTML canvas element plotted with world country linework … now including …
overlayed by HTML canvas element dedicated to nearest airport plotting …
<canvas id=myacanvas height='180' width='360' style='background-color:transparent;z-index:55;display:inline-block;position:absolute;top:0px;left:0px;'></canvas>
overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay
… and to, at document.body onload logic …
// ... and extended document.body onload event logic has added, up near its top ...
elema = document.getElementById('myacanvas');
contexta = elema.getContext('2d');
… and supplement with another HTML sub “emoji button” ❓ ( ❓ ) type element …
function askariver() {
var another=false;
var retthis=prompt('What is the name of this new blue river plotted on the world map? Enter ? to get given more time looking at (longitude,latitude) = (' + arivers[iguess].split(':')[1].split(',')[0] + ',' + arivers[iguess].split(':')[1].split(',')[1] + ')', '');
if (retthis == null) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (retthis.trim() == '?') {
setTimeout(askariver, 8000);
return '';
} else if (retthis.trim() == '') {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (lastn.toLowerCase().indexOf(retthis.toLowerCase()) == -1) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (lastn.toLowerCase() == retthis.toLowerCase()) {
jgoes++;
jscore++;
another=confirm('Well done! Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (retthis.trim().length >= 1) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else {
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
}
if (another) { getariver(); } else { contexta.clearRect(0,0,360,180); }
return '';
}
… to work the Rivers Quiz. Finally, though, for all good practicalities we also need those zoom logics out of GeoJson World Countries logic, via “emoji button” 🔎 ( 🔎 ) …
where to modularise … we think “data collection” commonality is a good reason, and so we make these changes to intair.php
making an (“animated emoji”) button dual purpose on top of originally being a single purpose button …
<sub title='Show Nearby Airports' onclick='doair=how(true,this); twothousand*=2; this.title=this.title.substring(0,4) + String.fromCharCode(105) + String.fromCharCode(110) + String.fromCharCode(103) + this.title.replace(this.title.split(String.fromCharCode(32))[0] + String.fromCharCode(32), String.fromCharCode(32));' data-type=9992 style=cursor:pointer; id=portsub>✈</sub>
… working with the intairsuffix global variable that could add a new GET argument where both the “port” label in &port=[value] and that [value] can affect behaviour from the intair.php PHP helper tool above …
var intairsuffix='', zhra=null, zhrb=null, kklat=0, kklong=0, doair=false, vsll=[-999.0], answered=true;
function how(atr, isub) {
var curgd=isub.getAttribute('data-type');
if (('' + curgd) == '9992') { // airport
if (intairsuffix != '') {
intairsuffix='&port=air';
isub.title='Showing Nearby Airports and Ports';
document.getElementById('title').value='Nearby Timezone Places and Airports and Ports';
}
} else if (('' + curgd) == '128674') { // port
if (intairsuffix == '') {
if (doair) {
intairsuffix='&port=air';
isub.title='Showing Nearby Airports and Ports';
document.getElementById('title').value='Nearby Timezone Places and Airports and Ports';
} else {
intairsuffix='&port=y';
isub.title='Showing Nearby Ports';
document.getElementById('title').value='Nearby Timezone Places and Ports';
}
}
}
return true;
}
function feedhow() {
var isub=document.getElementById('portsub');
var curgd=isub.getAttribute('data-type');
if (('' + curgd) == '9992') { // airport
isub.innerHTML='🚢';
isub.setAttribute('data-type', '128674');
} else if (('' + curgd) == '128674') { // port
isub.innerHTML='✈';
isub.setAttribute('data-type', '9992');
}
}
Ajax asynchronous usage for second half of a synchronous previous usage …
var intairsuffix='', zhra=null, zhrb=null, kklat=0, kklong=0, doair=false, vsll=[-999.0], answered=true;
function stateChangedb() {
if (zhrb.readyState == 4) {
if (zhrb.status == 200) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
var onepixelequals=izoom;
var bts=zhrb.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh<bts.length; ijh++) {
var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
if (!inarray(vs,vsll)) {
vsll.push(vs);
drawaac(
eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
);
}
}
}
}
}
}
function stateChangeda() {
if (zhra.readyState == 4) {
if (zhra.status == 200) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
var onepixelequals=izoom;
var bts=zhra.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh<bts.length; ijh++) {
var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
if (!inarray(vs,vsll)) {
vsll.push(vs);
drawaac(
eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
);
}
}
}
if (intairsuffix.indexOf('&port=air') != -1) {
zhrb = new XMLHttpRequest();
zhrb.onreadystatechange=stateChangedb;
zhrb.open('get', '/HTMLCSS/intair.php?num=6&lat=' + kklat + '&long=' + kklong + '&port=y', true);
zhrb.send(null);
}
answered=true;
}
}
}
… keeps a fastish synchronous call (that we enforce via that answered global variable) but truely invokes an asynchronous arrangement extracting Nearby Ports data to plot, as applicable
Introducing the Map Chart recognition of nearby Airports with yesterday’s GeoJson World Countries Nearest Airports Tutorial‘s progress on our latest GeoJson World Countries PHP web application, it set us to seeing …
the combination of Google Directions‘s talents allowing you to reposition on the fly … and …
the onmousemove event, at least for our non-mobile users
… could mean that if we pre-plot airports on the world map, given that the user has clicked the ✈ ( ✈ ) “Show an Interest in Airports” emoji button, as a non-mobile user hovers over the world map, this pre-plotting might help trip planners with their travel options, should air travel be part of their interest, in the same way it is an option up at Google Directions in our changedcountries.phpweb application you can also try below. The overlay scenario now reads …
document.body (now with the new onmousemove=airportplot(event); event logic) lowest level …
overlayed by HTML canvas element plotted with world country linework … now including …
overlayed by HTML canvas element dedicated to nearest airport plotting …
<canvas id=myacanvas height='180' width='360' style='background-color:transparent;z-index:55;display:inline-block;position:absolute;top:0px;left:0px;'></canvas>
overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay
… worked by new (sometimes Ajax) Javascript code …
function inarray(needle, haystack) { // thanks to https://stackoverflow.com/questions/784012/javascript-equivalent-of-phps-in-array
var length = haystack.length;
for (var i = 0; i < length; i++) {
if (haystack[i] == needle) return true;
}
return false;
}
function stateChangeda() {
if (zhra.readyState == 4) {
if (zhra.status == 200) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
var onepixelequals=izoom;
var bts=zhra.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh<bts.length; ijh++) {
var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
if (!inarray(vs,vsll)) {
vsll.push(vs);
drawaac(
eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
);
}
}
}
answered=true;
}
}
}
function naira(klat, klong) {
if (answered && doair) {
answered=false;
zhra = new XMLHttpRequest();
zhra.onreadystatechange=stateChangeda;
zhra.open('get', '/HTMLCSS/intair.php?num=6&lat=' + klat + '&long=' + klong, true);
zhra.send(null);
}
}
function airportplot(e) {
if (answered) {
var rectis=null; //document.body.getBoundingClientRect();
var blat=0, blong=0;
var topllong=-180.0;
var topllat=90.0;
onepixelequals=eval(0.0 + eval(1.0 * izoom));
e = e || window.event;
e.preventDefault();
if (e.touches) {
if (e.touches[0].pageX) {
naira(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY)); //if (drawac(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY))) e = e; }
} else {
rectis=document.body.getBoundingClientRect();
naira(eval(-rectis.left + e.touches[0].clientX), eval(-rectis.top + e.touches[0].clientY)); //if (drawac(eval(-rectis.left + e.touches[0].clientX), eval(-rectis.top + e.touches[0].clientY))) { e = e; }
}
} else if (e.pageX || e.pageY) {
blat=eval(eval(eval(topllat * onepixelequals - eval(-rectistop + e.pageY) * 1)) / onepixelequals);
blong=eval(eval(eval(topllong * onepixelequals + eval(-rectisleft + e.pageX) * 1)) / onepixelequals);
if ((blat >= -90.0 && blat <= 90.0) && (blong >= -180.0 && blong <= 180.0)) {
naira(blat, blong); //if (drawac(eval(-rectisleft + e.pageX), eval(-rectistop + e.pageY))) { e = e; }
}
} else {
rectis=document.body.getBoundingClientRect();
naira(eval(-rectis.left + e.clientX), eval(-rectis.top + e.clientY)); //if (drawac(eval(-rectis.left + e.clientX), eval(-rectis.top + e.clientY))) { e = e; }
}
}
}
// ... and extended document.body onload event logic has added, up near its top ...
elema = document.getElementById('myacanvas');
contexta = elema.getContext('2d');
add interfacing functionality to the excellent Google Directions part of Google Maps, perhaps to help with Trip planning, or even just to associate a Placename with a latitude and longitude as clicked by the user, via the very simple URL arrangement … https://www.google.com/maps/dir/[decimalLatitudeDegrees]/[decimalLongitudeDegrees]
… helped out by new Javascript functions …
var lastlats=[], lastlongs=[], lastlat=-99.0, lastlong=-99.0, thislat=0.0, thislong=0.0;
function preface(inblurb) {
var extras='';
var outblurb=inblurb;
if (Math.abs(eval('' + lastlat)) > 0.0 || Math.abs(eval('' + lastlong)) > 0.0) {
if (Math.abs(eval('' + lastlat)) <= 90.0 && Math.abs(eval('' + lastlong)) <= 180.0) {
extras=' Add G for Google Directions between (' + lastlat + ',' + lastlong + ') to (' + thislat + ',' + thislong + ') and spaces (also more trip legs) to hashtag navigate to Google Charts later. ';
}
}
return extras + outblurb;
}
function alats(inlat) {
if (inlat == 0 && lastlats.length == 0) { inlat=inlat; } else { lastlats.push(inlat); }
return inlat;
}
Onto yesterday’s GeoJson World Countries TimeZone Times Tutorial GeoJson World Countries web application’s capabilities we want to add zooming, that doesn’t rely on web browser functionality (which continues to work). With that in mind we create a new emoji ( 🔎 ) 🔎 link, with this onclick event code …
var jzoom=1.0, izoom=location.search.split('zoom=')[1] ? eval(decodeURIComponent(location.search.split('zoom=')[1].split('&')[0])) : 1.0;
… to multiply the webpage zoom factor in a programmatical way. To acheive this, we have a two way approach (as you might have surmised from above) …
for mobile, the logic is easier by introducing a new meta name=viewport …
<meta id="myviewport" name="viewport" content="width=device-width, initial-scale=1, minimum-scale=0.1, maximum-scale=8, user-scalable=yes" >
… tag … while …
for non-mobile we needed to realize that event.pageX and event.pageY co-ordinates grow in proportion to the zoom factor, and that better latitude and longitude determining lines of code would go …
function canvasclick(e) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
onepixelequals=eval(0.0 + eval(1.0 * izoom));
//document.title='canvasclick';
e = e || window.event;
e.preventDefault();
if (e.touches) {
if (e.touches[0].pageX) {
//lastl='Longitude,Latitude coordinates are ' + eval(topllong + eval(-rectis.left + e.touches[0].pageX) * onepixelequals) + ',' + eval(topllat - eval(-rectis.top + e.touches[0].pageY) * onepixelequals);
if (drawc(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY))) {
thislat=eval(topllat - eval(-rectistop + e.touches[0].pageY) * onepixelequals);
thislong=eval(topllong + eval(-rectisleft + e.touches[0].pageX) * onepixelequals);
//console.log('rectistop=' + rectistop + ' and rectisleft=' + rectisleft + ' and rectisy=' + rectisy + ' and thislat=' + thislat);
document.getElementById('nearestif').src='/PHP/tz_places.php?place=&latitude=' + encodeURIComponent('' + eval(topllat - eval(-rectistop + e.touches[0].pageY) * onepixelequals)) + '&longitude=' + encodeURIComponent('' + eval(topllong + eval(-rectisleft + e.touches[0].pageX) * onepixelequals)) + '&ntztontz=y';
}
} else {
Some readers might be aware of our “theory regarding adverbs” and “web applications” on the net …
the most catered for adverb relates to the “where of life” … and the second banana is …
the “when of life”
… and, further to yesterday’s GeoJson World Countries SVG Overlay Safari Error Tutorial‘s emphasis on the “where of life”, today we add in a bit of the “when of life”, something right down the line of the remit of TimeZone talents.
Seriously though, a lot of us dream of the rest of the world on a world map, and wonder what time it is in other parts of the world. Phone call to relatives? A reminder SMS call? Email a game collaboration? It could all be part of life’s rich tapestry!
The expresion of this, for us, today, improving the communications with our current GeoJsom World Countries web application, take the form of emoji clocks from the 12 hour clock example forms such as …
1 o’clock is 🕐 🕐
2 o’clock is 🕑 🕑
12 o’clock is 🕛 🕛
2:30 is 🕝 🕝
11:30 is 🕦 🕦
12:30 is 🕧 🕧
… to show in “prompt” and “confirm” popup windows, as well as Map Chart maps … via new Javascript functions …
Also, in these same places we add in Time Place country ISO-2 character code based emoji flags, adding to information and colour pizazz in the informational parts to the workings of our changedcountries.phpweb application you can also try below.
On discovering our first solution theory of turning yesterday’s mapsvg.js external Javascript work into an async piece of work made no difference to this situation, we surmised that the huge amount of content held in the Javascript (ie. client side) global variable appendtoinnerHTML was causing memory issues. We couldn’t shift much to do with the overall amount of “data” needing to be handled, in order to implement country SVG colour infilling, but we could shift the data from being …
client side (external) Javascript held … to, instead, (have that data) be (determined on the) …
server side PHP filling in the contents of our (relevant) HTML div id=svgd ahead of the document.body onload event timing …
<?php
$icnt=0;
function apptohtmstuff($coordsare, $origc) {
global $icnt;
$minl=-1;
$mint=-1;
$maxl=-1;
$maxt=-1;
$zysare=explode(',', $coordsare);
$svgcis='';
for ($ij=0; $ij<sizeof($zysare); $ij+=2) {
if ($minl < 0) {
$minl=$zysare[$ij];
$maxl=$zysare[$ij];
$mint=$zysare[1 + $ij];
$maxt=$zysare[1 + $ij];
} else {
if ($zysare[$ij] < $minl) { $minl=$zysare[$ij]; }
if ($zysare[$ij] > $maxl) { $maxl=$zysare[$ij]; }
if ($zysare[1 + $ij] < $mint) { $mint=$zysare[1 + $ij]; }
if ($zysare[1 + $ij] > $maxt) { $maxt=$zysare[1 + $ij]; }
}
}
this improved web application stability …
…everywhere … and an idea we’d ditched yesterday of …
idea to pre-colour “land” parts of the world GeoJson map green (ahead of the document.body onload event) also came good (after causing problems yesterday)
… meaning now, “overlay” wise, we could say …
document.body lowest level …
overlayed by HTML canvas element plotted with world country linework …
overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay
… and we (reckon we’ve) improved the colour coding user experience at the same time.
We noticed that tweaks in the changed …
var lastflagged='', appendtoinnerHTML='', waitplease=false; // used to make sure "area" element onclick code precedes any document.body onmousedown or ontouchdown code
… also lessened the burden on the client side by only asking any Javascript DOM command operations act on single HTML element at a time, not a whole swathe of hosted ones, in any operation.
What deducible data item needs to be determined for these Geo Charts to work? We need a way to deduce ISO-2 character country codes from the ISO-3 character codes existing in the GeoJson “countries.geojson” data from yesterday’s work. We happened upon the extremely generous mapping data webpage to help with these ISO-2 character deductions …
… in our image map area elements PHP creation code above. As you can see, extra “intelligence”, moving forward, is contained in area element global data attributes.
Geo Chart can involve emoji (🏠 &127968;) or image (SVG) circle based symbology for the “User Clicked Place” and nearby TimeZone places respectively …
all these symbols can be clicked to open popup windows containing TimeZone Place Wikipedia webpages of relevance …
an emoji national flag (eg. Zambia “ZA” could be used to derive 🇿🇦 🇿🇦 flag emoji) derived from those ISO-2 character codes can supplement the GeoJson (more ISO-3 character based) names presented in the underlying data, in the Geo Chart title …
contextualizing the accompanying Map Chart … and …
vice versa regarding hovering over symbology (which works on Map Chart, but not Geo Chart) …
within the Map Chart iframe a “Geo” link can glean a “zoomed out” world Geo Chart view of your TimeZone places
Know your GeoJson! Yes, pretty obviously, any two GeoJson datasets might display the same in that “map plotting” sense, but one might have different and/or more “intelligence” than the other. Often, an XML has more “intelligence” than equivalent HTML (barring the use of global data attributes, that is), as today’s Corollacorollary.
Luckily for programmers all over, the organization of TimeZones has had an International flavour in its development and maintenance. As such, given the “purely coastline” GeoJson data involved in our fledgling PHP web application of yesterday’s GeoJson World Coastline Primer Tutorial a useful arrangement for improvement involves …
document.body onclick event co-ordinates … able to be converted to …
longitude, latitude (easily, only because of our simplistic map projection, of course) … onfed to …
… can have us helping out your curious web “clicking” user with the 3 nearest TimeZone places, as a reference as to where they are “clicking” in the world.
in a discrete click methodology of interest, you could adopt a non-mobile “onmousedown” logic set that does not get interfered with by a mobile “ontouchdown” logic set (perhaps leaving “onclick” event, which both non-mobile and mobile both recognise, for another event logic role) … and …
neither will interrupt the mobile gestures associated with swiping and pinching, which refer to the events “ontouchstart” and “ontouchend” at either end of their lifespan
And so, we arrive at a long planned for tilt at Image Map functionality that we often turn to Mobilefish.Com and its excellent Image Map Creation to help us out … but not today?! Why not? We have a funny set of needs, they being …
our Image Map’s image will have a variable set of width x height dimensions …
our Image Map’s image will be transparent
our Image Map needs to have a hole left aside inside it where the functionality that originally existed (and pointed to WordPress Blog content like you are reading), is still working
… style of arrangement in the past, but we’ve found it useful recently (a couple of times) because we find we want to, more and more, use …
hashtagged data
reassembled into a …
form
method=POST
action=[URLofInterestOfPHP]
target=_self
… arrangement …
… that way keeping any window.parent and window.self and/or window.opener and window.self connections intact, and at the same time, being able to involve very long (mostly hashtagged data) URLs in the arrangements.
Let’s show you an example. In the recently talked about Google ChartsImage Chart Map Chart inhouse interfacer called image_chart.php downaways into the code it now goes …
<?php
$screenheight='0';
<br>
// ;Continent;CC1|CC2|:blLAT,blLONG,trLAT,trLONG:width,height:scblX,scblY,sctrX,sctrY
$continfo=';Europe;IS|CY|:35.16666,-27.6,67.0,33.36666:468,450:422,560,890,140'; // 53 523
$continfo.=';Australia;AU|AU|:-44,113.65,-10.26667,161.28333:600,450:422,560,866,140';
$continfo.=';Asia;FI|WS|:-14,37,81,179.9:600,450:422,560,866,140';
$continfo.=';America;GS|US|:-56,-179.9,77,-35:600,450:422,560,866,140';
$continfo.=';Africa;TF||:-35,-17,37,52:600,450:422,560,890,140';
if (isset($_GET['screenheight'])) {
$screenheight=str_replace('+',' ',urldecode($_GET['screenheight']));
}
if (isset($_POST['screenheight'])) {
$screenheight=str_replace('+',' ',urldecode($_POST['screenheight']));
}
<br>
if (isset($_GET['nothing'])) {
exit;
} else if ($screenheight != '0' && !isset($_GET['returnxytoparent']) && (!isset($_POST['returnxytoparent']) && !isset($_POST['ix']))) {
echo "<html>
<head>
<script type=text/javascript>
function tryit() {
if (('' + location.hash).indexOf('returnxytoparent=') != -1) {
document.getElementById('returnxytoparent').value=decodeURIComponent(('' + location.hash).split('returnxytoparent=')[1].split('&')[0].split('#'));
document.getElementById('submy').click();
}
}
</script>
</head>
<body onload=tryit();>
<form style=display:none; target=_self action=/PHP/GeoChart/image_chart.php method=POST>
<input type=hidden name=returnxytoparent id=returnxytoparent value=\"\"></input>
<input type=hidden name=screenheight value=" . $screenheight . "></input>
<input type=submit style=display:none; value=Submit id=submy></input>
</form>
</body>
</html>";
exit;
} else if (isset($_GET['returnxytoparent']) || (isset($_POST['returnxytoparent']) && !isset($_POST['ix']))) {
// more code to do with real returnxytoparent data follows
}
// more code follows
?>
… called from our inhouse Region Picker in this way now (where inhouse Javascript function windowdotdotopen can be thought of as window.open for these purposes) …
… setting up a two way understanding between window.opener Region Picker and window.self Google Charts Image Chart Map Chart inhouse interfacer. Using that target=_self arrangement above allows …
despite the “double dipping” transfer of hashtagged URL data (when longer than 750 characters, that is) into method=POST form data (of considerable size) …
that understanding between window.open Region Picker and window.self Google Charts Image Chart Map Chart inhouse interfacer remains intact
Yayyyyyy!
If this was interesting you may be interested in this too.
… in a variety of ways, so as to use it (as a tool) to determine whether Google Charts Image Charts are active (though unlikely, because they are deprecated)
So, if Image Charts are deprecated, why bother? As Lleyton would say … come on! We live in hope!
And we like keeping the user informed, where possible.
Curl HTTP Request Methods Querying Primer Tutorial
We’re not allowed to start a blog posting with “Did you know?” … that would be just so bad?!
But … Did you know?
That wonderful curl tool can help you … ↘️ 👩🏼💻 ↙️ … yes, you, find out the list of HTTP Request Methods available at a website of interest …
from the web server command line
user@Users-Air htdocs % curl -X OPTIONS https://www.rjmprogramming.com.au -i
HTTP/1.1 200 OK
Date: Wed, 22 May 2024 00:09:44 GMT
Server: Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1e-fips mod_bwlimited/1.4
Allow: GET,HEAD,POST,OPTIONS,TRACE
Content-Length: 0
Connection: close
Content-Type: text/html
background-image:url(‘data:image/svg+xml;utf8, blahde blah ‘); nor background-image:url(‘data:image/svg+xml;base64, blahde blah ‘); ideas were not working for us … but today, we started to try …
overlay HTML div position:absolute; opacity:0.5; z-index:-4; …
We can’t remember a “foreground overlay” scenario so resembling a “background image feeling” end result, the transparent colour introduced into the Google ChartsGeo ChartSVG being crucial to help make this all work.
We want to be able to control the way a Google Charts Geo Chart can be nested within an HTML div element, for instance. We started the day wanting to be able to make …
a Google Charts Geo Chart be a background image to a div element … alas, on this first draft we couldn’t get there (but will continue with the research here) … whereas we succeeded …
adding the Google Charts Geo Chart interfacer’s resultant SVG data as the innerHTML (ie. content) …
<?php echo ”
function newbackin() {
if (dmyxhr.readyState == 4) {
if (dmyxhr.status == 200) {
if (dmyxhr.responseText) {
var m_t='image/jpeg';
var h_t='179';
var w_t='320';
var dbits = dmyxhr.responseText.split('\"height\": ');
if (dbits.length > 1) {
h_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"mime_type\": \"');
if (dbits.length > 1) {
m_t=dbits[1].split('\"')[0];
}
dbits = dmyxhr.responseText.split('\"width\": ');
if (dbits.length > 1) {
w_t=dbits[1].split(',')[0].split(String.fromCharCode(10))[0].split('}')[0].trim();
}
dbits = dmyxhr.responseText.split('\"data\":');
dbits = dmyxhr.responseText.split('\"data\":');
if (dbits.length > 1) {
// replace all '_' with '/' and all '-' with '+' thanks to https://stackoverflow.com/questions/757675/website-screenshots
dgsbi='<img alt=\"Blog Posting Image\" style=\"width:' + w_t + 'px;height:' + h_t + 'px;\" width=' + w_t + ' height=' + h_t + ' src=\"data:' + m_t + ';base64,' + dbits[1].split('\"')[1].split('\"')[0].replace(/\_/g,'/').replace(/\-/g,'+') + '\"></img>';
//alert('dgsbi=' + dgsbi);
}
}
}
}
}
function ajaxit(urlin) {
if (urlin.length > 0) {
aurl=urlin;
if (window.XMLHttpRequest) {
dmyxhr = new window.XMLHttpRequest;
}
else {
try {
dmyxhr = new ActiveXObject('Msxml2.XMLHTTP');
} catch (othermicrosoft) {
try {
dmyxhr = new ActiveXObject('Microsoft.XMLHTTP');
} catch (failed) {
dmyxhr = false;
}
}
}
var xurl = 'https://www.googleapis.com/pagespeedonline/v1/runPagespeed?url=' + encodeURIComponent(urlin) + '&screenshot=true';
if (dmyxhr) {
dmyxhr.onreadystatechange = newbackin;
dmyxhr.open('GET', xurl, true);
dmyxhr.send(null);
}
}
}
function wbtoa(instris) {
var outstris=instris;
while (outstris.indexOf(String.fromCharCode(10)) != -1) {
outstris=outstris.replace(String.fromCharCode(10),'');
}
var xzs=prompt(outstris, outstris);
return outstris.replace(/\\\"/g, \"'\");
}
In that Nearest Places form part of the TimeZone places webpage, we noticed that even when shaping to enter a TimeZone Placename in the first textbox we made no attempt to fill in …
latitude
longitude
… when we have the information to do so, and even if we’re misunderstanding a placename designation, that should not stop us from trying, because those two numerical fields above can be corrected, and the form resubmitted, in these scenarios.
Apart from making a PHP derived Javascript variable be made available to the code, we “wrap” …
GeoJson World Countries Drag and Drop Makeover Nuance Tutorial
Nuance alert! We’re not sure if you noticed, but if you tried out the Drag and Drop functionality in the World Countries web application of yesterday’s GeoJson World Countries Drag and Drop Makeover Tutorial you may have noticed …
for a country with lots of TimeZone places, like Brazil, you could get a decent Google Chart Geo Chart map up … but if you were to click the “Map?” link down the bottom of that iframe …
it would fail to show a Google Chart Map Chart for that country’s TimeZone places
This fix, believe it or not, is interesting, perhaps only in an “internal use only” sense, we grant you. But our solution got us delving even more into hashtagging data, so that the solution we came up with was a hybrid whereby …
stayed with PHP $_GET[] (ie. address bar ? and & argumented) data (versus using PHP $_POST[] methodologies) … but …
where it came to the &data=[most of the data] part, other than its first character, we hashtagged the rest
… so that the logic flows as per usual, but we intervene at places and flesh it out via location.hash (client side only) means. We won’t bore you too much with all the places of intervention except the receiving map.php’s “easiest to get” intervention …
In addition to the Wikipedia information, at the very least, presented following a successful drag and drop operation, from today, we also start presenting a new HTML iframe element containing …
Nearest TimeZone places along with Google Charts for each unique country involved
we wanted the first popup window content be aligned to the top and left … and then …
we thought it would be good to also, in “Drag and Drop land”, relevant countries nearby to the user’s drop point TimeZone Places be showing below that (and it panned out the best way to show this, for us, was via an iframe pointing at another incarnation of the tz_places.php webpage, because it could have GET arguments iso, iso2, iso3 etcetera to point at ISO 2 letter country codes, which we made more readily available (via a new data-ccglobal data attribute applied to the select option subelements presented) for the changed external Javascript countries.js we decided should get into the mix via a new Javascript function …
function tzagain(inhtml) {
var outhtml=inhtml, dccs=[], getarg='?', theone=1;
if (inhtml.indexOf('left:0px;') != -1 && inhtml.indexOf(' data-cc=') != -1) {
dccs=inhtml.split(' data-cc=');
for (var ii=1; ii<dccs.length; ii++) {
if (getarg == '?') {
getarg+='iso=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2);
theone++;
} else if (getarg.indexOf('=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2)) == -1) {
getarg+='&iso' + theone + '=' + dccs[ii].substring(0,4).replace(/\'/g,'').replace(/\"/g,'').substring(0,2);
theone++;
}
}
if (getarg != '?') {
if (inhtml.indexOf('</bo' + 'ody>') != -1) {
outhtml=inhtml.split('</bo' + 'dy>')[0] + '<iframe src="./tz_places.php' + getarg + '" style="position:absolute;left:0px;top:120px;width:100%;height:900px;"></iframe></body></html>';
} else {
outhtml=inhtml + '<iframe src="./tz_places.php' + getarg + '" style="position:absolute;left:0px;top:120px;width:100%;height:900px;"></iframe>';
}
}
}
return outhtml;
}
… that superfluous looking ?rand=blah measure being pretty useful really regarding getting around the cache keeping old external Javascript in its mind after changes.
Curiously, the grandparent regions.php starting off all this needed no changing! We hope you like these tweaks!
add similar drag and drop logic into our World Coastlines GeoJson web application … and along the way, also for the World Countries web application …
hold off involving the (pretty kludgy looking) vertical scrollbar of our drag and drop pin’s underlying HTML iframe …
<iframe scrolling=no frameborder=0 name=iftr id=iftr style=display:none; srcdoc="<body style=background-color:transparent;><p id=mg title='Wikipedia country page below via drag and drop to world map' draggable='true'>📍</p><br><br><div id=myh1></div><script type='text/javascript' src='./countries.js?rand=321156747657' defer></script></body>" data-src=></iframe>
… until the first drag operation starting, calls on …
parent.document.getElementById('iftr').scrolling='yes';
… proving a Javascript DOM control of the “scrolling” attribute works (as we weren’t sure, having never done this before)
It’s worth beavering away at a guinea pig web application until (just about complete) satisfaction (for now) before having a parallel set of code changes happening simultaneously, we’ve always found.
So, what happened in “external Javascript land”? No need for a “regions.js” here, as parent.document.URL can be scrutinised in that “external Javascript land” to discover which web application is the parent, and act accordingly. So changed were our changed external Javascript countries.js in …
our GeoJson World Countries web application Drag and Drop logic worked on an iPhone … but …
our GeoJson World Countries web application Drag and Drop logic did not work on an iPad
They’re both iOS! And usually the smaller iPhone has the problem and the larger iPad is okay when there is an odd scenario happening. So, what gives? Well, the odd thing is, it was just rearrangements of code and iframe srcdoc usage …
<iframe frameborder=0 name=iftr id=iftr style=display:none; srcdoc="<body style=background-color:transparent;><p id=mg title='Wikipedia country page below via drag and drop to world map' draggable='true'>📍</p><br><br><div id=myh1></div><script type='text/javascript' src='./countries.js?rand=321156747657' defer></script></body>" data-src=></iframe>
… that ended up helping us fix the issues. Figure this, on iPad our emoji pin could not be made visible down the bottom left of iPad screen but could be made to work in the title elements section?! Of course, we might have been having a bad day, but in our defence, even debugging in Safari via …
iPad Safari web browser invocation …
Apple white lead from iPad to MacBook Air …
MacBook Air Safari web browser Develop menu dropdown got us to debugging functionality
“long hover” (ie. on non-mobile, wait for a long while after the onmouseover event initiation to see whether the user is still hovering) … and today, a bit like that, is the new, for us …
“long drag” (the user waits a long time between the drag initialization and the drop event)
… and because we found “dawdling” on a drag and drop fairly unnatural, we think this “long drag” idea “has legs”, in that it works well as a deliberate act made by a user, knowing at the end they benefit from their actions. For us, with our GeoJSON Map web application, yesterday, with GeoJson World Drag and Drop Pin Tutorial, the drag and drop led to …
Wikipedia country information webpage … and today, we allow a “long drag” get you to …
Google Maps drop position information webpage … if the “long drag” is for 10 or more seconds …
Google Earth drop position information webpage … if the “very long drag” is for 20 or more seconds
onclick event logic … and today, we start to also include …
drag and drop event logic (like, but nuanced as explained below, the experimental drag and drop ideas included in the recent Planet Moon Game Tutorial) … the nuanced differences involving …
the drag part of the events occurs in an iframe (populated via small amount of srcdoc HTML and Javascript) able to reference its originator via window.parent …
drop part of the events occurs in that originator parent and so several Javascript function called by the child reside in the parent … and …
the child “drag” event controller uses the new external Javascript countries.js …
// countries.js
// July, 2023
// RJM Programming
// Help out countries.html and countries.php
var pos3=0, pos4=0, tdid='', poligono, punto, coone='', prectis;
// var poligono = [[2,9],[8,6],[12,10],[15,2],[10,4],[5,1]];
// var punto = [6, 5];
function pointInPolygon(polygon, point) { // thanks to https://community.appinventor.mit.edu/t/geofence-check-if-a-point-is-inside-a-polygon-javascript-map/57091
var odd = false;
for (var i = 0, j = polygon.length - 1; i < polygon.length; i++) {
if (((polygon[i][1] > point[1]) !== (polygon[j][1] > point[1]))
&& (point[0] < ((polygon[j][0] - polygon[i][0]) * (point[1] - polygon[i][1]) / (polygon[j][1] - polygon[i][1]) + polygon[i][0]))) {
odd = !odd;
}
j = i;
}
return odd;
}
function andlaterstill() {
if (9 == 6) { // temporary
if (tdid != '') {
document.getElementById(tdid).innerHTML=document.getElementById(tdid).innerHTML.substring(0,1);
} else if (document.getElementById('mytable').innerHTML.indexOf(clonedatatwo) != '') {
document.getElementById('myh1').innerHTML=document.getElementById('myh1').innerHTML.split('</table>')[0] + '</table>';
}
if (document.getElementsByTagName('div')[0].innerHTML.indexOf(clonedatatwo) != -1) {
document.getElementsByTagName('div')[0].innerHTML=document.getElementsByTagName('div')[0].innerHTML.replace(clonedatatwo,'');
} else if (document.getElementsByTagName('div')[0].innerHTML.indexOf(clonedatatwo.replace('dragging','')) != -1) {
document.getElementsByTagName('div')[0].innerHTML=document.getElementsByTagName('div')[0].innerHTML.replace(clonedatatwo.replace('dragging',''),'');
} else if (document.body.innerHTML.split('<table')[0].indexOf(clonedatatwo.replace('dragging','')) != -1) {
document.body.innerHTML=document.body.innerHTML.replace(document.body.innerHTML.split('<table')[0], document.body.innerHTML.split('<table')[0].replace(clonedatatwo.replace('dragging',''),''));
} else if (document.body.innerHTML.split('<table')[0].indexOf(clonedatatwo) != -1) {
document.body.innerHTML=document.body.innerHTML.replace(document.body.innerHTML.split('<table')[0], document.body.innerHTML.split('<table')[0].replace(clonedatatwo,''));
}
}
tdid='';
}
function getprectis() {
if (window.opener) {
if (window.opener.document.getElementsByTagName('body')[0]) {
return window.opener.document.body.getBoundingClientRect();
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
return parent.document.body.getBoundingClientRect();
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
return parent.document.body.getBoundingClientRect();
}
}
return null;
}
function wod(ididea) {
if (window.opener) {
if (window.opener.document.getElementsByTagName(ididea)[0]) {
return window.opener.document.getElementsByTagName(ididea)[0];
} else if (window.parent) {
if (parent.document.getElementsByTagName(ididea)[0]) {
return parent.document.getElementsByTagName(ididea)[0];
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName(ididea)[0]) {
return parent.document.getElementsByTagName(ididea)[0];
}
}
return null;
}
function ccit() {
var divs, esot=[], bodyois=null;
if (window.opener) {
if (window.opener.document.getElementsByTagName('body')[0]) {
bodyois=window.opener.document.getElementsByTagName('body')[0];
divs=window.opener.document.getElementsByTagName('div');
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
bodyois=parent.document.getElementsByTagName('body')[0];
divs=parent.document.getElementsByTagName('div');
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
bodyois=parent.document.getElementsByTagName('body')[0];
divs=parent.document.getElementsByTagName('div');
}
}
function andqlater() {
//alert('HeRe');
tdid='';
var ppig='[]', coo='', coos=[], ip=0;
var squares; //=window.opener.document.getElementsByTagName('area');
if (window.opener) {
if (window.opener.document.getElementsByTagName('body')[0]) {
squares=window.opener.document.getElementsByTagName('area');
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
squares=parent.document.getElementsByTagName('area');
}
}
} else if (window.parent) {
if (parent.document.getElementsByTagName('body')[0]) {
squares=parent.document.getElementsByTagName('area');
}
}
window.addEventListener("DOMContentLoaded", () => {
const source = document.querySelector("#mg");
console.log('source.id=' + source.id);
source.addEventListener("dragstart", (ev) => {
console.log("dragStart");
// Change the source element's background color
// to show that drag has started
ev.currentTarget.classList.add("dragging");
// Clear the drag data cache (for all formats/types)
ev.dataTransfer.clearData();
// Set the drag's format and data.
// Use the event target's id for the data
ev.dataTransfer.setData("text/plain", ev.target.id);
//ev.dataTransfer.setData("text/html", ev.target.outerHTML);
});
source.addEventListener("dragend", (ev) =>
ev.target.classList.remove("dragging")
);
const target = wod('body'); //window.opener.document.getElementsByTagName('body')[0];
target.id='usemap';
console.log('target.id=' + target.id);
target.addEventListener("dragover", (ev) => {
console.log("dragOver");
ev.preventDefault();
});
target.addEventListener("drop", (ev) => {
console.log("Drop");
ev.preventDefault();
// Get the data, which is the id of the source element
const data = ev.dataTransfer.getData("text");
const source = document.getElementById(data);
… where heap memory concerns related to the global variables memory used in our GeoJson World Coastlines webpage could cause mobile platform usage reloads of the web application, reminiscent of the external Javascript concerns we had back at GeoJson World Countries SVG Overlay Safari Error Tutorial.
There, as for here, mobile usage got better by swapping global variable usage for HTML content static PHP …
GeoJson World Coastline Function Noun Naming Tutorial
We’re working on an extension to yesterday’s GeoJson World Coastline Rivers Quiz Tutorial‘s Rivers Quiz functionality within our GeoJson World Coastlines web application, and have …
settled on an approach … but …
not yet finished on deployment issues
… but it is this approach we wanted to talk about today.
Our approach borrows from Object Oriented Programming (OOP) the idea that …
just as with OOP thinking class names are like nouns and the methods within that class are like verbs … we, with our approach …
help readability of our non-OOP functional code by including those nouns and verbs, as well as ideas like use of plurals to indicate array involvement, with our Javascript function naming
… we can best illustrate to you via showing you new functions and variables and modified code to show you this approach in code …
var rivers='', arivers=[], iguess=-1, isofar=' ', jscore=0, jgoes=0, elema=null, contexta=null, rectisleft=0, rectistop=0, isokto=true;
var populations='', apopulations=[], jguess=-1, jsofar=' ', both=false, jlastn='';
var idone=false;
function askapopulation() {
var another=false;
var origboth=both;
var midbit='';
var thing='population';
if (!both) { midbit='Append spaces to also answer a question regarding the Rivers Quiz, or R to just do Rivers Quiz.'; } else { thing='river'; }
var retthis=prompt('What is the name of this new red population area plotted on the world map? ' + midbit + ' Enter ? to get given more time looking at (longitude,latitude) = (' + apopulations[jguess].split(':')[1].split(',')[0] + ',' + apopulations[jguess].split(':')[1].split(',')[1] + ')', '');
if (retthis == null) {
both=false;
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (retthis.toLowerCase().trim() == 'r') {
both=false;
isokto=true;
getariver();
return '';
} else if (retthis.trim() == '?') {
if (retthis.trim() != retthis && !origboth) { both=true; }
setTimeout(askapopulation, 9000);
return '';
} else if (retthis.trim() == '') {
if (retthis != '' && !origboth) { thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
if (retthis != '' && !origboth) { isokto=true; both=true; getariver(); }
} else if (jlastn.toLowerCase().indexOf(retthis.toLowerCase()) != -1 && jlastn.toLowerCase() == retthis.toLowerCase() && retthis.trim().length >= 1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (jlastn.toLowerCase().indexOf(retthis.toLowerCase()) == -1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (jlastn.toLowerCase() == retthis.toLowerCase()) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Well done! Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else if (retthis.trim().length >= 1) {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
another=confirm('Bad luck. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
} else {
if (retthis.trim() != retthis && !origboth) { both=true; thing='river'; }
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + jlastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new ' + thing + '?');
}
if (both) { contexta.clearRect(0,0,360,180); }
if (both && !origboth) { isitok=true; getariver(); return ''; }
if (another) { if (both) { getariver(); getapopulation(); } else { getapopulation(); } } else { contexta.clearRect(0,0,360,180); }
return '';
}
function plotapopulation(which) {
if (isokto) { contexta.clearRect(0,0,360,180); }
//if (both) { isokto=true; }
jlastn=apopulations[which].split(':')[0];
var rest=apopulations[which].split(':')[1];
var restlonglat=[]; //rest.split(',');
restlonglat=rest.split(',');
if (eval('' + restlonglat.length) >= 2) {
contexta.fillStyle = 'red';
contexta.fillRect(eval(180.0 + eval('' + restlonglat[0])), eval(90.0 - eval('' + restlonglat[1])),1,1);
contexta.fill();
}
}
It’s time to turn our attention away from GeoJson World Countries, as talked about with yesterday’s GeoJson World Countries Plotted Ports Tutorial, and back to GeoJson World Coastline ideas. Why? We want to add a …
Rivers Quiz
… via the (generously provided) HTTP://geojson.xyz rivers lake centerlines GeoJSON data we download and then uploaded to become rivers.geojson data file. Now we were wondering out of …
use the URL to this GeoJSON file as the “src” attribute of an HTML iframe …
<iframe id=ifrivers onload=getthejson(this); style=display:none; src=./rivers.geojson></iframe>
… element (and then access the content via the onload event …
var rivers='', arivers=[], iguess=-1, isofar=' ', jscore=0, jgoes=0, elema=null, contexta=null, rectisleft=0, rectistop=0;
function getthejson(iois) {
var aconto = (iois.contentWindow || iois.contentDocument);
if (aconto != null) {
if (aconto.document) { aconto = aconto.document; }
if (aconto.body != null) {
rivers='' + aconto.body.innerHTML;
setTimeout(populaterivers, 500);
}
}
}
… function) would suffice, or if we would end up using …
Ajax call
… to access this data, and were a bit surprised the former method was all fine. Of course there are snazzy inbuilt Javascript hierarchical calls you can make to process the data, but we find, with GeoJSON data, in the client realm (where we’re keen to stay with today’s work (though PHP serverside can, of course, be purloined to do all this work, should you have that available)), of Javascript, we just need very basic string functions …
The progress with GeoJson World Countries helped too. We knew to add another HTML canvas layer as per …
document.body (now with the new onmousemove=airportplot(event); event logic) lowest level …
overlayed by HTML canvas element plotted with world country linework … now including …
overlayed by HTML canvas element dedicated to nearest airport plotting …
<canvas id=myacanvas height='180' width='360' style='background-color:transparent;z-index:55;display:inline-block;position:absolute;top:0px;left:0px;'></canvas>
overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay
… and to, at document.body onload logic …
// ... and extended document.body onload event logic has added, up near its top ...
elema = document.getElementById('myacanvas');
contexta = elema.getContext('2d');
… and supplement with another HTML sub “emoji button” ❓ ( ❓ ) type element …
function askariver() {
var another=false;
var retthis=prompt('What is the name of this new blue river plotted on the world map? Enter ? to get given more time looking at (longitude,latitude) = (' + arivers[iguess].split(':')[1].split(',')[0] + ',' + arivers[iguess].split(':')[1].split(',')[1] + ')', '');
if (retthis == null) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (retthis.trim() == '?') {
setTimeout(askariver, 8000);
return '';
} else if (retthis.trim() == '') {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (lastn.toLowerCase().indexOf(retthis.toLowerCase()) == -1) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (lastn.toLowerCase() == retthis.toLowerCase()) {
jgoes++;
jscore++;
another=confirm('Well done! Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else if (retthis.trim().length >= 1) {
jgoes++;
another=confirm('Bad luck. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
} else {
jgoes++;
jscore++;
another=confirm('Will pay that. Answer was ' + lastn + '. Score ' + jscore + '/' + jgoes + '. Another go with a new river?');
}
if (another) { getariver(); } else { contexta.clearRect(0,0,360,180); }
return '';
}
… to work the Rivers Quiz. Finally, though, for all good practicalities we also need those zoom logics out of GeoJson World Countries logic, via “emoji button” 🔎 ( 🔎 ) …
where to modularise … we think “data collection” commonality is a good reason, and so we make these changes to intair.php
making an (“animated emoji”) button dual purpose on top of originally being a single purpose button …
<sub title='Show Nearby Airports' onclick='doair=how(true,this); twothousand*=2; this.title=this.title.substring(0,4) + String.fromCharCode(105) + String.fromCharCode(110) + String.fromCharCode(103) + this.title.replace(this.title.split(String.fromCharCode(32))[0] + String.fromCharCode(32), String.fromCharCode(32));' data-type=9992 style=cursor:pointer; id=portsub>✈</sub>
… working with the intairsuffix global variable that could add a new GET argument where both the “port” label in &port=[value] and that [value] can affect behaviour from the intair.php PHP helper tool above …
var intairsuffix='', zhra=null, zhrb=null, kklat=0, kklong=0, doair=false, vsll=[-999.0], answered=true;
function how(atr, isub) {
var curgd=isub.getAttribute('data-type');
if (('' + curgd) == '9992') { // airport
if (intairsuffix != '') {
intairsuffix='&port=air';
isub.title='Showing Nearby Airports and Ports';
document.getElementById('title').value='Nearby Timezone Places and Airports and Ports';
}
} else if (('' + curgd) == '128674') { // port
if (intairsuffix == '') {
if (doair) {
intairsuffix='&port=air';
isub.title='Showing Nearby Airports and Ports';
document.getElementById('title').value='Nearby Timezone Places and Airports and Ports';
} else {
intairsuffix='&port=y';
isub.title='Showing Nearby Ports';
document.getElementById('title').value='Nearby Timezone Places and Ports';
}
}
}
return true;
}
function feedhow() {
var isub=document.getElementById('portsub');
var curgd=isub.getAttribute('data-type');
if (('' + curgd) == '9992') { // airport
isub.innerHTML='🚢';
isub.setAttribute('data-type', '128674');
} else if (('' + curgd) == '128674') { // port
isub.innerHTML='✈';
isub.setAttribute('data-type', '9992');
}
}
Ajax asynchronous usage for second half of a synchronous previous usage …
var intairsuffix='', zhra=null, zhrb=null, kklat=0, kklong=0, doair=false, vsll=[-999.0], answered=true;
function stateChangedb() {
if (zhrb.readyState == 4) {
if (zhrb.status == 200) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
var onepixelequals=izoom;
var bts=zhrb.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh<bts.length; ijh++) {
var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
if (!inarray(vs,vsll)) {
vsll.push(vs);
drawaac(
eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
);
}
}
}
}
}
}
function stateChangeda() {
if (zhra.readyState == 4) {
if (zhra.status == 200) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
var onepixelequals=izoom;
var bts=zhra.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh<bts.length; ijh++) {
var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
if (!inarray(vs,vsll)) {
vsll.push(vs);
drawaac(
eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
);
}
}
}
if (intairsuffix.indexOf('&port=air') != -1) {
zhrb = new XMLHttpRequest();
zhrb.onreadystatechange=stateChangedb;
zhrb.open('get', '/HTMLCSS/intair.php?num=6&lat=' + kklat + '&long=' + kklong + '&port=y', true);
zhrb.send(null);
}
answered=true;
}
}
}
… keeps a fastish synchronous call (that we enforce via that answered global variable) but truely invokes an asynchronous arrangement extracting Nearby Ports data to plot, as applicable
Introducing the Map Chart recognition of nearby Airports with yesterday’s GeoJson World Countries Nearest Airports Tutorial‘s progress on our latest GeoJson World Countries PHP web application, it set us to seeing …
the combination of Google Directions‘s talents allowing you to reposition on the fly … and …
the onmousemove event, at least for our non-mobile users
… could mean that if we pre-plot airports on the world map, given that the user has clicked the ✈ ( ✈ ) “Show an Interest in Airports” emoji button, as a non-mobile user hovers over the world map, this pre-plotting might help trip planners with their travel options, should air travel be part of their interest, in the same way it is an option up at Google Directions in our changedcountries.phpweb application you can also try below. The overlay scenario now reads …
document.body (now with the new onmousemove=airportplot(event); event logic) lowest level …
overlayed by HTML canvas element plotted with world country linework … now including …
overlayed by HTML canvas element dedicated to nearest airport plotting …
<canvas id=myacanvas height='180' width='360' style='background-color:transparent;z-index:55;display:inline-block;position:absolute;top:0px;left:0px;'></canvas>
overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay
… worked by new (sometimes Ajax) Javascript code …
function inarray(needle, haystack) { // thanks to https://stackoverflow.com/questions/784012/javascript-equivalent-of-phps-in-array
var length = haystack.length;
for (var i = 0; i < length; i++) {
if (haystack[i] == needle) return true;
}
return false;
}
function stateChangeda() {
if (zhra.readyState == 4) {
if (zhra.status == 200) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
var onepixelequals=izoom;
var bts=zhra.response.split('.src + ' + String.fromCharCode(39));
if (eval('' + bts.length) > 1) {
for (var ijh=1; ijh<bts.length; ijh++) {
var vs=eval(eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))) +
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top))));
if (!inarray(vs,vsll)) {
vsll.push(vs);
drawaac(
eval(eval(eval(-topllong + eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[1])) * onepixelequals) + eval(0 * eval(rectis.left))),
eval(eval(eval(topllat - eval(bts[ijh].split(String.fromCharCode(39))[0].split('[')[1].split(',')[0])) * onepixelequals) + eval(0 * eval(rectis.top)))
);
}
}
}
answered=true;
}
}
}
function naira(klat, klong) {
if (answered && doair) {
answered=false;
zhra = new XMLHttpRequest();
zhra.onreadystatechange=stateChangeda;
zhra.open('get', '/HTMLCSS/intair.php?num=6&lat=' + klat + '&long=' + klong, true);
zhra.send(null);
}
}
function airportplot(e) {
if (answered) {
var rectis=null; //document.body.getBoundingClientRect();
var blat=0, blong=0;
var topllong=-180.0;
var topllat=90.0;
onepixelequals=eval(0.0 + eval(1.0 * izoom));
e = e || window.event;
e.preventDefault();
if (e.touches) {
if (e.touches[0].pageX) {
naira(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY)); //if (drawac(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY))) e = e; }
} else {
rectis=document.body.getBoundingClientRect();
naira(eval(-rectis.left + e.touches[0].clientX), eval(-rectis.top + e.touches[0].clientY)); //if (drawac(eval(-rectis.left + e.touches[0].clientX), eval(-rectis.top + e.touches[0].clientY))) { e = e; }
}
} else if (e.pageX || e.pageY) {
blat=eval(eval(eval(topllat * onepixelequals - eval(-rectistop + e.pageY) * 1)) / onepixelequals);
blong=eval(eval(eval(topllong * onepixelequals + eval(-rectisleft + e.pageX) * 1)) / onepixelequals);
if ((blat >= -90.0 && blat <= 90.0) && (blong >= -180.0 && blong <= 180.0)) {
naira(blat, blong); //if (drawac(eval(-rectisleft + e.pageX), eval(-rectistop + e.pageY))) { e = e; }
}
} else {
rectis=document.body.getBoundingClientRect();
naira(eval(-rectis.left + e.clientX), eval(-rectis.top + e.clientY)); //if (drawac(eval(-rectis.left + e.clientX), eval(-rectis.top + e.clientY))) { e = e; }
}
}
}
// ... and extended document.body onload event logic has added, up near its top ...
elema = document.getElementById('myacanvas');
contexta = elema.getContext('2d');
add interfacing functionality to the excellent Google Directions part of Google Maps, perhaps to help with Trip planning, or even just to associate a Placename with a latitude and longitude as clicked by the user, via the very simple URL arrangement … https://www.google.com/maps/dir/[decimalLatitudeDegrees]/[decimalLongitudeDegrees]
… helped out by new Javascript functions …
var lastlats=[], lastlongs=[], lastlat=-99.0, lastlong=-99.0, thislat=0.0, thislong=0.0;
function preface(inblurb) {
var extras='';
var outblurb=inblurb;
if (Math.abs(eval('' + lastlat)) > 0.0 || Math.abs(eval('' + lastlong)) > 0.0) {
if (Math.abs(eval('' + lastlat)) <= 90.0 && Math.abs(eval('' + lastlong)) <= 180.0) {
extras=' Add G for Google Directions between (' + lastlat + ',' + lastlong + ') to (' + thislat + ',' + thislong + ') and spaces (also more trip legs) to hashtag navigate to Google Charts later. ';
}
}
return extras + outblurb;
}
function alats(inlat) {
if (inlat == 0 && lastlats.length == 0) { inlat=inlat; } else { lastlats.push(inlat); }
return inlat;
}
Onto yesterday’s GeoJson World Countries TimeZone Times Tutorial GeoJson World Countries web application’s capabilities we want to add zooming, that doesn’t rely on web browser functionality (which continues to work). With that in mind we create a new emoji ( 🔎 ) 🔎 link, with this onclick event code …
var jzoom=1.0, izoom=location.search.split('zoom=')[1] ? eval(decodeURIComponent(location.search.split('zoom=')[1].split('&')[0])) : 1.0;
… to multiply the webpage zoom factor in a programmatical way. To acheive this, we have a two way approach (as you might have surmised from above) …
for mobile, the logic is easier by introducing a new meta name=viewport …
<meta id="myviewport" name="viewport" content="width=device-width, initial-scale=1, minimum-scale=0.1, maximum-scale=8, user-scalable=yes" >
… tag … while …
for non-mobile we needed to realize that event.pageX and event.pageY co-ordinates grow in proportion to the zoom factor, and that better latitude and longitude determining lines of code would go …
function canvasclick(e) {
var rectis=document.body.getBoundingClientRect();
var topllong=-180.0;
var topllat=90.0;
onepixelequals=eval(0.0 + eval(1.0 * izoom));
//document.title='canvasclick';
e = e || window.event;
e.preventDefault();
if (e.touches) {
if (e.touches[0].pageX) {
//lastl='Longitude,Latitude coordinates are ' + eval(topllong + eval(-rectis.left + e.touches[0].pageX) * onepixelequals) + ',' + eval(topllat - eval(-rectis.top + e.touches[0].pageY) * onepixelequals);
if (drawc(eval(-rectisleft + e.touches[0].pageX), eval(-rectistop + e.touches[0].pageY))) {
thislat=eval(topllat - eval(-rectistop + e.touches[0].pageY) * onepixelequals);
thislong=eval(topllong + eval(-rectisleft + e.touches[0].pageX) * onepixelequals);
//console.log('rectistop=' + rectistop + ' and rectisleft=' + rectisleft + ' and rectisy=' + rectisy + ' and thislat=' + thislat);
document.getElementById('nearestif').src='/PHP/tz_places.php?place=&latitude=' + encodeURIComponent('' + eval(topllat - eval(-rectistop + e.touches[0].pageY) * onepixelequals)) + '&longitude=' + encodeURIComponent('' + eval(topllong + eval(-rectisleft + e.touches[0].pageX) * onepixelequals)) + '&ntztontz=y';
}
} else {
Some readers might be aware of our “theory regarding adverbs” and “web applications” on the net …
the most catered for adverb relates to the “where of life” … and the second banana is …
the “when of life”
… and, further to yesterday’s GeoJson World Countries SVG Overlay Safari Error Tutorial‘s emphasis on the “where of life”, today we add in a bit of the “when of life”, something right down the line of the remit of TimeZone talents.
Seriously though, a lot of us dream of the rest of the world on a world map, and wonder what time it is in other parts of the world. Phone call to relatives? A reminder SMS call? Email a game collaboration? It could all be part of life’s rich tapestry!
The expresion of this, for us, today, improving the communications with our current GeoJsom World Countries web application, take the form of emoji clocks from the 12 hour clock example forms such as …
1 o’clock is 🕐 🕐
2 o’clock is 🕑 🕑
12 o’clock is 🕛 🕛
2:30 is 🕝 🕝
11:30 is 🕦 🕦
12:30 is 🕧 🕧
… to show in “prompt” and “confirm” popup windows, as well as Map Chart maps … via new Javascript functions …
Also, in these same places we add in Time Place country ISO-2 character code based emoji flags, adding to information and colour pizazz in the informational parts to the workings of our changedcountries.phpweb application you can also try below.
On discovering our first solution theory of turning yesterday’s mapsvg.js external Javascript work into an async piece of work made no difference to this situation, we surmised that the huge amount of content held in the Javascript (ie. client side) global variable appendtoinnerHTML was causing memory issues. We couldn’t shift much to do with the overall amount of “data” needing to be handled, in order to implement country SVG colour infilling, but we could shift the data from being …
client side (external) Javascript held … to, instead, (have that data) be (determined on the) …
server side PHP filling in the contents of our (relevant) HTML div id=svgd ahead of the document.body onload event timing …
<?php
$icnt=0;
function apptohtmstuff($coordsare, $origc) {
global $icnt;
$minl=-1;
$mint=-1;
$maxl=-1;
$maxt=-1;
$zysare=explode(',', $coordsare);
$svgcis='';
for ($ij=0; $ij<sizeof($zysare); $ij+=2) {
if ($minl < 0) {
$minl=$zysare[$ij];
$maxl=$zysare[$ij];
$mint=$zysare[1 + $ij];
$maxt=$zysare[1 + $ij];
} else {
if ($zysare[$ij] < $minl) { $minl=$zysare[$ij]; }
if ($zysare[$ij] > $maxl) { $maxl=$zysare[$ij]; }
if ($zysare[1 + $ij] < $mint) { $mint=$zysare[1 + $ij]; }
if ($zysare[1 + $ij] > $maxt) { $maxt=$zysare[1 + $ij]; }
}
}
this improved web application stability …
…everywhere … and an idea we’d ditched yesterday of …
idea to pre-colour “land” parts of the world GeoJson map green (ahead of the document.body onload event) also came good (after causing problems yesterday)
… meaning now, “overlay” wise, we could say …
document.body lowest level …
overlayed by HTML canvas element plotted with world country linework …
overlayed by HTML img (transparent image) element and its associated image map area elements … and today we add into the mix …
HTML div hosting SVG elements overlaying initialized with “land” parts green infilled SVG at a mid range z-index (only as well as when called upon) individual GeoJson entities overlay
… and we (reckon we’ve) improved the colour coding user experience at the same time.
We noticed that tweaks in the changed …
var lastflagged='', appendtoinnerHTML='', waitplease=false; // used to make sure "area" element onclick code precedes any document.body onmousedown or ontouchdown code
… also lessened the burden on the client side by only asking any Javascript DOM command operations act on single HTML element at a time, not a whole swathe of hosted ones, in any operation.
What deducible data item needs to be determined for these Geo Charts to work? We need a way to deduce ISO-2 character country codes from the ISO-3 character codes existing in the GeoJson “countries.geojson” data from yesterday’s work. We happened upon the extremely generous mapping data webpage to help with these ISO-2 character deductions …
… in our image map area elements PHP creation code above. As you can see, extra “intelligence”, moving forward, is contained in area element global data attributes.
Geo Chart can involve emoji (🏠 &127968;) or image (SVG) circle based symbology for the “User Clicked Place” and nearby TimeZone places respectively …
all these symbols can be clicked to open popup windows containing TimeZone Place Wikipedia webpages of relevance …
an emoji national flag (eg. Zambia “ZA” could be used to derive 🇿🇦 🇿🇦 flag emoji) derived from those ISO-2 character codes can supplement the GeoJson (more ISO-3 character based) names presented in the underlying data, in the Geo Chart title …
contextualizing the accompanying Map Chart … and …
vice versa regarding hovering over symbology (which works on Map Chart, but not Geo Chart) …
within the Map Chart iframe a “Geo” link can glean a “zoomed out” world Geo Chart view of your TimeZone places
Know your GeoJson! Yes, pretty obviously, any two GeoJson datasets might display the same in that “map plotting” sense, but one might have different and/or more “intelligence” than the other. Often, an XML has more “intelligence” than equivalent HTML (barring the use of global data attributes, that is), as today’s Corollacorollary.
Luckily for programmers all over, the organization of TimeZones has had an International flavour in its development and maintenance. As such, given the “purely coastline” GeoJson data involved in our fledgling PHP web application of yesterday’s GeoJson World Coastline Primer Tutorial a useful arrangement for improvement involves …
document.body onclick event co-ordinates … able to be converted to …
longitude, latitude (easily, only because of our simplistic map projection, of course) … onfed to …
… can have us helping out your curious web “clicking” user with the 3 nearest TimeZone places, as a reference as to where they are “clicking” in the world.
in a discrete click methodology of interest, you could adopt a non-mobile “onmousedown” logic set that does not get interfered with by a mobile “ontouchdown” logic set (perhaps leaving “onclick” event, which both non-mobile and mobile both recognise, for another event logic role) … and …
neither will interrupt the mobile gestures associated with swiping and pinching, which refer to the events “ontouchstart” and “ontouchend” at either end of their lifespan
And so, we arrive at a long planned for tilt at Image Map functionality that we often turn to Mobilefish.Com and its excellent Image Map Creation to help us out … but not today?! Why not? We have a funny set of needs, they being …
our Image Map’s image will have a variable set of width x height dimensions …
our Image Map’s image will be transparent
our Image Map needs to have a hole left aside inside it where the functionality that originally existed (and pointed to WordPress Blog content like you are reading), is still working