Category Archives: Uncategorized

FOSSGIS-Jeopardy: The buzzer

First in a series of very terse posts explaining and documenting the technical setup behind our remote video conferencing FOSSGIS-Jeopardy game setup. Mostly meant as public backup and because sharing is caring. Maybe it can inspire someone else to build silly stupid stuff for fun.

Here we are with the buzzer for the candidates to hit if they want to solve a task (aka ask the question to the presented thing). I wrote this for a funny digital xmas party with the lovely colleagues at Civity in 2020 and re-used it for FOSSGIS-Jeopardy 2021 and 2022.

It’s using WAMP (basically PubSub via websockets) between webbrowser pages via the awesome Crossbar + AutobahnJS combo which I first used for the crazy https://findingplaces.hamburg/ project.

Yes, there is a random partyparrot on each run.

A admin page can be used to unlock a buzzer “button” (well, a parrot GIF in our case because we need more silly fun in life) on participants’ buzzer pages. Participants can then touch/click their screen to “buzz”. The incoming buzzes are displayed visible for everyone with the delay since the buzzer was unlocked.

This is obviously highly dependent on the participants’ latency, both on their device (touch/click to network message) as well as from their device to the message router. A tab on a desktop browser in LAN will win against a mobile device in 2G if they’d hit it in the same moment. ¯\_(ツ)_/¯

There are two/three components: The message router, the webpages and optionally a webserver (I used crossbar as router which can also host static webpages)

Ask me anything if you want to set this up following the amazing instructions and fail.

Install and setup crossbar

pip install crossbar

https://crossbar.io/docs/TLS-Certificates/#using-lets-encrypt-with-crossbar-io & https://certbot.eff.org/instructions?ws=other&os=ubuntufocal

Set up a realm and static web directory in a config.json, e. g. something like this:

{
	"version": 2,
	"controller": {},
	"workers": [{

		"type": "router",
		"realms": [{
			"name": "YOURREALMHERE",
			"roles": [{
				"name": "public",
				"permissions": [{
					"uri": "com.example.your(sub)domainhere.*",
					"allow": {
						"call": false,
						"register": false,
						"publish": true,
						"subscribe": true
					}
				}]
			}]
		}],
		"transports": [{
			"type": "web",
			"endpoint": {
				"type": "tcp",
				"port": 443,
				"tls": {
					YOURTLSSETUPHERE
				}
			},
			"paths": {
				"/": {
					"type": "static",
					"directory": "web"
				},
				"ws": {
					"type": "websocket",
					"auth": {
						"anonymous": {
							"type": "static",
							"role": "public"
						}
					},
					"options": {
					        "max_frame_size": 20480,
					        "max_message_size": 20480,
					        "fail_by_drop": true
					}
				}
			}
		}]
	}]
}

I don’t remember anything about the options part. It might not be necessary. I probably tried to stuff something more complex into the messages at some point.

Then just run crossbar as root because you used a single-purpose 2€ VPS for this and you will delete it after the event anyways. Yolo! You should definitely secure your system, crossbar config and certificate and everything else properly otherwise. Seriously!

crossbar start --config /root/config.json

Webpages for control and playing

Here you go:

This is really really horrible and messy code, with lots of left-over bits, bugs and random snippets. But it works so who cares! I could not care less about its bEAuTy. Sometimes a banana is the right hammer.

  • Screen stays on (support depends on the OS)!
  • Fullscreen mode can be triggered with a button!
  • There is a sound when the buzzer is buzzed by anyone!
  • Animations are CSS transforms on PNG sprites!
  • Random emojis are displayed if a user did not set a username!
  • Inconsistent indentation!
  • Probably some german words here and there!
  • w3schools was extensively used to create this!

Put them in a web/ directory according to the crossbar config (or host it with another webserver if you like).

Using the buzzer

Now you have three webpages available:

Eyes that follow the cursor in QGIS

I did this all in some random EPSG:25832 location and scale, this uses several magic numbers that make it work for that. I did not make it work for any CRS or canvas size. If you do, please share. But this is just silly fun so …..

Have two polygons for the eyes.

Set their Symbol Layer Type to Geometry Generator and smooth them:

smooth($geometry, 3)

