Closed hanselmoovita closed 11 months ago
Hi @hanselmoovita sorry for late answer. You are probably having an exception raising in the try block at line 494 of the map_widget.py file that results in one of the except blocks which is returning self.empty_tile_image. Could you please provide further details?
@LorenzoMattia
Can I clarify with you, if I would like to apply overlay layer on top of Main Tile layer, is this the correct way to do it? See screenshot below, right window for highlighted portion:
The current problem I faced is that when I added this line, the tkinter application goes blank when I made the selection with overlay (ie: Google satellite)
For this scenario, I have not made modifications to map_widget.py I have tried to modify the definition for
For your kind advice please
@hanselmoovita
Yes, you are doing right in setting the overlay_tile_server.
The problem I think you are facing is in the block I mentioned in my previous answer.
I suggest you to execute the code in debug mode or trivially to temporarily remove the try except block at line 494 in map_widget.py to see what exception is raised. My intuition is that a certain problem is causing that block to fall in on of the except blocks in which the empty_tile_image is being returned and that is probably the blank image you are seeing in the screen.
I'm talking about this piece of code
try:
url = self.tile_server.replace("{x}", str(x)).replace("{y}", str(y)).replace("{z}", str(zoom))
image = Image.open(requests.get(url, stream=True, headers={"User-Agent": "TkinterMapView"}).raw)
if self.overlay_tile_server is not None:
url = self.overlay_tile_server.replace("{x}", str(x)).replace("{y}", str(y)).replace("{z}", str(zoom))
image_overlay = Image.open(requests.get(url, stream=True, headers={"User-Agent": "TkinterMapView"}).raw)
image = image.convert("RGBA")
image_overlay = image_overlay.convert("RGBA")
if image_overlay.size is not (self.tile_size, self.tile_size):
image_overlay = image_overlay.resize((self.tile_size, self.tile_size), Image.ANTIALIAS)
image.paste(image_overlay, (0, 0), image_overlay)
if self.running:
image_tk = ImageTk.PhotoImage(image)
else:
return self.empty_tile_image
self.tile_image_cache[f"{zoom}{x}{y}"] = image_tk
return image_tk
except PIL.UnidentifiedImageError: # image does not exist for given coordinates
self.tile_image_cache[f"{zoom}{x}{y}"] = self.empty_tile_image
return self.empty_tile_image
except requests.exceptions.ConnectionError:
return self.empty_tile_image
except Exception:
return self.empty_tile_image
Keep me updated once you have tried!
I've removed the entire try-except code block starting from line 494 in map_widget.py, and when the app is launched, the default selection OpenStreetMap shows a blank grey screen, and many exception errors were returned in the debugger in one go:
No other modifications were made other than the one you've suggested above. The full log of exception errors are found below:
Hi @hanselmoovita, sorry, I did not explain myself well.
You do not have to completely remove the try catch block, but only to remove the try, leaving the block of code inside it, and all the catches. So this code:
try:
url = self.tile_server.replace("{x}", str(x)).replace("{y}", str(y)).replace("{z}", str(zoom))
image = Image.open(requests.get(url, stream=True, headers={"User-Agent": "TkinterMapView"}).raw)
if self.overlay_tile_server is not None:
url = self.overlay_tile_server.replace("{x}", str(x)).replace("{y}", str(y)).replace("{z}", str(zoom))
image_overlay = Image.open(requests.get(url, stream=True, headers={"User-Agent": "TkinterMapView"}).raw)
image = image.convert("RGBA")
image_overlay = image_overlay.convert("RGBA")
if image_overlay.size is not (self.tile_size, self.tile_size):
image_overlay = image_overlay.resize((self.tile_size, self.tile_size), Image.ANTIALIAS)
image.paste(image_overlay, (0, 0), image_overlay)
if self.running:
image_tk = ImageTk.PhotoImage(image)
else:
return self.empty_tile_image
self.tile_image_cache[f"{zoom}{x}{y}"] = image_tk
return image_tk
except PIL.UnidentifiedImageError: # image does not exist for given coordinates
self.tile_image_cache[f"{zoom}{x}{y}"] = self.empty_tile_image
return self.empty_tile_image
except requests.exceptions.ConnectionError:
return self.empty_tile_image
except Exception:
return self.empty_tile_image
will be:
url = self.tile_server.replace("{x}", str(x)).replace("{y}", str(y)).replace("{z}", str(zoom))
image = Image.open(requests.get(url, stream=True, headers={"User-Agent": "TkinterMapView"}).raw)
if self.overlay_tile_server is not None:
url = self.overlay_tile_server.replace("{x}", str(x)).replace("{y}", str(y)).replace("{z}", str(zoom))
image_overlay = Image.open(requests.get(url, stream=True, headers={"User-Agent": "TkinterMapView"}).raw)
image = image.convert("RGBA")
image_overlay = image_overlay.convert("RGBA")
if image_overlay.size is not (self.tile_size, self.tile_size):
image_overlay = image_overlay.resize((self.tile_size, self.tile_size), Image.ANTIALIAS)
image.paste(image_overlay, (0, 0), image_overlay)
if self.running:
image_tk = ImageTk.PhotoImage(image)
else:
return self.empty_tile_image
self.tile_image_cache[f"{zoom}{x}{y}"] = image_tk
return image_tk
Obviously, this operation only aimes at understanding the error that is causing the display of blank images, indeed using the try catch, the error is never shown in the console. Morever, even if this approach works, the best way to do this would be to use the debugger and not to modify the code. Once you will understand why that part of code is generating an exception you will probably closer to the solution.
Let me know!
Hi,
The change is able to work based on the modification to if-else conditional as you have suggested:
For the above test, I used OpenStreetMap as base layer with Railway lines as overlay.
Please note that the Antialias class seems to have been removed, I have replaced it with LANCZOS as per: https://stackoverflow.com/questions/76616042/attributeerror-module-pil-image-has-no-attribute-antialias
Do note that there are warning issued:
/usr/lib/python3/dist-packages/requests/init.py:89: RequestsDependencyWarning: urllib3 (2.1.0) or chardet (3.0.4) doesn't match a supported version! warnings.warn("urllib3 ({}) or chardet ({}) doesn't match a supported " Exception in thread Thread-1: Traceback (most recent call last): File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner self.run() File "/usr/lib/python3.8/threading.py", line 870, in run self._target(*self._args, **self._kwargs) File "/home/han/Documents/TkinterMapView-MY/src/map_widget.py", line 436, in pre_cache self.request_image(zoom, x, self.pre_cache_position[1] + radius, db_cursor=db_cursor) File "/home/han/Documents/TkinterMapView-MY/src/map_widget.py", line 495, in request_image image = Image.open(requests.get(url, stream=True, headers={"User-Agent": "TkinterMapView"}).raw) File "/home/han/.local/lib/python3.8/site-packages/PIL/Image.py", line 3305, in open raise UnidentifiedImageError(msg) PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x7f8be06630e0>
However, can I also ask, if Tkintermapview is able to support Openstreetmap/Google maps as base layer, with local map tiles as overlay layer?
With the latest update to implementation, only the map tile layer (overlay) is loaded, and the base layer with Openstreetmap doesn't load:
Conditions to which this issue is attempted:
The intended implementation is to look like this:
How do you suggest map_widget.py maybe further modified to support this implementation?
Hi @hanselmoovita, cool! Removing the try revealed that the error that was causing the blank image to be returned in one of those catch blocks was related to that Image.LANCZOS.
So now you can actually reintroduce the try and bring back the code to its original version, obviously keeping the fix of the LANCZOS. I would suggest this to you, because using try catches will prevent the program to crash when, for instance, the server from which you download the images is not available.
Concerning your second question, this is surely possible, but it is necessary to change something in the request_image method. Are you using a db created with the offline_loading.py file to load offline the overlays?
Hi @LorenzoMattia Yes, if I revert the code back to its original version, and keeping the fix for LANCZOS, the crash issue disappears.
No....I did not create db using offline_loading.py; I set up a local HTTP server by opening an Ubuntu terminal, screenshot shown below. 0 to 19 indicates the zoom level:
While I am able to load the local tiles using set_tile_server as the sole layer, I am not able to display the local tiles if it is an overlay layer, with OSM as base layer with the original mplementation (only change applied is changing line 504 ANTIALIAS-->LANCZOS):
Would like to ask how def set_overlay_tile_server (or any other related functions) can be modified to support image tile overlay.....
Hi @hanselmoovita, first of all I want to clarify that in my answer I am supposing your self.database_path to be unspecified (None).
Starting from this assumption, what I can tell you is that problems are probably again in that try catch block. So, I would suggest you again to remove that try, as you already did to discover the LANCZOS problem, and see what is the error that is causing this problem. Indeed, that part of code should actually work, and the behavior you are obtaining is really strange.
Lastly, just to be sure, are your overlay images .png with no bacground?
Keep me updated!
Hi @LorenzoMattia I have once again attempted to replace the try-except block with the if-else block at Line 494 which you have suggested.
For this scenario, selecting option with the OSM base layer + overlay doesn't work either. Additionally, I noticed the following issues:
Multiple traceback errors of this type returned: AttributeError: 'PhotoImage' object has no attribute '_PhotoImage__photo' Exception ignored in: <function PhotoImage.del at 0x7fc227a2d280> Traceback (most recent call last): File "/home/han/.local/lib/python3.8/site-packages/PIL/ImageTk.py", line 132, in del
Location would default to Berlin, even though I have specified self.map_widget.set_address to Singapore
The default location would go into the highest resolution; sometimes, when I attempted to change the map selection to Google, a black tile appears instead:
Scenario illustrated in screenshot below:
The overlay_images.png have no background; the background intended to be used would be either OSM or Google streetmap. If you would like to have a copy of the overlay images, you can email me at hansel.chan@moovita.com
Hope to hear from you again, wishing you a Merry Christmas!
Hi @hanselmoovita, thank you and I hope you had a really Merry Christmas!
Answering to your points:
Hoping to be helpful, let me know!
@LorenzoMattia Good day, I am happy to share my code with you, is it possible you send me a private message at hansel.chan@moovita.com so that I can share it with you?
Wishing you a Happy New Year
Example used: map_with_customtkinter.py
I added a 4th option to include overlay option for "Railway". Modifications made were:
In self.map_option_menu:
self.map_option_menu = customtkinter.CTkOptionMenu(self.frame_left, values=["OpenStreetMap", "Google normal", "Google satellite", "railway"]
In def change map:
elif new_map == "railway": self.map_widget.set_overlay_tile_server("http://a.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png") # railway infrastructure
However, upon running the program and selecting the "railway" option for Tile Server, the TkinterMapView returned blank map:
I used the suggested API link given under "Use other tile servers" in your Readme.
May I ask for help to fix or advice on this issue