17

Is it possible to use a background image in a polygon without rasterizing the polygon?

I tried with textures, but they seem to be viable only for simple polygons.

I want to put a flag as the background of the polygon that represents a country shape. I can do this for one country by rasterizing the polygon:

ImageAdd[
    Graphics[CountryData["Monaco", "Polygon"]], 
    CountryData["Monaco", "Flag"]
]

monaco

But by rasterizing the polygon, I make it difficult to apply this procedure to several neigboring countries and assemble them together.

I would like to put each country flag in the background of, for example:

Graphics[
    {EdgeForm[White], 
     CountryData[#, "Polygon"] & /@ {"Venezuela", "Colombia"}}]  

colombia and venezuela


Solution

After seeing cormullion answer, I was able to do what I wanted:

Graphics[
 {EdgeForm[Black],
  {{Texture[ImageReflect[CountryData[#, "Flag"], Top -> Right]], 
    CountryData[#, "Polygon"] /. 
      {Polygon[a_] :> Polygon[First[a], VertexTextureCoordinates -> 
       Transpose[Rescale /@ Transpose[First[CountryData[#, "Coordinates"]]]]]}} & /@ 
CountryData["SouthAmerica"]}}]

southamerica

The key part is the use of Rescale[].

LCarvalho
  • 9,233
  • 4
  • 40
  • 96
Gustavo Delfino
  • 8,348
  • 1
  • 28
  • 58
  • I wish Mathematica had more advanced vector-graphics and layering; this is so simple in a drawing program. A good solution would preserve everything as vector with no rasterized textures, but I don't think it can be done in a general way. -- edit I didn't notice that the Flag data is already raster. I guess Texture makes the most sense in that case. – Mr.Wizard Feb 04 '12 at 23:45
  • You may find ImagePad useful if you want small flags inside country borders – Szabolcs Feb 05 '12 at 01:00
  • 1
    One caveat: This texture-based approach leads to a graphic that cannot be exported to PDF (on Macs, at least). This seems worth mentioning since you specifically asked for a method preserving the vector character of the borders. Just in case anyone encounters this, a workaround for this problem is to export to svg first, and then use Inkscape to convert to PDF. – Jens Feb 05 '12 at 19:24

3 Answers3

12

This example is from the documentation for Texture in Mathematica version 8:

With[{vc = 
 Transpose[
  Rescale /@ Transpose[First[CountryData[#, "Coordinates"]]]]}, 
Show[CountryData[#, "Shape"], 
 ImageSize -> {{100}, {100}}] /. {RGBColor[__] :> 
  Texture[ImageReflect[Image[CountryData[#, "Flag"]], 
    Top -> Right]], 
 Polygon[a_] :> 
  Polygon[First[a], VertexTextureCoordinates -> vc]}] & /@ 
CountryData["SouthAmerica"]

but I don't know whether it does what you need. Still, it looks pretty.

flags

cormullion
  • 24,243
  • 4
  • 64
  • 133
4

Try:

Graphics[{EdgeForm[White], {{Texture[Image[CountryData[#, "Flag"]]], 
  Polygon[First@First[CountryData[#, "Polygon"]], 
   VertexTextureCoordinates ->Transpose[
     Rescale /@ Transpose[First@First[CountryData[#, 
         "Polygon"]]]]]}} & /@ {"Venezuela", "Colombia"}}]

which gives

maps and flags

kglr
  • 394,356
  • 18
  • 477
  • 896
2

For coastal countries with a lot of islands, CountryData[tag,"Polygon"] gives something of the form Polygon[{island1, island2, island3,...}], where island1 and the like is another list of latitudes and longitudes.

The case is simpler for inland countries. CountryData[tag,"Polygon"] gives Polygon[{island1}], where island1 is simply the mainland, and is itself a list of latitudes and longitudes.

An example can be given using UK and Venezuela:

Table[Length /@ First[CountryData[country, "Polygon"]], {country, {"UnitedKingdom", "Venezuela"}}]

The result is:

{{20, 25, 31, 54, 52, 63, 141, 195, 1927}, {491}}

which means that UK is made up of 9 polygons/islands, and Venezuela only 1.

To fill a country's land with its own flag, we must be careful to apply the flag texture to all the islands. The important thing is to treat each island of a country one by one.

To apply a texture to any polygon, we must first come up with a function that can yield the correct VertexTextureCoordinates. For example:

countryRescale1[polygon_] := (Transpose[Rescale /@ Transpose[First[polygon]]])

The function accepts as argument a polygon of an island of a country, which is of the form Polygon[{{x1,y1},{x2,y2},...}}]. We obtain the list of coordinates with First[].

We apply transpose to the list of coordinates {{x1,y1},{x2,y2},...}}, so that we group all the x's together, and y's together: {{x1, x2, x3, ...}, {y1, y2, y3, ...}}. We apply to it Rescale[], so that x's and y's are both rescaled from 0 to 1 separately. Then apply transpose to group them back into {{x1,y1},{x2,y2},...}}, where x and y is now in [0,1].

Then we can overlay the flag onto a country. Let's use Hong Kong (though not a country, but it has many islands) as an example. The important thing is to treat each island of a country one by one.

Graphics[{EdgeForm[Black], Texture[CountryData["HongKong", "Flag"]], 
  Polygon[#, VertexTextureCoordinates -> countryRescale1[Polygon[#]]] & /@
  First[CountryData["HongKong", "Polygon"]]}]

We map the pure function Polygon[#, VertexTextureCoordinates -> countryRescale1[Polygon[#]]]& to the expression First[CountryData["HongKong", "Polygon"]], which in itself is a list of islands { {{x1, y1}, {x2, y2}, ..., {xn,yn}}, {{x1, y1}, {x2, y2}, ...}, ... }.

So it's just like:

Graphics[{EdgeForm[Black], Texture[CountryData["HongKong", "Flag"]],
  {Polygon[], Polygon[], Polygon[] ... }]

The result looks like this:

Hong Kong with the islands

For Argentina, the island province Tierra del Fuego (the bottom right triangular piece of land) is also treated with the flag:

Argentina with the island

The final thing left to do is to apply this to all countries to make a world flag map:

Graphics[{EdgeForm[Black], Table[{Texture[CountryData[c, "Flag"]], 
  Polygon[#, VertexTextureCoordinates -> countryRescale1[Polygon[#]]] & /@  
  First[CountryData[c, "FullPolygon"]]}, {c, CountryData[]}]}, ImageSize -> 700]

The result looks like this:

enter image description here

For better flags, one can refer to this post: Why do some CountryData flags render at low resolution?

Jonas
  • 123
  • 5
  • I have used the better flags, 500px ones from Why do some CountryData flags render at low resolution? to create a world flag map. What is the right way to export it so that I can zoom in and see even Hong Kong? Should I use 200px, 500px, or 1000px for the flags? – Jonas Jun 08 '14 at 07:06
  • What do you think of this piece of code Graphics[{EdgeForm[Black],{Texture[CountryData[#1,"Flag"]],Polygon[#,VertexTextureCoordinates->countryRescale1[Polygon[#]]]&/@First[CountryData[#1,"FullPolygon"]]}&/@CountryData[]},ImageSize->200] as compared to the one using Table[]? – Jonas Jun 08 '14 at 07:12