Add another Geometry Generator symbol layer to it and throw in the following magic expression to build the pupils. It calculates the distance from your cursor to the centroids of the polygons and it prepares a line from each centroid to the cursor. Then it places a point geometry onto that line at a fraction of the distance. Use the Geometry Type “Point / MultiPoint” for this Geometry Generator.

with_variable(
  'distance',
  distance(@canvas_cursor_point, centroid($geometry)),
  with_variable(
    'line',
    make_line(centroid($geometry), @canvas_cursor_point),
    line_interpolate_point(@line, @distance/5)
  )
)

Set the layer itself to automatically refresh its rendering in the layer’s properties:

Now the eyes will follow your cursor as it moves across the map canvas!

Task for you: The pupils are not clipped and can exit the eyes. Oops! Head over to Topi’s for a hint how to solve this: https://twitter.com/tjukanov/status/1278689814288760837

And then you paint the rest of the fucking owl:

Those are lines with 3 vertices each:

And the middle vertex is moved vertically using the expression below. Basically the line is reconstructed.

smooth(
  make_line(
    start_point($geometry),  -- first point is kept
    translate(
      point_n($geometry, 2),  -- second point is translated
      0,
      distance(
        @canvas_cursor_point, centroid($geometry)
      )/10  -- move according the cursor distance to centroid
    ),
    end_point($geometry)  -- last point is kept
  ),
  4
)

Here is my project file including the temporary scratch layers (use the awesome Memory Layer Saver plugin to have them loaded automatically):

User cannot log in to your MediaWiki?

I just spent too much time on this. A user could not log in to a wiki I maintain. We tried everything. At some point I must have fixed the main issue but sadly I had introduced a new one. Here is what happened in the hopes that if *you* read this, you can fix it as easily as me (spending much less time on it).

Looking at the browser’s network log we noticed that the wiki first logged them in but then, on the redirect to the main page, deleted the cookies again.

Looking at the debugging log of the wiki I saw:

[authentication] Primary login with MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider succeeded
[authentication] Login for bob succeeded

...

[DBQuery] wiki BEGIN /* Wikimedia\Rdbms\Database::query (User::saveSettings) 31.16.28.131 */

[DBQuery] wiki
UPDATE /* User::saveSettings 31.16.28.131 */ user
SET user_name = 'bob',user_real_name = '',
user_email = 'bob@example.com',
user_email_authenticated = NULL,
user_touched = '20190423202751',
user_token = 'stripped',
user_email_token = NULL,
user_email_token_expires = NULL
WHERE user_id = '3' AND user_touched = '20190423202746'

[DBQuery] wiki ROLLBACK /* MWExceptionHandler::rollbackMasterChangesAndLog 31.16.28.131 */

[exception] [stripped] /w/index.php?title=Special:UserLogin&returnto=Main+Page
MWException from line 4026 of /srv/wiki/w/includes/user/User.php: CAS update failed
on user_touched for user ID '3' (read from replica); the version of the user to be
saved is older than the current version.
#0 /srv/wiki/w/includes/session/SessionBackend.php(648): User->saveSettings()
#1 /srv/wiki/w/includes/deferred/MWCallableUpdate.php(30): MediaWiki\Session\SessionBackend->MediaWiki\Session\{closure}()
#2 /srv/wiki/w/includes/deferred/DeferredUpdates.php(257): MWCallableUpdate->doUpdate()
#3 /srv/wiki/w/includes/deferred/DeferredUpdates.php(210): DeferredUpdates::runUpdate(MWCallableUpdate, Wikimedia\Rdbms\LBFactorySimple, string, integer)
#4 /srv/wiki/w/includes/deferred/DeferredUpdates.php(131): DeferredUpdates::execute(array, string, integer)
#5 /srv/wiki/w/includes/MediaWiki.php(905): DeferredUpdates::doUpdates(string)
#6 /srv/wiki/w/includes/MediaWiki.php(731): MediaWiki->restInPeace(string)
#7 /srv/wiki/w/includes/MediaWiki.php(750): MediaWiki->{closure}()
#8 /srv/wiki/w/includes/MediaWiki.php(554): MediaWiki->doPostOutputShutdown(string)
#9 /srv/wiki/w/index.php(43): MediaWiki->run()
#10 {main}

So after the login succeeded there was an exception which invalidated the session and caused the wiki to delete the cookie on the next page load. The stupid reason for this was that someone (not me, ha ha ha) earlier set the user’s user_touched field to 0 because surely that was a safe value. Setting it to 20190423202746 instead let the user log in just fine. And afterwards it was happily updated by the wiki again.

Fantec QB-X8US3/H82-SU3S2 keeps resetting

Hoping that *you* found this post via Google and this helps you:

If your Fantec QB-X8US3 enclosure keeps resetting and you get lots of sd or usb errors in dmesg, check your harddisks and *remove* flaky ones. I had random, frequent resets (unmounting all your drives forcefully…) when I thought I could use a dying drive as temporary storage in the enclosure. As soon as I removed that bad drive, the device was rock-stable again. 10++ would buy again.

See also http://forum.mediasonic.ca/viewtopic.php?f=115&t=3413.

Fantec QB-X8US3 is apparently the same as a H82-SU3S2 ProBox. Here’s a review.

Sincerely yours, DenverCoder9

WFS-T on Geoserver

If you encounter an error like
ERROR 1: Error returned by server :

java.lang.IllegalArgumentException: argument type mismatch
argument type mismatch


you probably have a mismatch between WFS versions. Try changing your WFS url to a specific version. Using http://www.example.com/geoserver/sf/ows?strict=true&version=1.0.0 worked for me after I spent hours with that stupid error…

If you add features to your store but nothing new appears on the map, check if the features actually end up with geometries on your server… Eg if you try to add LineStrings to a MultiLineString layer via Geoserver WFS-T you will get objects without geometries. This happens silently, at least when using the osgeo ogr module in Python.

If every request takes 10 seconds your server might time out trying to parse your schema. Check your geoserver logs for things like
2016-03-10 15:48:00,486 WARN [geotools.xml] - Error parsing: http://123.123.123.123.:8080/geoserver/ows?strict=true&VERSION=1.0.0&SERVICE=WFS&REQUEST=DescribeFeatureType&TYPENAME=my:layer

If you know how to find out how to debug this please tell me:
2016-03-10 15:48:00,501 ERROR [geoserver.wfs] - Transaction failed
org.geoserver.wfs.WFSTransactionException: Error performing insert: null
at org.geoserver.wfs.InsertElementHandler.execute(InsertElementHandler.java:215)
at org.geoserver.wfs.Transaction.execute(Transaction.java:322)
(... a million more steps truncated...)

Merge GML files into one SQLite or Spatialite file

For example the buildings in Hamburg, Germany:

layer=$1
for file in *.xml
do
 if [ -f "${layer}".spatialite ]
 then
  ogr2ogr -f "SQLite" -update -append "${layer}".spatialite "${file}" "${layer}" -dsco SPATIALITE=YES
 else
  ogr2ogr -a_srs EPSG:25832 -f "SQLite" "${layer}".spatialite "${file}" "${layer}" -dsco SPATIALITE=YES
 fi
done

Remove the -dsco SPATIALITE=YES and change the output filename for SQLite. QGIS can work with both.

$ bash mergexmltospatialite.sh AX_Gebaeude

Be aware that Spatialite is much more sensitive to geometric problems. You might get things like

ERROR 1: sqlite3_step() failed:
ax_gebaeude.GEOMETRY violates Geometry constraint [geom-type or SRID not allowed] (19)
ERROR 1: ROLLBACK transaction failed: cannot rollback - no transaction is active
ERROR 1: Unable to write feature 1712 from layer AX_Gebaeude.

ERROR 1: Terminating translation prematurely after failed translation of layer AX_Gebaeude (use -skipfailures to skip errors)

but on the other hand, you get spatial indexing which makes queries or high zoom interaction much quicker.

Be aware that if you try to merge files into a Shapefile and fields are getting truncated, those fields will only be filled with data for the first file you merge. On the later files OGR will try to match the input field names to the merged file’s fieldnames, notice the difference and discard them. If you still want to convert to Shapefiles, check out the -fieldTypeToString IntegerList,StringList options.